TheBeast2001
Why do we use macros in elixir?
I have seen macros being used in a lot of code at different parts but never really understood the thought process behind it. Can someone please list some examples with some thought process behind using macro ?
Most Liked
sabiwara
First thing to mention is when NOT to use a macro, that is, almost always. You only want to reach for a macro when you have no choice but to use one. See Library Guidelines — Elixir v1.15.5
There are several reasons you might need a macro, but almost all of them are for libraries, not your typical application code:
-
you need to do something at compile time, e.g. defining a function (
defninNx,Plug.Router…), which regular functions cannot do -
you want a specific DSL like
Ecto.Query -
you want a syntax to generate a data structure that also works in patterns/guards (
../2for ranges, Date/Time sigils like~D…). You can doa..b = 1..10, but functions cannot be called in patterns/guards. -
you need to access the AST, e.g.
assert/1: if it was a function, it would only see the final value (false), not the operation you’re trying to do (>,==…), the operands (left and right), or the code it is printing (code: assert 1 + 2 + 3 + 4 > 15). -
performance/optimization:
Loggercould typically be implemented with functions, but we want to only evaluate the message if the logger level is above the configured level, and an API likeLogger.maybe_info(fn -> "hello #{world}" end)would have a bigger runtime footprint (plus a more clunky API). Instead, we generate efficient code by checking the log level at compile time.
Marcus
For an example the if/2 in Elixir is a macro that expands to a case/2. You can see this if you disassemble the code from the BEAM file.
# macro.exs
Mix.install([{:beam_file, "~> 0.5.3"}])
defmodule Foo do
def pos(x) do
if x > 0 do
"yes"
else
"no"
end
end
end
|> BeamFile.elixir_code!()
|> IO.iodata_to_binary()
|> IO.puts()
> elixir macro.exs
defmodule Elixir.Foo do
def pos(x) do
case :erlang.>(x, 0) do
false -> "no"
true -> "yes"
end
end
end
odix67
a good entry point is here Macros - The Elixir programming language, but also point 1 and 3 of the meta programming section
D4no0
Not necessarily. Modeling things in terms of offloading business validations and having custom rules at compile-time is extremely powerful and can make code that is complex from business side into a delight to work on by application developers.
Not sure I agree with that. I’ve created in quite a few projects DSLs that subsequent developers would use successfully to write business logic in an easy manner. I hope to show more examples in the future as this is a topic that comes around from 90s and it is a very powerful concept, but it can be used only in languages that support metaprogramming.
I would say that the biggest problem now with writing a lot of macros and using metaprogramming is how wild this field is at the moment, it’s extremely powerful but at the same time it requires a better abstraction for specific things. I think that libraries like Spark are a great step into the right direction on abstracting and making easier to use the power of metaprogramming without going into its complexity, but still require a lot of experimenting.
LostKobrakai
Abstractions should be modeled with modules and functions though.
Macros don’t help with abstraction. Macros cannot do what you cannot manually write out as well - by definition. So macros are always optional. They do however allow you to write less code manually by using code generation to let the computer create the boilerplate portion of the desired code.
That doesn’t mean you cannot use macros in your own codebase. But I also wouldn’t call them “typical application code”. Besides code needing to be a macro to interact with a macro of a library (e.g. ecto fragments, stuff around heex, stuff around verified routes, …) truely custom to an application macros in my experience are rare.







