What is Macro?
Macro is a code that writes code. Macros are one of the most advanced and powerful features of Elixir. They make it possible to perform powerful code transformations in compilation time. . Before jumping into macros lets have a basic understanding of the abstract syntax tree (AST)
What is Abstract Syntax Tree(AST)?
Abstract syntax trees are used to represent the structure of a program’s source code for the compiler to use. Compiler or interpreter generates AST before generating the machine code.
Let's take a look at AST representation for expression 2 * 6 - 9 looks like this

Elixir's creator designed the language differently. In elixir, AST is exposed in a form that can be represented by Elixir’s own data structures. you can represent elixir code in AST format by using a quote macro. you can pass a chunk of code as an argument to quote macro it will return the AST of that syntax
quote:
The main purpose of the quote is to retrieve an internal
representation of the code
iex(1)> quote do: 1 + 2
{:+, [context: Elixir, import: Kernel], [1, 2]}
Elixir AST is a three-element tuple. the first element represents the function, the second element represents metadata and the third element represents the arguments list
you can use Macro.to_string to convert AST to string
iex(6)> Macro.to_string( quote do: 1 + 2)
"1 + 2"
you can evaluate AST using Code.eval_quoted function
iex(12)> ast = quote do: 1 + 2
{:+, [context: Elixir, import: Kernel], [1, 2]}
iex(13)> Code.eval_quoted(ast)
{3, []}
unquote:
unquote is another macro you need to be aware of, It helps to inject
the chunk of code inside the AST, you can not access outside values
inside the macro directly, unquote helps you to access those values
eg: iex(7)> num = 1
iex(8)> quote do: num + 2
{:+, [context: Elixir, import: Kernel], [{:num, [], Elixir}, 34]}
unquote works smiliarly as string inpterpoation, it injects values inside quote
iex(31)> quote do: unquote(num) + 2
{:+, [context: Elixir, import: Kernel], [1, 2]}
I hope you got an idea of What AST is and how it represents in
elixir. If you are still confused don't worry you will get clarity
by the end of this blog.
You can define macro using defmacro keyword. macros accept AST as
argument and return AST value. Let's build a macro together to get
more clarity
If you are from the ruby background you are already familiar with
unless statements. basically, unless statement works opposite of the
if statement
if 2== 2 , do: IO.puts "Hello", else: "World"
In this statement, The output will be Hello
` if we use unless in the above expression `unless 2== 2, do: IO.
puts "this", else: "that"
the result will be that
Before building our own unless macro let's define rules first. Rules
are very simple It should accept two arguments, the first argument
is an expression that needs to be evaluated and the second argument
is a list of do, else keyword arguments, if the expression is false
or nil do block will be executed otherwise else block will be
executed
When you pass any argument it macro it will implicitly convert into
AST statement
defmodule ControlFlow do
defmacro unless!(expression, [do: this, else: that] ) do
quote do
case (unquote(expression)) do
x when x in [false, nil] -> unquote(this)
_ -> unquote(that)
end
end
end
end
Macros always return AST value, we wrapped our entire logic inside
the quote block. we evaluated the expression using unquote macro it
will give true, false, or nil value. If the expression is false or
nil, we unquote `this` statement otherwise we unquote `that`
statement
Lets test this in iex terminal
ex(13)> require ControlFlow
ControlFlow
iex(14)> import ControlFlow
ControlFlow
iex(15)> unless! 2 == 3 , do: IO.puts("Execute unless macro"), else: IO.puts("Failed to excute unless macro ")
Execute unless macro
:ok
we succefully created our own macro.
We can perform pattern matching on macros. Let's define a new macro
add, If the input to the macro pattern match with tuple {:+, _,
[lhs, rhs] } and perform addition operation on lhs and rhs
defmodule ControlFlow do
defmacro add({:+, _, [lhs, rhs]}) do
quote do
unquote(lhs + rhs)
end
end
end
Macros are really powerful most of the elixir code is written using
macros
I hope you understand the power of macros in elixir. You can learn
more about elixirs in
https://elixir-lang.org/getting-started/meta/macros.html
and the Metaprogramming in Elixir by ChrisMcCord