Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elixir: use macro in the body of same module that defined it

This is common elixir:

defmodule Fizz do
  defmacro asdf, do: IO.puts("asdf")
end

defmodule Buzz do
  require Fizz
  Fizz.asdf
end

However, although you can reference macros in the same context like:

defmodule Fizz do
  # ...
  defmacro asdf_qwer, do: asdf && IO.puts("qwer")
end

... you can't reference macros in the body of the same module that defined them:

defmodule Fizz do
  defmacro asdf, do: IO.puts("asdf")
  asdf
end

This raises undefined function asdf/0.

Is there a workaround for this "problem"? Sometimes I may want to use macros to remove some boilerplate from the module I'm working on, and that macro's functionality may be specific enough not to put it in another module.

like image 676
ichigolas Avatar asked Jul 29 '16 15:07

ichigolas


People also ask

When should I use macros in Elixir?

Elixir already provides mechanisms to write your everyday code in a simple and readable fashion by using its data structures and functions. Macros should only be used as a last resort. Remember that explicit is better than implicit. Clear code is better than concise code.

What is __ module __ In Elixir?

__MODULE__ is a compilation environment macros which is the current module name as an atom. Now you know alias __MODULE__ just defines an alias for our Elixir module. This is very useful when used with defstruct which we will talk about next. In the following example, we pass API.

What does use mean in Elixir?

here's a lightweight explanation of 'use' in elixir as I currently understand it. Use is a tool to reuse code just like require, import, and alias. Use simply calls the __using__ macro defined in another module. The __using__ macro allows you to inject code into another module.


1 Answers

The reason that we're getting undefined function errors here is because at compile time, the asdf macro does not yet exist.

So we need to notify the compiler that an extra step is required just before compilation is finished.

One option is the @after_compile module callback attribute which lets you invoke some code just after compilation in order to perform a final bit of code generation.

For example:

defmodule M do
  @after_compile __MODULE__

  def __after_compile__(env, _bytecode) do
    IO.inspect env
  end
end

Another option is to put your macros in a Fizz.Macros module.

like image 175
Yos Riady Avatar answered Oct 12 '22 21:10

Yos Riady