背景
我有一个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 是多态类型?