2017-02-02 88 views
0

我想給__using__/1宏模塊內的動態定義的一些功能。這些函數的名稱將由提供給__using__/1宏的opts參數定義。事情是這樣的:爲什麼我的動態定義函數未定義?

defmodule MyModule do 

    defmacro __using__(opts) do 
    names = Keyword.get(opts, :names) 
    Enum.each(names, fn(name) -> 
     quote bind_quoted: [name: name] do 
     def unquote(:"function_for_#{name}")(param) do 
      IO.puts("Hello from function_for_#{name}!") 
      IO.puts("With parameter: #{param}.") 
     end 
     end 
    end) 
    end 

end 

然後,這個模塊將是use「由另一個模塊D,是這樣的:

defmodule UserModule do 
    use MyModule, names: [:foo, :bar] 
end 

我所期望的行爲是:

iex> UserModule.function_for_foo(:hello_world) 
> Hello from function_for_foo! 
> With parameter: :hello_world. 
iex> UserModule.function_for_bar(:hola_mundo) 
> Hello from function_for_bar! 
> With parameter: :hola_mundo. 

但是,取而代之的是,他們沒有被定義:

iex> UserModule.function_for_foo(:hello_world) 
> ** (UndefinedFunctionError) function UserModule.function_for_foo/1 is undefined or private 

我讀過像過去幾天的一些相關文件:Kernel.use/2 documentationModule documentation on compile callbacks,在Domain Specific Languages documentation提供了一個例子,a very related question here in SO,但我似乎無法使這項工作。

在此先感謝,任何幫助將不勝感激!

+0

在內核文檔中提出的最佳實踐建議,不應該在'__using__'中定義函數,那麼也許我的整個方法是錯誤的? –

回答

2

有兩種錯誤的位置:

  1. 您需要返回引用AST。 Enum.each忽略該函數返回的值。您需要改用Enum.map

  2. 你在這一行錯過了周圍的nameunquote()

    IO.puts("Hello from function_for_#{name}!") 
    

最終代碼:

defmodule MyModule do 
    defmacro __using__(opts) do 
    names = Keyword.get(opts, :names) 
    Enum.map(names, fn(name) -> 
     quote bind_quoted: [name: name] do 
     def unquote(:"function_for_#{name}")(param) do 
      IO.puts("Hello from function_for_#{unquote(name)}!") 
      IO.puts("With parameter: #{param}.") 
     end 
     end 
    end) 
    end 
end 

defmodule UserModule do 
    use MyModule, names: [:foo, :bar] 
end 

UserModule.function_for_foo(:hello_world) 
UserModule.function_for_bar(:hola_mundo) 

輸出:

Hello from function_for_foo! 
With parameter: hello_world. 
Hello from function_for_bar! 
With parameter: hola_mundo. 

內核文檔中建議的最佳實踐表明,中應該沒有使用定義的函數,那麼也許我的整個方法是錯誤的?

只有在可以在模塊中定義函數並將其導入use時纔可以。由於要動態定義的函數名和機構,你需要做的是,在__using__(或類似的地方,如@before_compile)。

+0

這解決了它,謝謝! –