What are your tips on reading source code containing macros?

I am new to elixir. IMO, elixir is easy to use, but reading the source code is comparatively hard due to macros, attributes …, and vscode extensions seem to be of little help resolving definitions and references.
I mean, you can neatly hide the details underlying the interface using elixir, but there can be thousands of complex ways to implement it owing to the flexibility of lang.
I am curious how experienced or any elixir programmers think about this.

1 Like

Hey @dewilliam are you talking about the source code of the Elixir language itself?

Sorry for the ambiguity of the title. Actually, I am referring to any programs written in elixir.
I was reading source code of phoenix, plug …, it seems to me the macros and related features make the source code harder to read than other languages and I can’t help thinking why and if it is just my inexperience.
I think that macros may provide flexibility, but it is not what you see is what you get, there is two versions, one is what you see, the other is what it is when it gets expanded.
I am fine with many languages like python, javascript, or even c. C’s macros is rather simple though can be complicated too. Actually, macros in elixir reminds me of m4, the general-purpose macro processor in c.
Also, how do you annotates the types of macros?
With just functions and variables, all types can be easily figured out, and that is very useful for understanding codes.

Macros are always a contentious feature and that is never going to change. In our case, Elixir wouldn’t be Elixir without macros, so regardless of how one may feel about them, to write Elixir is to accept macros. Key constructs like defmodule, and def are macros! They allow Elixir to stay very close to Erlang while reducing a lot of the boilerplate that comes with writing Erlang (the first chapter of Elixir in Actions gives a good example of this). Otherwise, as you touched on, they allow the language itself to stay incredibly simple while allowing for really flexible extensibility. One of the best things about Elixir is that the language itself doesn’t change much (it’s often repeated how there will likely never be a 2.0, at least not in foreseeable future).

Personally I really like macros but I live by the advice I learned from the Ruby world: any form of metaprogramming should only be used in libraries, never ever in business logic. It does mean you need to somewhat understand macros if you want to read library code, though since Elixir macros are compile-time, running Macro.expand on any piece of macro code will give you exactly what you’re going to get at compile time. It’s an annoying extra step for sure but I know there are editor plugins that will do it for you.

1 Like

I think it depends a lot on where you look. If you’re trying to read the source code of the DSLs that Phoenix / Ecto / Plug provide then yes, there are a lot of macros, and that isn’t easy to work out.

However if you look at the code that actually processes your data, macros don’t feature nearly as heavily. For example Plug.Conn is all just regular functions, Phoenix.Controller has the macros that set up a controller boilerplate, but all of the actual req / response calls are regular functions, etc. Ecto.Changeset is all functions, etc. So it depends.

Macros are still somewhat a dark region for documentation and debugging, as this is a feature that almost no other language has, we are still learning on how to write correctly macros and document them. Phoenix uses them extensively for all the magic it does (just like rails).

In general the approach in elixir community is to read documentation, because documentation is a first-class citizen, and not code, opposite to how you would do it in golang. A good library/framework should abstract completely the inner workings from the apis it delivers to the developer.

If you want to learn about macros, I would recommend to start with the book Metaprogramming Elixir by Chris McCord, once you understand the concepts on how compile-time code generation works, it will open a lot of doors in terms of designing software.

1 Like