能否在Elixir宏内部定义并使用内部函数?
在Elixir宏内部定义可调用函数的问题
你遇到的这个问题核心是没搞清楚Elixir宏的运行上下文和注入代码上下文的区别。
你尝试在__before_compile__宏里直接定义defp say_greetings然后立刻调用,但编译器报错说函数未定义——这是因为你写的defp say_greetings并不是宏所在模块的函数,而是会被注入到使用这个宏的目标模块里的代码。宏运行的时候,这段函数定义还只是待注入的AST,根本不存在于当前宏的执行上下文中,自然调用不了。
那该怎么解决?分两种情况:
情况1:想在宏内部复用逻辑(编译时执行)
如果这个say_greetings只是宏自己要用来处理数据的工具函数,不需要注入到目标模块,那直接把它定义在宏所在的模块里就行:
defmodule Greetify do defmacro __using__(_) do quote do Module.register_attribute __MODULE__, :greet, accumulate: true, persist: false @before_compile Greetify end end # 把工具函数定义在模块里,供宏内部调用 defp say_greetings(greetings) do for {name, age} <- greetings do IO.puts "#{name} is #{age} years old" end end defmacro __before_compile__(env) do greetings = Module.get_attribute(env.module, :greet) # 直接调用模块内的私有函数,这是在编译时执行的 say_greetings(greetings) end end
情况2:想让函数成为目标模块的一部分(运行时/编译时注入后调用)
如果这个say_greetings是要作为目标模块的私有函数,那你需要把调用逻辑也包裹在quote里,让它和函数定义一起注入到目标模块中:
defmacro __before_compile__(env) do greetings = Module.get_attribute(env.module, :greet) quote do defp say_greetings(greetings) do for {name, age} <- greetings do IO.puts "#{name} is #{age} years old" end end # 在注入的代码上下文里调用这个函数,此时它已经是目标模块的一部分了 say_greetings(unquote(greetings)) end end
简单总结一下:
- 宏里不在
quote中的代码:属于宏所在模块的编译时上下文,只能调用该模块已定义的函数 - 宏里在
quote中的代码:属于目标模块的上下文,会被注入到目标模块中,只能在目标模块的上下文里调用
内容的提问来源于stack exchange,提问作者lapinkoira
相关产品推荐
相关产品推荐

