2015-08-08 76 views
7

我正在試驗萬靈藥中的宏。因此,我將要展示的代碼當然應該用簡單的函數來完成,但是..我正在試驗!elixir中的宏擴展:如何使用另一個宏來定義2個宏?

我想定義2個宏(A和B),並讓A使用B來實驗宏擴展。 當我使用A時,出現編譯錯誤,說明函數 B是undefined

下面是代碼:

defmodule MyMacros do 
    defmacro print_expr(expr) do 
    quote do 
     IO.puts(unquote(expr)) 
    end 
    end 

    defmacro print_hashes_around(expr) do 
    quote do 
     IO.puts "###" 
     print_expr(unquote(expr)) 
     IO.puts "###" 
    end 
    end 
end 

defmodule MyModule do 
    require MyMacros 

    def my_print(expr) do 
    MyMacros.print_hashes_around(expr) 
    end 
end 

MyModule.my_print("hello world") 

這裏是編譯錯誤:

macro_test.exs:17: warning: redefining module MyModule 
** (CompileError) macro_test.exs:21: function print_expr/1 undefined 
(stdlib) lists.erl:1336: :lists.foreach/2 
macro_test.exs:17: (file) 
(elixir) lib/code.ex:307: Code.require_file/2 

的方式我(MIS)明白的事情:

  1. 通過要求MyMacros中,模塊MyModule應該知道兩個宏的存在。因此我應該可以使用任何宏。
  2. 當在MyModule中展開print_hashes_around時,編譯器應該發現print_expr也是一個宏。因此,應該發生另一次擴張。
  3. 似乎發生的事情是第二次擴張不會發生。因此編譯器查找不存在的函數定義。

對嗎?

正如建議鬆懈,前print_exprMyMacros.修復它。我仍然不明白爲什麼。 MyModule要求MyMacros所以這兩個宏應該是已知的和可擴展的......當我看看unless的定義時,它使用if而不是Kernel.if

回答

9

By requiring MyMacros, the module MyModule should know the existence of both macros. Therefore I should be able to use any macros.

誤會就在這裏。 :) require只會使編譯器可用的模塊,它不會導入模塊的功能。如果你使用import MyModule那麼它會工作。

但是,最好通過爲模塊名稱加前綴來解決問題,因爲這樣您可以允許開發人員使用您的代碼明確使用您的宏(使用require)或導入它們。

另一種選擇是避免這樣的多重宏調用:

defmodule MyMacros do 
    defmacro print_expr(expr) do 
    quoted_print_expr(expr) 
    end 

    defmacro print_hashes_around(expr) do 
    quote do 
     IO.puts "###" 
     unquote(quoted_print_expr(expr)) 
     IO.puts "###" 
    end 
    end 

    defp quoted_print_expr(expr) do 
    quote do 
     IO.puts(unquote(expr)) 
    end 
    end 
end 
+1

感謝何塞,這是驚人的,以獲得從語言的創造者自己的答案! – svarlet