通过宏创建匿名函数

时间:2014-07-20 10:55:09

标签: elixir

我想制作一个API,其中匿名函数由宏组成,例如

transform [x, y], do: x + y
transform x,      do: x

应使用transform headbody[:do]作为匿名函数的头部和正文。例如,上面的宏调用上面的例子应该收集到:

fn [x, y] -> x + y; x -> x end

使用非引用片段,可以轻松创建新的命名函数def,但不能创建新的匿名函数:

iex> val = 1
iex> fn() -> unquote(val) end
** (CompileError) iex:99: unquote called outside quote
(elixir) src/elixir_exp_clauses.erl:23: :elixir_exp_clauses.clause/5
(elixir) src/elixir_fn.erl:33: anonymous fn/3 in :elixir_fn.expand/3
(stdlib) lists.erl:1237: :lists.map/2
(elixir) src/elixir_fn.erl:36: :elixir_fn.expand/3

这是我目前的进展:

defmacro anonymous_fn(parts) do
  quote bind_quoted: [parts: parts] do
    fn_branches = for {head, body} <- parts, do: {:->, [], [[head], body]}
    unquote({:fn, [], fn_branches})
  end
end

但是,嵌套的unquote失败并出现相同的unquote called outside quote错误。

此时我只是使用简单的匿名函数,宏观方法过度,但我仍然有兴趣知道这是否可行。

提前致谢!

1 个答案:

答案 0 :(得分:2)

  

此时我将使用一个简单的匿名函数,宏观方法过度,但我仍然有兴趣知道这是否可行。

这正是我要提出的建议。 :)匿名函数更简单,它也使得作用域规则清晰,适用于合成等等。

非引号片段实际上是定义模块函数的便利,它们不能完全适用于任何代码,因为它无法知道何时应用非引用片段。例如,如果你有这个:

def foo do
  fn -> unquote(bar) end
end

您如何知道它是否适用于foo或匿名函数?无论如何,要回答您的问题,您需要在引号中明确定义代码:

defmacro anonymous_fn(parts) do
  fn_branches = for {head, body} <- parts, do: {:->, [], [[head], body]}
  {:fn, [], fn_branches}
end

或者:

defmacro anonymous_fn(parts) do
  fn_branches = for {head, body} <- parts do
    quote do: (unquote(head) -> unquote(body))
  end
  {:fn, [], fn_branches}
end
相关问题