Macros: The Building Blocks
Learn to build custom language features using macros.
We'll cover the following...
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
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
...