首页 > 解决方案 > Elixir 中透析器的多态类型

问题描述

背景

我有一个struct调用MyApp.Result,它基本上是 Result Monad 的表示。该结构旨在成为操作成功和错误的正式结构表示:

defmodule MyApp.Result do

  @enforce_keys [:type]
  defstruct type: nil,
            result: nil,
            error_reason: nil,
            error_details: nil,
            input_parameters: []

  
  @type type :: :ok | :error
  @type result :: any()
  @type reason :: atom() | nil
  @type details :: any()
  @type params :: [any()]

  @type t() :: %__MODULE__{
          type: type(),
          result: result(),
          error_reason: reason(),
          error_details: details(),
          input_parameters: params()
        }

  @spec ok :: __MODULE__.t()
  def ok, do: %__MODULE__{type: :ok}


  @spec ok(result()) :: __MODULE__.t()
  def ok(result), do: %__MODULE__{type: :ok, result: result}

  @spec error(reason()) :: __MODULE__.t()
  def error(reason), do: %__MODULE__{type: :error, error_reason: reason}

  @spec error(reason(), details()) :: __MODULE__.t()
  def error(reason, details) do
    %__MODULE__{type: :error, error_reason: reason, error_details: details}
  end

  @spec error(reason(), details(), params()) :: __MODULE__.t()
  def error(reason, details, input) do
    %__MODULE__{
      type: :error,
      error_reason: reason,
      error_details: details,
      input_parameters: input
    }
  end

问题

在向我的一位同事展示这一点时,他提出了一个很好的观点:

我看到你在这里尝试做什么,但是当我看到一个MyApp.Result结构时,我必须检查代码以了解result字段内部的内容,以防成功。对我来说,这只是隐藏事物的另一层,如果操作成功,它并不清楚操作返回什么。

公平地说,我认为这是一个很好的观点。结果单子会隐藏计算结果,直到您需要它们。但我确实认为有一种更好的方法,一种我们仍然可以拥有一个 Result Monad 的方法,它明确显示结果字段的类型。

带透析器的多态类型

我相信我的问题的解决方案可能是透析器的多态类型。引用一篇文章:

来自Learn You Some Erlang 当我说我们可以将整数列表定义为[integer()]orlist(integer())时,那些是多态类型。它是一种接受类型作为参数的类型。

为了使我们的队列只接受整数或卡片,我们可以将其类型定义为:

-type queue(Type) :: {fifo, list(Type), list(Type)}.
-export_type([queue/1]).

所以现在我知道这在 erlang 中是可能的。如果那里有可能,那么在 Elixir 中也应该有可能。

错误

因此,我将代码更改为以下内容:

@enforce_keys [:type]
  defstruct type: nil,
            result: nil,
            error_reason: nil,
            error_details: nil,
            input_parameters: []

  @type type :: :ok | :error
  @type result :: Type
  @type reason :: atom() | nil
  @type details :: any()
  @type params :: [any()]

  @type t(Type) :: %__MODULE__{
          type: type(),
          result: result(),
          error_reason: reason(),
          error_details: details(),
          input_parameters: params()
        }

  @spec ok :: BusyBee.Wrappers.FFmpeg.Result.t(nil)
  @spec ok :: __MODULE__.t()
  def ok, do: %__MODULE__{type: :ok}

  @spec ok(result()) :: __MODULE__.t(Type)
  def ok(result), do: %__MODULE__{type: :ok, result: result}

然而,这打破了。我不知道如何使用透析器在 Elixir 中表示多态类型。

问题

如何修复此代码,以便我的透析器知道 Result 是多态类型?

标签: erlangelixirdialyzer

解决方案



推荐阅读