
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
- /opensuse
- /rust
- /kotlin
- /ruby
- /erlang
- /python
- /clojure
- /react
- /quarkus
- /go
- /vapor
- /react-native
- /v
- /wasm
- /django
- /security
- /nodejs
- /centos
- /rails
- /haskell
- /fable
- /gleam
- /js
- /swift
- /deno
- /tailwind
- /assemblyscript
- /laravel
- /symfony
- /phoenix
- /crystal
- /typescript
- /debian
- /adonisjs
- /julia
- /arch-linux
- /svelte
- /spring
- /c-plus-plus
- /preact
- /flutter
- /actix
- /java
- /angular
- /ocaml
- /kubuntu
- /zig
- /scala
- /zotonic
- /vim
- /rocky
- /lisp
- /keyboards
- /html
- /emacs
- /nim
- /vuejs
- /elm
- /nerves