Reputation: 35349
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?
Upvotes: 0
Views: 359
Reputation: 121000
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"
Upvotes: 1