代码之家  ›  专栏  ›  技术社区  ›  lapinkoira

定义和使用宏中的函数

  •  0
  • lapinkoira  · 技术社区  · 6 年前

    以下示例:

    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\u grettings未定义

    该示例来源 http://elixir-recipes.github.io/metaprogramming/accumulating-annotations/

    1 回复  |  直到 6 年前
        1
  •  2
  •   Aleksei Matiushkin    6 年前

    嗯,这是可能的。代码的问题是混合了作用域。Elixir中的宏在编译阶段被扩展。没有已编译的 say_greetings/1 此时的函数(除此之外,不能调用 defp 从内部 defmacro ,但这可能会被适当的引用所克服。)

    你需要做的是申报 说\u问候/1 在与相同的范围内 __before_compile__/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