2016-01-20 19 views
8

Ho una macro che definisce un modulo come tale.Elixir macros and bind_quotated

defmodule Bar do 
    def bar do 
    IO.puts "I am #{inspect __MODULE__}" 
    end 
end 

defmodule MacroFun do 

    defmacro define_module(name) do 
    quote do 
     defmodule unquote(name) do 
     import Bar 
     def foo do 
      bar 
      IO.puts "I am #{inspect __MODULE__}" 
     end 
     end 
    end 
    end 

end 

defmodule Runner do 
    require MacroFun 

    def run do 
    MacroFun.define_module Foo 
    Foo.foo 
    end 

end 

Runner.run 

L'uscita del funzionamento di questo è:

I am Bar 
I am Runner.Foo 

che ha un senso; MacroFun.define_module è stato chiamato in Runner.run così il modulo è stato definito e quindi annidato sotto il modulo Runner.

Ma ora se cambio MacroFun.define_module per utilizzare l'opzione :bind_quoted:

defmacro define_module(name) do 
    quote bind_quoted: [name: name] do 
     defmodule name do 
     import Bar 
     def foo do 
      bar 
      IO.puts "I am #{inspect __MODULE__}" 
     end 
     end 
    end 
    end 

L'uscita diventa ora:

I am Bar 
I am Foo 

Perché ??

risposta

7

Penso che sia perché il posto dove non si stia tagliando (vincolante) la variabile name.

Nel primo caso, si annulla la variabile name durante la creazione di un modulo, pertanto l'associazione della variabile in quel momento richiederebbe il controllo del contesto (controllare se il codice si trova all'interno di un altro modulo, ad esempio). Quindi, ottieni il tuo atomo attuale più il contesto appropriato: Runner.Foo.

Nel secondo caso, si annulla la variabile name prima che venga inserita in un contesto, pertanto il suo valore non cambierà e sarà l'atomo Foo (senza contesto).

+2

Wow, che è abbastanza sottile, ma ha un senso. Ricapitolare per essere sicuri di capire: quando viene usato 'bind_quoted', quindi' name' è non quotato _outside_ il corpo ('do'..'end') della citazione. Ma quando 'bind_quoted' non viene usato, quindi' name' è esplicitamente non quotato all'interno della citazione. – cjbottaro

+0

Citando Heisenberg da Breaking Bad: "Hai maledettamente ragione". –

0

Con questo codice vedrete i valori corretti utilizzati per creare i moduli:

require Logger 

defmodule Bar do 
    def bar do 
    IO.puts "I am #{inspect __MODULE__}" 
    end 
end 

defmodule MacroFun do 

    defmacro define_module(name) do 
    quote do 
     Logger.debug("#{inspect unquote(name)}") 
     defmodule unquote(name) do 
     import Bar 
     Logger.debug("#{inspect unquote(name)}") 
     def foo do 
      bar 
      IO.puts "I am #{inspect __MODULE__}" 
     end 
     end 
    end 
    end 

    defmacro define_module2(name) do 
    quote bind_quoted: [name: name] do 
     defmodule name do 
     import Bar 
     Logger.debug("#{inspect name}") 
     def foo do 
      bar 
      IO.puts "I am #{inspect __MODULE__}" 
     end 
     end 
    end 
    end 
end 

defmodule Runner do 
    require MacroFun 

    def run do 
    MacroFun.define_module Foo 
    Foo.foo 
    end 
    def run2 do 
    MacroFun.define_module2 Foo2 
    Foo2.foo 
    end 

end 

Runner.run 
Runner.run2 

uscita:

[warn] Foo 
[warn] Runner.Foo 
I am Bar 
I am Runner.Foo 

[warn] Foo2 
I am Bar 
I am Foo2