你如何将函数捕获传递给 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"