Fun With Elixir Macros by Daniel Jaouen

2017-11-12

Today we’re going to dive into a topic that’s been on my mind lately. And that topic is macro-writing macros!

Let’s take a look at an example:

defmodule Test do
  defmacro test(one, two, three, four) do
    one |> IO.inspect

    quote do
      unquote(two) |> IO.inspect

      defmacro unquote(one)(five, six) do
        unquote(three) |> IO.inspect
        four = unquote(four)
        quote do
          [unquote(four), unquote(five), unquote(six)]
        end
      end
    end
  end
end

defmodule Test.Test do
  require Test

  Test.test(:one, :two, :three, :four)
end

In this example, we see that we have a macro-writing macro. The module Test.Test calls the macro Test.test, which takes four arguments. The macro then prints the value of one and then enters a quoted block and prints out the value of unquote(two). The macro then defines another macro, which takes two arguments and whose name is unquote(one). This new macro then prints out the value of unquote(three) and assigns unquote(four) to four. The macro then returns a quoted list containing three elements (four, five, and six, all unquoted).

Let’s load this up in IEx and see what happens.

○ iex -S mix
Erlang/OTP 20 [erts-9.1] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]

Compiling 1 file (.ex)
:one
:two
Interactive Elixir (1.5.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> require Test.Test
Test.Test
iex(2)> Test.Test.one(:five, :six)
:three
[:four, :five, :six]
iex(3)>

We see immediately that :one and :two are printed out. Then, when we invoke Test.Test.one(:five, :six), :three is printed out and the return value is [:four, :five, :six].

Takeaways.

What we can take away from this example is that evaluation happens one step at a time. The Elixir runtime evals all code up to the inner-most quote block and interpolates all unquoted variables not included in that block. Then, when we call that macro (and only then), four, five, and six are interpolated. Note that we don’t have access to one, two, or three in the inner-most quoted block and we only have access to four because we unquoted it in the inner-most macro definition.

And that’s it. Feel free to fire up an instance of IEx and try it out for yourself.

Comment

Back