I often get into trouble with Elixir and compile-time values. I’m wondering if there are any IDE plugins that highlight areas of code that are evaluated at compile time. E.g. module attributes and macros. Sometimes I’m not always 100% sure what is a macro and what might be some interesting new feature that some clever developer has deployed. So it would be a helpful reminder that some stuff is evaluated at compile time, and that may affect how I structure certain code.
The problem is that until you do compile, you can’t know which part will be evaluated, since the compilation stage runs any code at top/module level and that code can do anything.
As a rule of thumb, if its not within a def
(p
) or if is a macro then it is evaluated at compile time.
The only code, which goes into the compiled .beam files are functions for modules. So runtime code must be code of a function. Therefore @NobbZ’s rule of thumb works quite well.
It get’s tricky for macros, as when using macros you cannot be sure if the AST input to the macro and the AST generated by the macro is actually used at runtime or not without looking at both the macro definition and the place it’s used in.
Within defmacro
only places wrapped by quote do
are the ones in question. Everything else is run at compile time.
But only if that macro gets called… Thats why I said “macros get evaluated at compile time”.
How exactly are calls to System.get_env/2
handled? Are ENV vars read at compile time? Or does this change depending on where exactly the calls to System.get_env/2
get called?
Here’s an example from a recent Phoenix project that uses a simple “shared secret” authentication for some routes:
plug(SecretAuthPlug, shared_secret: Application.get_env(:my_app, :api_key))
The corresponding config reads from an ENV variable:
config :my_app, :api_key, System.get_env("API_SHARED_SECRET")
Because plug
is a macro, that gets evaluated at compile time… but does that mean that it evaluates all the way down through the config down to the ENV value at compilation time? Or does it store more of a reference like {MyApp.MyModule, :function, [1, "foo"]}
?
System.get_env
is a function so it gets called when it’s called.
If this is compiletime or runtime depends on the context.
Thanks, but that doesn’t entirely answer my question. How “deep” does the evaluation go when a function is called? In my case, when the plug
macro references Application.get_env
which in turn references System.get_env
, does that mean that the plug gets passed the ENV that was present at compile time?
The config is evaluated at mix
time. If you don’t deal with releases, you can consider it as “boottime” of your application, if you run releases, it’s compiletime. With the notable exception of config/release.exs
which is only evaluated at boottime of the release.
Though as plug is a macro, it takes its arguments as AST.
The macro decides when those are evaluated.
A good macro does contain this in the documentation.
If it’s not noted in the documentation, I’d expect everything not in a do
/end
to be compiletime evaluated.