HentaiMew
HentaiMew

Reputation: 337

About invoke macro inside module

defmodule DemoMacro do
  defmacro i_am_macro do
    quote do
      # ……
    end
  end

  i_am_macro # (CompileError) example.exs:10: undefined function i_am_macro/0
end

But imported from other modules, can be successfully called:

defmodule SecondModule do
  require DemoMacro
  DemoMacro.i_am_macro
end

What should I do to make the first piece of code compile?

Upvotes: 3

Views: 667

Answers (1)

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

You are calling the macro from the module body, meaning during the compilation stage. On the compiler stage, this module content is obviously not accessible yet because the module is not compiled.

Move macro into a function:

defmodule DemoMacro do
  defmacro i_am_macro do
    quote do
      IO.puts "Inside a macro"
    end
  end

  def test_macro(), do: i_am_macro()
end

DemoMacro.test_macro()
#⇒ "Inside a macro"

Macro content is an AST that is being injected inplace on each macro call. During the compilation stage, you might use already compiled Elixir code. Understanding the difference between compilation stage and runtime requires kinda mental shift, mostly because in both stages we use same Elixir syntax. But the thing is Elixir, being a compiled language, cannot use the code unless it’s compiled into BEAMs.


You also can use your macro inside @after_compile callback, during compilation stage, immediately after the module BEAM becomes available (compiled):

defmodule DemoMacro do
  defmacro i_am_macro do
    quote do
      IO.puts "Inside a macro"
    end
  end

  @after_compile __MODULE__

  def __after_compile__(env, _bytecode) do
    i_am_macro()
  end
end
#⇒ immediately prints "Inside a macro"

Upvotes: 5

Related Questions