An Elixir program can be represented by its own data structures as a nested (abstract syntax) tree of tuples with three elements. For example, the function call run(1, 2, 3)
is represented internally as:
Macros in Elixir lets you perform syntactic extensions, which are expanded to before a program executes. We use macros to transform our internal program structure by treating code as data, and thus metaprogram.
Example
We defined a macro with defmacro
, and take in a block of code as arguments. Macros in Elixir transforms the block of code
into its internal Abstract Syntax Tree (AST) representation, which is a nested tree of triples (3-size tuples.) When the macro macro
returns code
, that internal representation is injected back into the global program’s compile tree. In the above example, we just return it unmodified.
Let’s call our macro:
What’s most useful here is that we can modify that internal representation before returning, transforming it into a completely different piece of code.
Note that
code
was never executed/evaluated, only the returnednewcode
was executed.
quote
lets us transform a block of code into its internal AST representation. Internally, defmacro
calls this method to passed in parameters.
In the example above, we return a new internal representation newcode
instead of the original code
. Running the macro on any block of code results in the insertion and execution of our newcode
.
What if we want to evaluate a code block? unquote
lets you defer execution of the code block it receives, only running it when the code generated by quote
is executed. You can only use unquote
inside quote
blocks.
Here’s an example:
In the above example, we evaluate code using unquote
and use its result to perform some computation dispatched via pattern matching.
You can find real life examples of macros in Phoenix and ExUnit.
Additional reading: