【问题标题】:Quote function name in Elixir在 Elixir 中引用函数名
【发布时间】:2017-03-13 01:03:14
【问题描述】:

我现在正在学习 Elixir,我真的对 quoteunquote 感到困惑。 下面是我们如何使用 macro 创建具有动态名称的函数:

defmacro create_fun(n) do
  quote do: def unquote(:"times_#{n}")(a), do: a * 4
end

它创建函数times_6,例如,如果我将6 作为宏参数传递。

现在我不明白:这里我们取消引用原子:"times_#{n}"。 Elixir 文档说,当你引用一个原子时,它会返回一个原子。所以当我取消引用一个原子时,我也应该取回这个原子。这是真的:

iex(15)> Macro.to_string quote do: unquote(:"times_6")
":times_6"

但是在引用之后使用() 会给出这样的结果:

iex(14)> Macro.to_string quote do: unquote(:"times_6")()
"times_6()"

带括号的原子突然变成不是原子。如果我用:"times_6" 替换unquote(:"times_6"),它就不起作用了:

iex(4)> Macro.to_string quote do: :"times_6"()
** (SyntaxError) iex:4: syntax error before: '('

请问这是怎么回事,我不明白

【问题讨论】:

  • 不应该是do: a * unquote(n) 而不是a * 4 吗?
  • 是的。它应该。但这不是重点。这个宏工作正常。我只是试图理解为什么括号有区别。因为一开始我不知道用括号引用/取消引用原子突然返回的不是原子。
  • 这里的问题不在于宏,而在于:"times_6"()。这本身就是一个无效的陈述
  • 是的。但是为什么unquote(:"times_6")() 是正确的? unquote(:"times_6") 应该返回你 :"times_6"

标签: function macros elixir quote


【解决方案1】:

这就是 quoteunquote 在 Elixir 中的实现方式

来自Elixir Metaprogramming Guide

Elixir 程序的构建块是一个包含三个 元素。例如函数调用sum(1, 2, 3)表示 内部为:

iex> quote do: sum(1, 2, 3)
{:sum, [], [1, 2, 3]}

第一个元素是函数名,第二个是关键字列表 包含元数据,第三个是参数列表。


模块和方法名称在 Elixir 中内部表示为 atoms。直接查看调用unquote(:"hello")unquote(:"hello")() 时生成的底层长生不老药数据可能有助于澄清这一点:

iex(27)> quote do: unquote(:"hello")   
:hello

iex(28)> quote do: unquote(:"hello")() 
{:hello, [], []}

第一个简单地返回一个原子,而第二个返回一个 Elixir 数据结构(由 3 个元素组成的元组),它表示对带有 0 个参数的 hello 方法的函数调用。 unquote(:"hello")() 转换为 hello(),然后用作方法。

【讨论】:

  • 非常感谢!我会记住,括号的存在会改变原子的引用/取消引用行为。抱歉我的疏忽
  • unquote(:"hello")() 没有被进一步“转换”,它已经是一个 AST,代表一个函数。反之亦然:源代码中的hello() 被编译器转换{:hello, [], []} AST :)
  • 是的,我知道,但我试图用一种更容易理解的方式来表达它。我猜我做了相反的事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-27
  • 1970-01-01
  • 2017-08-25
  • 2018-03-07
  • 2018-08-16
  • 2014-03-25
相关资源
最近更新 更多