2015-08-08 73 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