2017-07-02 19 views
0

我一直在研究Elixir中的宏,然后我在编程Elixir>第20章>使用绑定注入值中找到这个片段。使用非引用的动态函数定义

defmodule Test do 
    require My 
    [ :fred, :bert ] |> Enum.each(&My.mydef(&1)) 
end 

其中mydef是一个简单的宏,它为给定的名称定义了一个echo方法。因此,我的模块测试应该有两个功能fredbert,那么笔者解释bind_quoted,它使用unquote的宏内部定义的功能,导致英寸

defmodule My do 
    defmacro mydef(name) do 
    quote bind_quoted: [name: name] do 
     def unquote(name)(), do: unquote(name) 
    end 
    end 
end 

笔者曾说过,bind_quoted将推迟unquote执行允许在运行时定义方法。然后我使用Macro.to_string来分析生成的内容,这指出我要测试它。

defmodule Test do 
    name = :something 
    def unquote(name)(), do: unquote(name) 
end 

Test.something() 
# Returns :something 

现在我想知道如何工作。该代码应该抛出一个错误,告知unquote未在quote块内使用,而对于宏mydef也几乎相同,根据Elixir文档,我们不能在使用bind_quoted时使用unquote

有人可以解释Elixir如何处理该代码吗?

+0

我原以为这是因为'def'是一个宏,那么在给宏参数传递给宏之前引用这个宏,所以'unquote'将被允许。但是,如果我尝试'My.mydef(unquote(name))',那么我会得到预期的错误。我认为有一些特殊的处理def参数。 –

+0

的确如此,我们可以在宏的参数上使用unquote,但为了获得该参数的值,我们必须使用'Macro.escape(expr,unquote:true)'来移除引用的表达式以去除对'unquote'的调用' 。然后我们可以引用一个新的表达式,它将使用我们的引用表达式。然而,为了在调用者的上下文中插入它的值,我们必须记住'unquote'这个表达式,'unquote(name)'作为参数的值将是标识符'name'。 –

+0

我可以回答这个问题,如果你请把它作为一个单一的问题,格式化。请删除答案,这绝不是答案,删除评论,将答案和注释的重要部分放入原始问题中,并将其作为[MCVE](https://stackoverflow.com/help/mcve )。 – mudasobwa

回答

0

我认为作者已经告诉,不解释Macro.escape,它使用bind_quoted绑定变量并禁用unquote在同一时间。因为,至少我不知道如何,仅仅为宏的某些部分禁用引号是不可能的,那么必须完成。 由于宏参数在传递给宏之前被引用,我们可以使用任何我们希望的,但是不引用将取决于宏如何处理,即。例如,当我们在PARAMS

defmacro print(str) do 
    quote do 
    IO.puts unquote(str) 
    end 
end 

print("Hello World") 
# => :ok 
str = "Hello again" 
print(unquote(str)) 
# => unquote cannot be used outside quote 

使用unquote第二次调用打印将扩大到IO.puts(unquote(str))有我们的错误的代码抛出一个错误。为了解决这个问题,我们必须避免参数的无引号,因为它在Kernel.def中完成 - 使用Macro.escape(expr,unquote:true)。