I’m curious how apply(module, atom, [any]) :: any and spawn(module, atom, list) :: pid arrive at function calls when they are given a module name and a atom representing the function name. I understand how the function arity is calculated by the number of arguments passed but I’m not sure of the mechanics involved in calling the atom which represents the function name. Are the number of arguments passed and function name atom used for a lookup into the module’s functions?

That is actually done via a built-in efficient erlang ‘apply’ VM call, but you could do it yourself by looking up the attributes on the passed in module to get the information about function calls.

1 Like

But this just takes my original question and pushes it down to the Erlang language level without answering it. How does the apply and spawn function go from an atom which can’t be called to a function call? My guess is the functions apply and spawn use the atom and arity(number of arguments) for a lookup into the named module… Am I right?

In Erlang function names are atoms, in Elixir AST they are also atoms

quote do: blah(1, 2, 3)
{:blah, [], [1, 2, 3]}

But that brings up the question. Why does apply and spawn use atoms to represent the function names? Why not use the function names?

Because function names are atoms in BEAM.

BEAM itself uses atoms for module-names, function-names, atom-names ;), variable-names, everything.

Why? because atoms are small and easy to pass around (as long as one sticks to the same node).

1 Like

But that just brings up another question. Why are function names passed as atoms and module names not in apply and spawn? Why the inconsistency?

Modulenames are passed as aliases, which again are just syntactic sugar over atoms.

So you could do apply(:"Elixir.Enum", :into, [[{:a, 0},{:b, 1},{:c, 2}], %{}]) which returns %{a: 0, b: 1, c: 2}.


And yes, the base internal apply uses the function name and arity to access a table of exported functions for the module to find the function definition. If there is no function then it generates an undef error at the Erlang level. In a “normal” apply this just raises the error while in a spawn it kills the process.

Note that this occurs after the process has been created and spawn has returned the new process pid so without a link the process dies silently.


Sorry for the nit-picky questions but details like this keep me up at night. Thanks everyone for the input.