如何动态生成函数子句

Howto dynamically generate function clauses

我需要根据用户的配置动态生成函数子句。为了清楚起见,假设我有一个原子列表:

@atoms ~w|foo bar baz|a

来自,比方说,config.exs。我需要的是生成这个函数(MCVE 过于简单,但它给我的印象是我实际需要什么):

@checker fn
  {:foo, _} -> false
  {:bar, _} -> false
  {:baz, _} -> false
  _ -> true
end

我目前正在做的是:

@clauses Enum.map(@atoms, fn tag ->
  {:->, [], [[{:{}, [], [tag, {:_, [], Elixir}]}], false]}
end) ++ [{:->, [], [[{:_, [], Elixir}], true]}]

defmacrop checker, do: {:fn, [], @clauses}

它工作得很好,但我想我把事情搞得太复杂了,漏掉了一些简单的东西。所以,我的问题是:

有没有简单的方法在编译时生成函数子句?

我使用 quote:

使其更具可读性(更多信息见下文)
defmodule A do
  @atoms ~w|foo bar baz|a
  @clauses Enum.flat_map(@atoms, fn tag ->
    quote do: ({unquote(tag), _} -> false)
  end) ++ quote(do: (_ -> true))

  defmacro checker, do: {:fn, [], @clauses}
end

defmodule B do
  require A
  f = A.checker
  IO.inspect f.({:foo, :ok})
  IO.inspect f.({:bar, :ok})
  IO.inspect f.({:baz, :ok})
  IO.inspect f.({:quux, :ok})
end

输出:

false
false
false
true

我希望 quote(do: a -> b) 可以工作,但现在是一个解析错误所以我们必须做 quote(do: (a -> b)) 将我们想要的引用片段包装在列表中。

我还希望 unquotefn 里面工作,但它在 quote 里面,但那也不行。

iex(1)> quote do
...(1)>   fn
...(1)>     unquote()
...(1)>     _ -> true
...(1)>   end
...(1)> end
** (SyntaxError) iex:2: expected clauses to be defined with -> inside: 'fn'

我认为这两个不是错误就是缺少功能。