Elixir 返回实现者结构类型的行为

问题描述 投票:0回答:1

我有一个行为来抽象过度解析各种 Phoenix 端点的 URL 查询参数。它看起来像这样:

defmodule Query do
  @callback from_query_params(params :: %{optional(String.t()) => any()}) ::
              {:ok, parsed :: struct} | {:error, reason :: atom}
end

一个简单的实现如下所示:

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

在这里我最想说的是:

  • 实现模块应该提供一个结构体(称之为
    t()
  • from_query_params/1
    上的成功类型应该使用 that struct
    t()
    ,而不仅仅是任何 struct

我怀疑在 Elixir typespec 语言中没有办法表达这一点,但我会 高兴 被证明是错误的。

elixir dialyzer typespec
1个回答
4
投票

虽然不可能在类型规范中表达这一点,但可以通过一些元编程来部分满足需求。

如果您可以使用自己的

Query
行为per实现来区分返回类型,则可以使用

defmodule QueryBuilder do
  defmacro __using__(opts \\ []) do
    quote do
      impl = __MODULE__
      defmodule Query do
        @callback from_query_params(map()) :: {:ok, %unquote(impl){}}

        def __after_compile__(env, _bytecode),
          do: env.module.__struct__
      end

      @behaviour Query
      @after_compile Query
    end
  end
end

而不是

@behaviour Query
,使用
use QueryBuilder
。这样嵌套的
Query
模块将具有正确的返回类型 and 如果目标模块未声明结构,编译器回调 将引发。

© www.soinside.com 2019 - 2024. All rights reserved.