2016-01-20 16 views
8

मेरे पास एक मैक्रो है जो इस तरह के मॉड्यूल को परिभाषित करता है।Elixir macros और bind_quoted

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 

इस चलाने का उत्पादन होता है:

I am Bar 
I am Runner.Foo 

कौन समझ में आता है; MacroFun.define_module को Runner.run में बुलाया गया था इसलिए मॉड्यूल को परिभाषित किया गया था और इस प्रकार Runner मॉड्यूल के तहत घोंसला गया था।

लेकिन अब अगर मैं बदल MacroFun.define_module: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 

उत्पादन अब हो जाता है:

I am Bar 
I am Foo 

क्यों ??

उत्तर

7

मुझे लगता है कि ऐसा इसलिए है क्योंकि जहां आप परिवर्तनीय name को अनवरोधित (बाध्यकारी) कर रहे हैं।

पहले मामले में, आप मॉड्यूल बनाते समय वेरिएबल name को अनवरोधित कर रहे हैं, इस प्रकार उस पल में चर को बाध्य करने के लिए संदर्भ की जांच करने की आवश्यकता होगी (उदाहरण के लिए कोड किसी अन्य मॉड्यूल के अंदर है या नहीं)। तो, आप अपने वर्तमान परमाणु के साथ उचित संदर्भ प्राप्त करते हैं: Runner.Foo

दूसरे मामले में, आप चर name unquoting कर रहे हैं इससे पहले कि यह एक संदर्भ में रखा गया है, इसलिए अपने मूल्य में परिवर्तन नहीं होगा और यह परमाणु Foo (कोई संदर्भ) हो जाएगा।

+2

वाह, यह बहुत सूक्ष्म है, लेकिन समझ में आता है। यह सुनिश्चित करने के लिए पुन: संक्षेप में कि मैं समझता हूं: जब 'bind_quoted' का उपयोग किया जाता है, तो उद्धरण के शरीर ('do'..'end') को' name' unquoted _outside_ अनदेखा किया जाता है। लेकिन जब 'bind_quoted' का उपयोग नहीं किया जाता है, तो उद्धरण के अंदर' नाम' स्पष्ट रूप से unquoted है। – cjbottaro

+0

ब्रेकिंग बैड से हेज़ेनबर्ग का हवाला देते हुए: "आप सही हैं"। –

0

इस कोड के साथ आप मॉड्यूल बनाने के लिए इस्तेमाल सही मान देखेंगे:

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 

आउटपुट:

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

[warn] Foo2 
I am Bar 
I am Foo2