
deadbeef
Metaprogramming Elixir: code example error (page 22)
The code example on page 22 is intended to show how to re-create Elixir’s if
macro. However, it uses if
on line 3.
The macro block just passes the args to if
. This could be a typo and meant to be my_if
. If that’s the case, a simple substitution would result in the error ... cannot invoke macro my_if/2 before its definition
.
defmacro my_if(expr, do: if_block), do: if(expr, do: if_block, else: nil)
^^
That leads me to believe the example can be re-written to be more similar to Elixir’s own implementation^1, e.g.
defmodule ControlFlow do
defmacro my_if(expr, clauses) do
build_my_if(expr, clauses)
end
defp build_my_if(expr, do: if_block) do
build_my_if(expr, do: if_block, else: nil)
end
defp build_my_if(expr, do: if_block, else: else_block) do
quote do
case unquote(expr) do
result when result in [false, nil] -> unquote(else_block)
_ -> unquote(if_block)
end
end
end
end
Same idea, but instead of 2 macros we have 1 macro that passes to a private function with 2 clauses.
Further, using the built-in if
leads to an unexpected expansion, using the example:
iex> quote do ControlFlow.my_if 1 == 1, do: :ok end |> Macro.expand_once(__ENV__)
:ok
I believe this is because Elixir’s implementation further reduces tautologies^2, which the example doesn’t. From the example, we’d expect to see :case
in the resulting AST (this confusion is what lead me here originally).
Popular Prag Prog topics










Other popular topics










Latest in PragProg
Latest (all)
Categories:
Popular Portals
- /elixir
- /rust
- /wasm
- /ruby
- /erlang
- /phoenix
- /keyboards
- /js
- /rails
- /python
- /security
- /go
- /swift
- /vim
- /clojure
- /java
- /haskell
- /emacs
- /svelte
- /onivim
- /typescript
- /crystal
- /c-plus-plus
- /tailwind
- /kotlin
- /gleam
- /react
- /flutter
- /elm
- /ocaml
- /vscode
- /opensuse
- /centos
- /ash
- /php
- /deepseek
- /scala
- /zig
- /html
- /debian
- /nixos
- /lisp
- /agda
- /sublime-text
- /textmate
- /react-native
- /kubuntu
- /arch-linux
- /ubuntu
- /revery
- /manjaro
- /django
- /spring
- /diversity
- /lua
- /nodejs
- /slackware
- /julia
- /c
- /neovim