【问题标题】:Define and use functions inside macros在宏中定义和使用函数
【发布时间】:2018-05-16 21:24:18
【问题描述】:

按照这个例子:

defmodule Greetify do

  defmacro __using__(_) do
    quote do
      Module.register_attribute __MODULE__, :greet, accumulate: true,
        persist: false
      @before_compile Greetify
    end
  end

  defmacro __before_compile__(env) do
    greetings = Module.get_attribute(env.module, :greet)
    for {name, age} <- greetings do
      IO.puts "#{name} is #{age} years old"
    end
  end

end

是否可以定义在宏中使用的内部函数?

例如:

  defmacro __before_compile__(env) do
    greetings = Module.get_attribute(env.module, :greet)
    say_greetings(greetings)

    defp say_greetings(grettings) do
      for {name, age} <- greetings do
        IO.puts "#{name} is #{age} years old"
      end
    end
  end

尝试这个编译器会抱怨函数 say_grettings 没有定义

那个例子来源http://elixir-recipes.github.io/metaprogramming/accumulating-annotations/

【问题讨论】:

    标签: macros elixir


    【解决方案1】:

    嗯,这是可能的。您的代码的问题是您混合了范围。 Elixir 中的宏正在编译阶段进行扩展。目前还没有编译好的say_greetings/1 函数(除了不能从defmacro 内部调用defp,但可以通过适当的引用来克服。)

    为此,您需要在与__before_compile__/1 相同的范围内声明say_greetings/1 以使其可用。您不能将其定义为函数(见上文),但解决方法是将其也定义为宏。这样它会在编译过程中扩展,一切都会正常工作(另外,我怀疑我理解这是什么原因。)

    总结:

    defmodule Greetify do    
      defmacro __using__(_) do
        quote do
          Module.register_attribute __MODULE__, :greet, accumulate: true, persist: false
          @before_compile Greetify
        end
      end
    
      defmacrop say_greetings(greetings) do
        quote do
          for {name, age} <- unquote(greetings) do
            IO.puts "#{name} is #{age} years old"
          end
        end
      end
    
      defmacro __before_compile__(env) do
        greetings = Module.get_attribute(env.module, :greet)
        say_greetings(greetings)
      end
    end
    
    defmodule Test do
      use Greetify
    
      @greet {"Jon", 21}
      @greet {"Sam", 23}
    end
    

    【讨论】:

    • 感谢您的回答,是的,我知道它们已编译,这就是为什么我认为它可以与 before_compile 宏一起使用。因此,例如,如果我必须使用几个函数而不仅仅是“say_greetings”,那么它们都必须是一个单独的宏,对吗?在 before_compile 范围内可用
    • 未编译,它们在编译前就地扩展为AST,这就是编译开始前可以访问它们的原因>。老实说,除了将它们放入单独的宏之外,我无法发明其他解决方案,但我不确定以其他方式完成,
    • mm 但可以按照您所说的正确引用宏在宏内部调用 def?
    • 是的,但是无法执行尚未编译的函数。你可以声明它,可以,但不能调用。
    猜你喜欢
    • 2020-10-07
    • 2011-08-18
    • 1970-01-01
    • 2022-01-02
    • 2011-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多