免责声明:我知道可以用更简单的方式编写代码,但您应该理解我将简化代码发布到SO。
我有一个使用Simple
的模块Included
:
defmodule Simple do
use Included,
events: [
[
name: :start,
callback: fn(x) -> x * 2 end
], [
name: :finish,,
callback: fn(x) -> x * 3 ; x end
]
]
end
我希望Included
模块定义一个函数,该函数为上面列表中的每个项目取一个参数并返回一个值。所以,我这样做:
defmodule Included do
defmacro __using__(opts)
events = Keyword.get(opts, :events)
quote bind_quoted: [events: events] do
events
|> Enum.each(fn(event) ->
def unquote(event[:name])(x) do
x
|> unquote(event[:callback]).()
|> IO.puts
end)
end
end
end
这里的问题是我收到了invalid quoted expression: #Function<0.105634730
。我试图用另一种方式实现它:
defmodule Included do
defmacro __using__(opts)
events = Keyword.get(opts, :events)
events
|> Enum.each(fn(event) ->
quote bind_quoted: [event: event] do
def unquote(event[:name])(x) do
x
|> event[:callback].()
|> IO.puts
end
end
end)
end
end
但在这种情况下,我还没有看到定义的功能。 (没有错误,这里没有函数Simple.start/1
和Simple.finish/1
。)
我的问题是:
答案 0 :(得分:1)
我不是100%确定原因,但在quote
中的Included.__using__/1
内,函数的AST正在转换为实际函数。如果您在IO.inspect(events)
的开头添加quote
,则会获得:
[[name: :start, callback: #Function<0.18558591 in file:c.exs>],
[name: :finish, callback: #Function<1.18558591 in file:c.exs>]]
我找到的解决方法是逃避事件中的:callback
。
defmacro __using__(opts) do
events = for event <- opts[:events] do
Keyword.update!(event, :callback, &Macro.escape/1)
end
quote bind_quoted: [events: events] do
...
end
最终代码:
defmodule Included do
defmacro __using__(opts) do
events = for event <- opts[:events] do
Keyword.update!(event, :callback, &Macro.escape/1)
end
quote bind_quoted: [events: events] do
events
|> Enum.each(fn(event) ->
def unquote(event[:name])(x) do
x
|> unquote(event[:callback]).()
|> IO.puts
end
end)
end
end
end
defmodule Simple do
use Included,
events: [
[
name: :start,
callback: fn(x) -> x * 2 end
], [
name: :finish,
callback: fn(x) -> x * 3 ; x end
]
]
end
Simple.start 10
Simple.finish 10
输出:
20
10