Order of evaluation of macro context

Here’s one example where (4) is very useful. ie Defering unquote to an expansion phase after the macro is inserted in the calling place.

This example applies two concepts:

  1. Module attributes are evaluated after macro expansion. Which means you can’t evaluate them in a macro. So they need to be evaluated after the AST returned by the macro is inserted into the caller.

  2. Special forms are also macros. So we need to help the compiler understand when to expand an unquote - in the macro or in the caller.

defmodule MyMacros do
  @moduledoc """
  Define a functions based upon a list of values
  """
  defmacro with_vals(values) do
    quote bind_quoted: [values: values] do
      # `for` introduces its own macro context in which
      # we want to unquote. So the unquote needs to be
      # deferred until a later expansion in the calling
      # site.  `bind_quoted: [values: values]` means that
      # referring to `values` in the quote body is automatically
      # unquoted. And any `unquote` is *not* evaluated in the
      # macro context but will be evaluated *after* the
      # returned AST is inserted into the calling site.
      for val <- values do
        def n(unquote(val)), do: unquote(val + 2)
      end
    end
  end
end

defmodule MyModule do
  require MyMacros
  @all_vals [1,2,3,4,5,6]

  # defines 6 function clauses for the
  # function `n`.
  MyMacros.with_vals(@all_vals)
end

In summary, here is a case where the entire for expression - including the unquote(...) is inserted unmodified into the calling site. Since for, def, defmodule are all themselves macros, its completely ok to unquote in their contexts. These cases are referred to as unquote fragments

4 Likes