如何编写创建带参数函数的 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
我想定义一个宏,这样我就可以将一个 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