Wand Maker
Wand Maker

Reputation: 18772

Elixir - Calling / Invoking Macros - UndefinedFunctionError

Why does Elixir report UndefinedFunctionError when calling a macro using Module.macroName syntax in an .exs file? I seem to able to call Macro only if I have another function that calls the macro and I call the function instead of macro.

Below code demonstrates this:

defmodule Sample do
  defmacro doIt(expression) do
    quote do
      IO.puts unquote(expression)
    end
  end

  def doFunc(e), do: doIt(e)
end

Sample.doFunc "Hello World via Function" # Works fine
Sample.doIt "Hello World from Macro!" # Gives error

Output

Hello World via Function
** (UndefinedFunctionError) undefined function: Sample.doIt/1
    Sample.doIt("Hello World from Macro!")
    (elixir) lib/code.ex:307: Code.require_file/2

The Elixir documentation example uses iex, instead of calling macro in the .exs file. Even above code, if we remove calls to Sample.doIt and load it in iex, and then call Sample.doIt works fine.

E:\elixir>iex hello.exs
Hello World via Function
Interactive Elixir (1.0.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> require Sample
nil
iex(2)> Sample.doIt "Hello"
Hello
:ok
iex(3)>

If I try to require Sample in my file above, like below

defmodule Sample
  ... rest of stuff as shown above ...
end

require Sample

Sample.doFunc "Hello World via Function"
Sample.doIt "Hello World from Macro!"

I get error

** (CompileError) hello.exs:11: module Sample is not loaded but was defined. This happens because you are trying to use a module in the same context it is defined. Try defining the module outside the context that requires it.
    (stdlib) lists.erl:1352: :lists.mapfoldl/3
    (stdlib) lists.erl:1353: :lists.mapfoldl/3

Upvotes: 8

Views: 2050

Answers (1)

Paweł Obrok
Paweł Obrok

Reputation: 23194

As usual you need to require a module before using its macros to signify to the compiler the order of compilation of the modules:

defmodule Other do
  def run do
    require Sample

    Sample.doFunc "Hello World via Function"
    Sample.doIt "Hello World from Macro!"
  end
end

Other.run # => Hello World via Function
          #    Hello World from Macro!

Upvotes: 6

Related Questions