如何编写创建带参数函数的 DSL?

How to write a DSL that creates a function with arguments?

我想定义一个宏,这样我就可以将一个 do 块传递给它,并让宏创建一个调用该块的函数,带有一个参数.我 运行 陷入先有鸡还是先有蛋的问题,因为下面的代码抱怨说 name 没有定义。

defmodule MyMacro do
  defmacro greet(do: block) do
    quote do
      def hello(name), do: unquote(block)
    end
  end
end

defmodule Test do
  import MyMacro
  greet do
    IO.puts("Hello, #{name}!")
  end
end

尝试编译此代码会导致:

(CompileError) iex:6: undefined function name/0
(stdlib) lists.erl:1338: :lists.foreach/2
(stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
(iex) lib/iex/evaluator.ex:250: IEx.Evaluator.handle_eval/5

根据我的理解,这甚至在它到达我的宏之前就已经爆炸了,因为 elixir 试图在调用我的宏之前为 do 块生成一个 AST,但是 name 是未定义的。

我的目标是在编译 DSL 后能够调用 Test.hello("world")。这在 Elixir 中可能吗?

Elixir 的宏是 hygienic,因此如果您在宏的 quote 中声明一个变量,调用者将无法使用它。您可以通过使用 var!:

包装变量声明来禁用此功能
quote do
  def hello(var!(name)), do: unquote(block)
end