Creating anonymous functions via macros

孤街醉人 提交于 2019-12-24 17:08:36

问题


I'm tying to make an API where an anonymous function are composed from macros, e.g.

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

should use the transform head and body[:do] as the heads and bodies for an anonymous function. For example, the above macro calls The example above should be collected into:

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

With unquote fragments it's easy to create new named function defs, but not new anonymous functions:

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

Here's my current progress:

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

However, the nested unquote fails with the same unquote called outside quote error.

At this point I'm just going to use a plain anonymous function, the macro approach was overkill, but I'd still be interested to know if this is possible.

Thanks in advance!


回答1:


At this point I'm just going to use a plain anonymous function, the macro approach was overkill, but I'd still be interested to know if this is possible.

This is exactly what I was going to propose. :) The anonymous function is much simpler and it also makes the scoping rules clear, works nice with composition and so on.

unquote fragments are really a convenience for defining module functions, they do not apply for any code exactly because it then becomes impossible to know when the unquote fragment applies. For example, if you had this:

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

How do you know if it is meant to apply to foo or to the anonymous function? In any case, to answer your question, you need to define the code explicitly in the quote:

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

Or:

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


来源:https://stackoverflow.com/questions/24849774/creating-anonymous-functions-via-macros

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!