使用unquote的动态函数定义

时间:2017-07-02 21:22:12

标签: macros elixir metaprogramming

我一直在研究Elixir中的宏,然后我在编程Elixir>中找到了这个片段。第20章>使用绑定来注入值

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

其中mydef是一个简单的宏,它定义了给定名称的echo方法。因此,我的模块Test应该有两个函数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文档,我们无法使用unquote { {1}}使用bind_quoted

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

1 个答案:

答案 0 :(得分:0)

我认为作者告诉我不解释Macro.escape,它使用bind_quoted绑定变量并同时禁用unquote。因为,至少我不知道如何,只能为宏的某些部分禁用unquote是不可能的,那么必须要做。 由于宏参数在传递给宏之前被引用,我们可以使用我们想要的任何东西,但是取消引用将取决于宏处理的方式,即。例如,当我们在params

中使用unquote时,此代码会引发错误
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

第二次打印调用将扩展为IO.puts(unquote(str))并且出现错误。为了解决这个问题,我们必须转义参数的unquote,因为它是在Kernel.def中完成的 - 使用Macro.escape(expr,unquote:true)。