...

/

Macros: The Building Blocks

Macros: The Building Blocks

Learn to build custom language features using macros.

Recreating Elixir’s unless macro

Suppose that Elixir lacks a built-in unless construct. In most languages, we would have to settle for if! expressions and learn to accept this syntactic shortcoming. Fortunately for us, Elixir isn’t like most languages.

Let’s define our own unless macro, using if as a building block of our implementation. Macros must be defined within modules, so let’s define a ControlFlow module.

defmodule ControlFlow do
  defmacro unless(expression, do: block) do 
    quote do
      if !unquote(expression), do: unquote(block)
    end
  end
end
A `ControlFlow` module with unless macro.

Note: Run the project and try the commands given below:

iex> c "unless.exs"
# Output: [ControlFlow]

iex> require ControlFlow
# Output: nil

iex> ControlFlow.unless 2 == 5, do: "block entered"
# Output: "block entered"

iex> ControlFlow.unless 5 == 5 do
...>   "block entered"
...> end
# Output: nil

We must first require ControlFlow before invoking its macros in cases where the module hasn’t already been imported.

Since macros receive the AST representation of arguments, we can accept any valid Elixir expression as the first argument to unless on line 2. In our second argument, we can pattern-match the provided do/end ...