Care to elaborate on the “pick out bugs easily” part?
If this is primarily about macros then it is important remember that there are always tradeoffs involved. Example:
defmodule Demo do
use Attr
attr :three
attr :two
attr :one
end
IO.inspect(Demo.get_attrs()) # [:one, :two, :three]
# ???
Evidently it’s equivalent to:
defmodule Demo1 do
def get_attrs(),
do: [:one, :two, :three]
end
which isn’t transparent on first sight unless you are already familiar with
# Blatantly plagiarized from Plug.Build
defmodule Attr do
defmacro __using__(_opts) do
quote do
import Attr, only: [attr: 1]
Module.register_attribute(__MODULE__, :attrs, accumulate: :true)
@before_compile Attr
end
end
defmacro __before_compile__(env) do
attrs = Module.get_attribute(env.module, :attrs)
quote do
def get_attrs() do
unquote(attrs)
end
end
end
defmacro attr(attr) do
quote do
@attrs unquote(attr)
end
end
end
In a way the macro invocation is an alternate representation (compression) of the code that it will leave behind. That succinctness suppresses certain details (which may be desired) some of which could improve clarity (e.g. showing how the resulting code connects and relates to the “rest of the world”). The only way to compensate for that lack of exposed detail is to understand what the macro actually represents. So improved succinctness may also increase the cognitive load for anybody trying to comprehend what the code does.
In many situations the tradeoff is worth it. But every case is still is a balancing act between the benefits of succinctness, reuse potential and the effect on clarity and comprehensibility. Not all abstractions are created equal.


















