Macro.expand_once

You're seeing just the function expand_once, go back to Macro module for more information.

Receives an AST node and expands it once.

The following contents are expanded:

If the expression cannot be expanded, it returns the expression itself. This function does not traverse the AST, only the root node is expanded.

expand_once/2 performs the expansion just once. Check expand/2 to perform expansion until the node can no longer be expanded.

Examples

In the example below, we have a macro that generates a module with a function named name_length that returns the length of the module name. The value of this function will be calculated at compilation time and not at runtime.

Consider the implementation below:

defmacro defmodule_with_length(name, do: block) do
  length = length(Atom.to_charlist(name))

  quote do
    defmodule unquote(name) do
      def name_length, do: unquote(length)
      unquote(block)
    end
  end
end

When invoked like this:

defmodule_with_length My.Module do
  def other_function, do: ...
end

The compilation will fail because My.Module when quoted is not an atom, but a syntax tree as follows:

{:__aliases__, [], [:My, :Module]}

That said, we need to expand the aliases node above to an atom, so we can retrieve its length. Expanding the node is not straightforward because we also need to expand the caller aliases. For example:

alias MyHelpers, as: My

defmodule_with_length My.Module do
  def other_function, do: ...
end

The final module name will be MyHelpers.Module and not My.Module. With Macro.expand/2, such aliases are taken into consideration. Local and remote macros are also expanded. We could rewrite our macro above to use this function as:

defmacro defmodule_with_length(name, do: block) do
  expanded = Macro.expand(name, __CALLER__)
  length = length(Atom.to_charlist(expanded))

  quote do
    defmodule unquote(name) do
      def name_length, do: unquote(length)
      unquote(block)
    end
  end
end