Elixir - 如何在我的宏中取消对函数数组的引用?
Elixir - How can I unquote an array of functions in my macro?
免责声明:我知道可以用更简单的方式编写代码,但您应该明白我post 将代码简化为 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
模块定义一个函数,它为上面列表中的每一项接受一个参数,return 一个值。所以,我这样做:
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
)。
我的问题是:
- 如何定义所需的功能?
- 为什么第二种方法中没有定义函数?
我不是 100% 确定为什么,但是在 Included.__using__/1
中的 quote
中,函数的 AST 正在转换为实际函数。如果您在 quote
的开头添加 IO.inspect(events)
,您将得到:
[[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
免责声明:我知道可以用更简单的方式编写代码,但您应该明白我post 将代码简化为 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
模块定义一个函数,它为上面列表中的每一项接受一个参数,return 一个值。所以,我这样做:
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
)。
我的问题是:
- 如何定义所需的功能?
- 为什么第二种方法中没有定义函数?
我不是 100% 确定为什么,但是在 Included.__using__/1
中的 quote
中,函数的 AST 正在转换为实际函数。如果您在 quote
的开头添加 IO.inspect(events)
,您将得到:
[[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