🎓 A1Learner
🔍
0 of 46 lessons completed

Elixir Advanced Macros

An advanced macro is a macro that builds new code from values given at the call site. It can define functions, repeat an expression, or choose code by matching a literal value.

A macro can repeat an expression

The macro inserts the call twice. The caller prints 42.

defmodule Twice do
  defmacro double(call) do
    quote do
      unquote(call) + unquote(call)
    end
  end
end

defmodule Caller do
  require Twice
  def value, do: Twice.double(21)
end

IO.puts(Caller.value())

A macro can define functions

The macro turns each name and value into a function. The caller prints both results.

defmodule Maker do
  defmacro make(name, value) do
    quote do
      def unquote(name)(), do: unquote(value)
    end
  end
end

defmodule Caller do
  require Maker
  Maker.make(:answer, 42)
  Maker.make(:greeting, "hi")
end

IO.puts(Caller.answer())
IO.puts(Caller.greeting())

A macro can choose code by literal value

Two macro clauses match two literal values. The caller prints one line for each.

defmodule Status do
  defmacro describe(:ok) do
    quote do: "all good"
  end
  defmacro describe(:error) do
    quote do: "failed"
  end
end

defmodule Caller do
  require Status
  def first, do: Status.describe(:ok)
  def second, do: Status.describe(:error)
end

IO.puts(Caller.first())
IO.puts(Caller.second())

Example

defmodule Maker do
  defmacro make(name, value) do
    quote do
      def unquote(name)(), do: unquote(value)
    end
  end
end

defmodule Twice do
  defmacro double(call) do
    quote do
      unquote(call) + unquote(call)
    end
  end
end

defmodule Caller do
  require Maker
  require Twice
  Maker.make(:base, 40)
  def doubled, do: Twice.double(21)
  def total, do: base() + doubled()
end

IO.puts(Caller.base())
IO.puts(Caller.doubled())
IO.puts(Caller.total())

Key points

  • Macro: a macro is code that writes code during compilation.
  • Call site: the call site is the place where a macro is used.
  • Expansion: expansion is the code a macro produces for a call.
  • Require: a caller module must require a macro module before it uses its macros.
  • Separate module: a macro call lives in a caller module that is separate from the module that defines the macro.

In this example, the maker macro defines a base function, the double macro expands to 21 + 21, and the total function adds the two results.

Note

a macro argument that is a literal value can be matched in the macro definition, as in the status example.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

What does Twice.double(21) expand to?