s3c*_*ur3 1 elixir dialyzer typespec
我有一个行为来抽象解析各种 Phoenix 端点的 URL 查询参数。它看起来像这样:
defmodule Query do
@callback from_query_params(params :: %{optional(String.t()) => any()}) ::
{:ok, parsed :: struct} | {:error, reason :: atom}
end
Run Code Online (Sandbox Code Playgroud)
一个简单的实现如下所示:
defmodule SearchQuery do
@moduledoc "Parses URL query params for search endpoint"
@behaviour Query
@enforce_keys [:search_term]
defstruct @enforce_keys
@typespec t :: %__MODULE__{search_term: String.t()}
@impl Query
def from_query_params(%{"query" => query}) when query != "" do
{:ok, %__MODULE__{search_term: query}}
end
def from_query_params(_), do: {:error, :missing_search_term}
end
Run Code Online (Sandbox Code Playgroud)
在这里我真正想说的是:
t())from_query_params/1应该使用该struct t(),而不仅仅是任何结构我怀疑 Elixir typespec 语言中没有办法表达这一点,但我很高兴被证明是错误的。
虽然\xe2\x80\x99 不可能在类型规范中表达这一点,但可以通过一些元编程来部分满足要求。
\n如果你同意有自己的Query行为来区分返回类型,则可以使用
defmodule QueryBuilder do\n defmacro __using__(opts \\\\ []) do\n quote do\n impl = __MODULE__\n defmodule Query do\n @callback from_query_params(map()) :: {:ok, %unquote(impl){}}\n\n def __after_compile__(env, _bytecode),\n do: env.module.__struct__\n end\n\n @behaviour Query\n @after_compile Query\n end\n end\nend\nRun Code Online (Sandbox Code Playgroud)\n而不是@behaviour Query使用use QueryBuilder. 这样嵌套Query模块将具有正确的返回类型和编译器回调如果目标模块未声明该结构,编译器回调将