2016-01-20 14 views
8

Tôi có macro xác định mô-đun như vậy.Macro Elixir và 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 

Kết quả của hoạt động này là:

I am Bar 
I am Runner.Foo 

nào có ý nghĩa; MacroFun.define_module được gọi trong Runner.run do đó mô-đun đã được xác định và do đó được lồng trong mô-đun Runner.

Nhưng bây giờ nếu tôi thay đổi MacroFun.define_module sử dụng :bind_quoted tùy chọn:

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 

Sản lượng lúc này là:

I am Bar 
I am Foo 

Tại sao ??

Trả lời

7

Tôi nghĩ rằng đó là vì địa điểm trong đó bạn không hủy (ràng buộc) biến name.

Trong trường hợp đầu tiên, bạn không hủy biến số name khi tạo mô-đun, do đó ràng buộc biến tại thời điểm đó sẽ yêu cầu kiểm tra ngữ cảnh (kiểm tra xem mã có nằm trong mô-đun khác không). Vì vậy, bạn nhận được nguyên tử hiện tại của bạn cộng với bối cảnh thích hợp: Runner.Foo.

Trong trường hợp thứ hai, bạn không hủy biến số name trước khi được đặt trong ngữ cảnh, do đó giá trị của nó sẽ không thay đổi và nó sẽ là nguyên tử Foo (không có ngữ cảnh).

+2

Wow, đó là khá tinh tế, nhưng có ý nghĩa. Tóm lại để chắc chắn rằng tôi hiểu: khi 'bind_quoted' được sử dụng, sau đó' name' là unquoted _outside_ phần thân ('do'..'end') của báo giá. Nhưng khi 'bind_quoted' không được sử dụng, thì' name' được bỏ qua một cách rõ ràng bên trong trích dẫn. – cjbottaro

+0

Trích dẫn Heisenberg từ Breaking Bad: "Bạn đúng là chết tiệt". –

0

Với mã này, bạn sẽ thấy các giá trị đúng dùng để tạo ra các module:

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 

Output:

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

[warn] Foo2 
I am Bar 
I am Foo2 
Các vấn đề liên quan