Why does the compilation of modules also execute code?

Hi all, maybe a stupid question here. I find it strange that during the compilation of modules expressions in the modules get executed as well. Example:

defmodule Human do
  IO.puts __MODULE__
end
iex> c "human.exs"
Elixir.Human
[Human]

I thought the compilation c "human.exs" will just compile the source code down to bytecode. I actually expect the bytecode to be executed only when for example some other modules call require Human

Any comments or corrections about this thinking?

My guess: because of macros. You could, for example, read a text file and generate functions based on the contents of that file. If you’d like to execute code when it’s required, you can use the __using__/1 macro.

Compiling Elixir code and executing elixir code are one and the same. I recommend the article by Xavier Noria on the matter: https://medium.com/@fxn/how-does-elixir-compile-execute-code-c1b36c9ec8cf

2 Likes

The article describes how Elixir compile/executes. Yes, it loads the “binary into the Erlang VM using the Erlang code server.” and “Call elixir_compiler_X.FILE/1”.

I would have expect that the Human module in my example to be loaded into the VM and when elixir_compiler_X.__FILE__/1 is called nothing would have happened because no source code is invoking it.

Well, you are in fact invoking IO.puts/2 in your module :slight_smile:

Major parts of Elixir are implemented as macros and so is def/2.
So what happens when a module file like this is compiled is that everything inside defmodule/2 (which is also a macro!) is executed.
This means, if you only have def/2 and defp/2 statements, you won’t notice them being run because they in turn don’t execute the code blocks passed to them. But if you put other code in the module, that will be executed.

3 Likes

defmodule is a macro. Macros are executed at compile time.

2 Likes

Hmm, that kindda make sense. Since def is also a macro. If the design of the language was to compile and then not execute the bytecode, then functions will not be evaluated and loaded into memory.

Yes … mostly. Macros are “expanded” rather than compiled. Their return value is AST that the compiler uses in its compilation steps. When macros contain side effects like IO.puts that is executed when the macro is expanded during an early phase of the overall compile.

Module definition is nested execution, that is not covered in the post.