Skip to content
Elixir

Metaprogramming with Macros

Write code that writes code at compile time.

By EZ4Code Team
macrometaprogrammingast

Code

# Macro: generates code at compile time
defmodule MyMacros do
  defmacro unless(condition, do: block) do
    quote do
      if not unquote(condition), do: unquote(block)
    end
  end

  defmacro my_if(cond, do: yes, else: no) do
    quote do
      case unquote(cond) do
        true -> unquote(yes)
        false -> unquote(no)
      end
    end
  end
end

# Usage
import MyMacros
unless false do
  IO.puts("This prints")
end

# Inspecting AST
ast = quote do: 1 + 2 * 3
IO.inspect(ast)
# {:+, [context: ...], [1, {:*, [...], [2, 3]}]}

# Macro that defines a function
defmodule Loggable do
  defmacro deflogged(name, do: body) do
    quote do
      def unquote(name) do
        IO.puts("Calling #{unquote(name)}")
        result = unquote(body)
        IO.puts("Result: #{inspect(result)}")
        result
      end
    end
  end
end

defmodule Example do
  import Loggable
  deflogged greet do
    "Hello!"
  end
end

Explanation

Macros operate on ASTs (abstract syntax trees) at compile time — they receive code as data and return new code. quote wraps code as AST; unquote injects values into the AST. Macros are the foundation of Elixir's DSLs (Plug, Ecto, Phoenix routes). Use sparingly — they make code harder to debug. Always check macro hygiene rules.

More Elixir Snippets