如何使用Elixir宏创建动态函数名称?

sha*_*evy 34 elixir

我想动态创建一个函数名.我写了这个宏

defmacro generate_dynamic(name) do
  quote do 
    def add_(unquote(name)) do
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

我用它是这样的:

defmodule AnotherModule do
  generate_dynamic :animal
end
Run Code Online (Sandbox Code Playgroud)

现在,我只得到AnotherModule.add_函数定义,而我期望AnotherModule.add_animal函数.

Pat*_*ity 43

要实现此目的,您可以取消引用之前添加:add_到名称前面.此外,在这种情况下,方法名称后面的括号需要防止歧义.这应该做的伎俩:

defmacro generate_dynamic(name) do
  quote do 
    def unquote(:"add_#{name}")() do
      # ...
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

  • 通常在定义不带参数的函数时,括号被省略,但是当用宏定义它们时,括号总是必要的。 (4认同)

Paw*_*rok 21

有时作为有用的快捷方式,您可以在不使用非引用片段编写宏的情况下实现内联的相同结果.

defmodule Hello do
  [:alice, :bob] |> Enum.each fn name ->
    def unquote(:"hello_#{name}")() do
      IO.inspect("Hello #{unquote(name)}")
    end
  end
end

Hello.hello_bob    # => "Hello bob"
Hello.hello_alice  # => "Hello alice"
Run Code Online (Sandbox Code Playgroud)

  • 我回答自己:unquote和[unquote fragments](http://elixir-lang.org/docs/stable/elixir/Kernel.SpecialForms.html)是两回事. (7认同)

Chr*_*nzo 5

我在一个要点中做了同样的事情来尝试和模仿Ruby的attr_accessor:

defmodule MacroExp do
  defmacro attr_accessor(atom) do
    getter = String.to_atom("get_#{atom}")
    setter = String.to_atom("set_#{atom}")
    quote do
      def unquote(getter)(data) do
        data |> Map.from_struct |> Map.get(unquote(atom))
      end
      def unquote(setter)(data, value) do
        data |> Map.put(unquote(atom), value)
      end
    end
  end

  defmacro attr_reader(atom) do
    getter = String.to_atom("get_#{atom}")
    quote do
      def unquote(getter)(data) do
        data |> Map.from_struct |> Map.get(unquote(atom))
      end
    end
  end
end


defmodule Calculation do
  import MacroExp
  defstruct first: nil, second: nil, operator: :plus

  attr_accessor :first   # defines set_first/2 and get_first/1
  attr_accessor :second  # defines set_second/2 and get_second/1
  attr_reader :operator  # defines set_operator/2 and get_operator/1

  def result(%Calculation{first: first, second: second, operator: :plus}) do
    first + second
  end
end
Run Code Online (Sandbox Code Playgroud)

https://gist.github.com/rcdilorenzo/77d7a29737de39f0cd84