2016-07-15 10 views
1

Angesichts der folgenden:Doppel unquoting für dynamisch generierte Makros

for fn_name <- [:foo, :bar, :baz] do 
    defmacro unquote(fn_name)(do: inner) do 
     fn_name = unquote(fn_name) # <--- Why? 
     quote do 
     IO.puts "#{unquote(fn_name)} called" 
     unquote(inner) 
     end 
    end 
    end 

Was für fn_name = unquote(fn_name) ist der Grund? Wenn ich diese Zeile weglasse, ist das ein Kompilierungsfehler. Was ist der Grund für dieses "doppelte" Noquoting?

Antwort

8

Nehmen wir das Beispiel ein wenig vereinfachen:

for fn_name <- [:foo, :bar, :baz] do 
    defmacro unquote(fn_name)(do: inner) do 
    fn_name = unquote(fn_name) # <--- Why? 
    quote do 
     {unquote(fn_name), unquote(inner)} 
    end 
    end 
end 

Im Beispiel oben, weil Zitat ist Zurückgeben eines Tupels mit zwei nicht angeklagten Elementen entspricht dies:

for fn_name <- [:foo, :bar, :baz] do 
    defmacro unquote(fn_name)(do: inner) do 
    fn_name = unquote(fn_name) # <--- Why? 
    {fn_name, inner} 
    end 
end 

Jetzt ist es einfacher zu verstehen, was passiert, wenn Sie nicht unquote(fn_name) vorher: die Variable fn_name wäre einfach nicht innerhalb der Makrodefinition existieren. Denken Sie daran, dass alle def s (def, defp, defmacro usw.) einen neuen Variablenbereich starten. Wenn Sie also fn_name inside verwenden möchten, müssen Sie dies irgendwie definieren.

Die andere Eigenschaft, die wir in diesem Code sehen, ist, dass Elixir nicht mehr quittiert, wenn es eine quote sieht. In dem obigen Zitat wird unquote nicht entkoppelt, wenn das Makro definiert ist, sondern wenn das Makro ausgeführt wird, was auch erklärt, warum die Variable innerhalb des Makros definiert werden muss.

+0

Danke, dass du dir die Zeit genommen hast! Das macht Sinn und ich habe jetzt zusätzliche Werkzeuge, um über Metaprogrammierung nachzudenken: D –

2

Es ist wegen Hygiene.

Elixier hat das Konzept von Makro Hygiene. Hygiene bedeutet, dass Variablen, Importe und Aliase, die Sie in einem Makro definieren, nicht in die eigenen Definitionen des Aufrufers gelangen.

for fn_name <- [:foo, :bar, :baz] do 
    defmacro unquote(fn_name)(do: inner) do 
     fn_name = unquote(fn_name) # <-- This is macro's context 
     quote do 
     IO.puts "#{unquote(fn_name)} called" # <-- This is caller's context 
     unquote(inner) 
     end 
    end 
    end 

Sie sollten lesen Hygiene schützt den Kontext des Caller von Chris McCord Metaprogrammierung Elixir Buch

+0

Vielen Dank! Denken in Bezug auf Makro vs Anrufer Kontext ist sehr hilfreich! –