2015-05-28 7 views
29

Desidero creare dinamicamente un nome di funzione. Ho scritto questa macroCome creare un nome di funzione dinamico usando la macro Elixir?

defmacro generate_dynamic(name) do 
    quote do 
    def add_(unquote(name)) do 
    end 
    end 
end 

E ho usato in questo modo:

defmodule AnotherModule do 
    generate_dynamic :animal 
end 

Ora, ho solo AnotherModule.add_ funzione definita, mentre mi aspetto AnotherModule.add_animal funzione.

risposta

36

Per ottenere ciò, è possibile anteporre :add_ al nome prima dello non quotato. Inoltre, le parentesi dopo il nome del metodo in questo caso sono richieste per evitare ambiguità. Questo dovrebbe fare il trucco:

defmacro generate_dynamic(name) do 
    quote do 
    def unquote(:"add_#{name}")() do 
     # ... 
    end 
    end 
end 
17

A volte come una scorciatoia utile si può ottenere lo stesso risultato in linea, senza scrivere una macro utilizzando un frammento di unquote.

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" 
+1

ho pensato 'unquote' lavorato solo ** all'interno di una macro **. Com'è che funziona all'interno di una funzione anonima? – asymmetric

+7

Mi rispondo: unquote e [unquote fragments] (http://elixir-lang.org/docs/stable/elixir/Kernel.SpecialForms.html) sono due cose diverse. – asymmetric

+0

Giusto per chiarire quanto sopra: i frammenti di unquote sono 'unquote's che sono al di fuori di un blocco' quote'. – asymmetric

5

Ho fatto questo stesso genere di cosa in un Gist per cercare di imitare 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 

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

Problemi correlati