如何根据 elixir 的属性定义函数?

How to define functions based on attribute to elixir?

假设我有一个模块 SilentDefiner。我想根据其属性为 Silent 定义几个函数。让我解释一下:

defmodule Silent do
  @function_names [:a, :b, :c]

  use Definer
end

defmodule Definer do
  defmacro __using__(_) do
    quote do
      Enum.each(@function_names, fn(n) ->
        def unquote(n)() do # line 5
          IO.puts "a new method is here!"
        end
      end)
    end
  end
end

但是这个方法实际上行不通,因为我有undefined function n/0 on line 5。我怎样才能实现所需的功能?

您需要将 unquote: false 传递给 Definer.__using__/1 中的 quote 才能将 unquote 片段注入 quote.

defmodule Definer do
  defmacro __using__(_) do
    quote unquote: false do
      Enum.each(@function_names, fn(n) ->
        def unquote(n)() do # line 5
          IO.puts "a new method is here!"
        end
      end)
    end
  end
end

defmodule Silent do
  @function_names [:a, :b, :c]

  use Definer
end

Silent.a
Silent.b
Silent.c

打印

a new method is here!
a new method is here!
a new method is here!

Kernel.SpecialForms.quote/2 docs 中详细记录了一个类似的案例,其中还提到了如何使用 bind_quoted 如果你想将一些变量注入 quote 并创建 unquote 碎片.