你如何将函数捕获传递给 Elixir 中的宏?
How do you pass a function capture to a macro in Elixir?
这里不行。使用 Macro.expand/2
也无济于事。
defmodule ExBs.Component do
defmacro defcomp(name, opts) do
func = Keyword.get(opts, :func)
IO.inspect(func)
func.("foo")
end
end
defmodule ExBs.Foo do
import ExBs.Component
defcomp(:foo,
func: &Phoenix.HTML.Link.link/2
)
end
这是 IEx 输出:
[{:__aliases__, [line: 24], [:Phoenix, :HTML, :Link]}, :link]
** (BadFunctionError) expected a function, got: nil
expanding macro: ExBs.Component.defcomp/2
可能吗?
宏 接收 AST 和 return AST。 returned AST 被注入到调用这个宏的地方,而不是调用宏。要 return AST,请使用 Kernel.quote/2
。也就是说,下面的方法可行:
defmodule ExBs.Component do
defmacro defcomp(name, opts) do
quote do
func = Keyword.get(unquote(opts), :func, fn _ -> "" end)
IO.inspect(func)
func.("foo")
end
end
end
defmodule ExBs.Foo do
import ExBs.Component
defcomp(:foo,
func: &IO.inspect/1
)
end
#⇒ #Function<0.48519526 in file:iex>
"foo"
请注意,该函数是在编译阶段 期间执行的,即宏展开时。也就是说,生成的 BEAM 没有此功能的踪迹。
我怀疑这是否是您真正想要的,但在不知道您想要实现的目标的情况下,不可能提出更有价值的建议。无论如何,现在代码复制并按设计工作。
大胆的猜测:如果你想声明函数 :foo
包装 func
,下面会做:
defmodule ExBs.Component do
defmacro defcomp(name, opts) do
func = Keyword.get(opts, :func)
quote do
def unquote(name)(), do: unquote(func).("foo")
end
end
end
defmodule ExBs.Foo do
import ExBs.Component
defcomp(:foo,
func: &IO.inspect/1
)
end
ExBs.Foo.foo
#⇒ "foo"
这里不行。使用 Macro.expand/2
也无济于事。
defmodule ExBs.Component do
defmacro defcomp(name, opts) do
func = Keyword.get(opts, :func)
IO.inspect(func)
func.("foo")
end
end
defmodule ExBs.Foo do
import ExBs.Component
defcomp(:foo,
func: &Phoenix.HTML.Link.link/2
)
end
这是 IEx 输出:
[{:__aliases__, [line: 24], [:Phoenix, :HTML, :Link]}, :link]
** (BadFunctionError) expected a function, got: nil
expanding macro: ExBs.Component.defcomp/2
可能吗?
宏 接收 AST 和 return AST。 returned AST 被注入到调用这个宏的地方,而不是调用宏。要 return AST,请使用 Kernel.quote/2
。也就是说,下面的方法可行:
defmodule ExBs.Component do
defmacro defcomp(name, opts) do
quote do
func = Keyword.get(unquote(opts), :func, fn _ -> "" end)
IO.inspect(func)
func.("foo")
end
end
end
defmodule ExBs.Foo do
import ExBs.Component
defcomp(:foo,
func: &IO.inspect/1
)
end
#⇒ #Function<0.48519526 in file:iex>
"foo"
请注意,该函数是在编译阶段 期间执行的,即宏展开时。也就是说,生成的 BEAM 没有此功能的踪迹。
我怀疑这是否是您真正想要的,但在不知道您想要实现的目标的情况下,不可能提出更有价值的建议。无论如何,现在代码复制并按设计工作。
大胆的猜测:如果你想声明函数 :foo
包装 func
,下面会做:
defmodule ExBs.Component do
defmacro defcomp(name, opts) do
func = Keyword.get(opts, :func)
quote do
def unquote(name)(), do: unquote(func).("foo")
end
end
end
defmodule ExBs.Foo do
import ExBs.Component
defcomp(:foo,
func: &IO.inspect/1
)
end
ExBs.Foo.foo
#⇒ "foo"