简体   繁体   中英

How do you pass a function capture to a macro in Elixir?

This here doesn't work. Using Macro.expand/2 doesn't help either.

    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

This is the IEx output:

    [{:__aliases__, [line: 24], [:Phoenix, :HTML, :Link]}, :link]

    ** (BadFunctionError) expected a function, got: nil
        expanding macro: ExBs.Component.defcomp/2

Is it possible?

Macros do receive AST and return AST . The returned AST is injected in the place where this macro was called from, instead of the call to macro. To return AST one uses Kernel.quote/2 . That said, the below would work:

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"

Note, that the function was executed during the compilation stage , namely while the macro was expanded. That said, the resulting BEAM has no trail of this function.

I doubt this is what you actually wanted, but without knowing what you are trying to achieve, it's impossible to suggest something more valuable. In any case, now the code copies and works as designed.


Wild guess: if you wanted to declare the function :foo wrapping func , the below would do:

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"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM