What’s going on here?

As usual, it’s better if you try understanding this by yourself. If you are up for the challenge, just boot up an Erlang VM and try to figure out what’s happening.

I’ll jump straight to the answer instead: Erlang has Dynamic Code Loading. As the docs point out…

The code server loads code according to a code loading strategy, which is either interactive (default) or embedded. In interactive mode, code is searched for in a code path and loaded when first referenced. In embedded mode, code is loaded at start-up according to a boot script. This is described in System Principles.

Alright, mystery solved: The new functions that appear once we evaluate the list comprehension a second time are loaded right there because they’re needed to evaluate the first list comprehension and print its result.

Let’s see if our observations match with that hypothesis. And just for fun, let’s do it in Elixir. First, let’s see if the same behavior is experienced in iex, too:

iex(1)> length(for {m, _} <- :code.all_loaded, {:exports, fs} <- m.module_info(), {f, a} <- fs, do: {m, f, a})

4239

iex(2)> length(for {m, _} <- :code.all_loaded, {:exports, fs} <- m.module_info(), {f, a} <- fs, do: {m, f, a})

4319

iex(3)>

Excellent! Now let’s see what functions are those new ones. Actually, since modules are loaded all at once (i.e. code server doesn’t load individual functions, it loads the entire module when it needs it), let’s see which modules are the new ones. To do that, we need to start a new VM, of course, and then…

iex(1)> ms = :code.all_loaded; length(ms)

172

iex(2)> new_ms = (:code.all_loaded -- ms)

[

{Inspect.Integer, 'path/to/Elixir.Inspect.Integer.beam'},

{Inspect.Algebra, 'path/to/Elixir.Inspect.Algebra.beam'},

{Inspect.Opts, 'path/to/Elixir.Inspect.Opts.beam'},

{Inspect, 'path/to/Elixir.Inspect.beam'}

]

iex(3)>

And there you have it, the new modules are no others than the ones required to print out 172 on the screen.