Why do Erlang modules look like atoms in Elixir?
This is a post about how Elixir knows the difference between Erlang modules, Elixir modules, and Elixir atoms
If you have ever come across Elixir code that calls an Erlang module, you will know it looks something like this:
However, normal atoms that are declared in Elixir code don’t get functions, so how does Elixir know the difference between them?
If we open up IEx
and create some atoms, there appears to be no difference between the :os
atom and the :tyler
atom. Yet when we try to call the system_time/0
function on the :os
atom it works, but trying to call any kind of function on the :tyler
atom raises an error because there is no :tyler
module.
So how does Elixir know when we are dealing with an atom and when we are dealing with an Erlang module?
There are a whole bunch of IEx helpers that are useful for exploring these kinds of things, and the one that will help us right now is the i/1 function, which will print information about the data type of the value we pass it.
So lets test that out on the :tyler
atom and the :os
atom. Starting with the :tyler
atom, we can see we get some information about its value:
If we do the same for the :os
atom we get a lot more information, and its easy to see that Elixir knows that we are now dealing with a module and not just a regular atom:
If you compare the Reference modules
fields for each, you can see that :tyler
is just an Atom
while :os
is Module, Atom
. This is not surprising; we already said that :os
is an atom and a module, while :tyler
is just a regular atom.
Lets try the same thing for the String
Elixir module:
Obviously we know that String
is a module, so we are not surprised to see the module-related stuff in there that looks similar to the previous example with :os
. What is interesting here is the Reference modules
still says Module
and Atom
.
If we look back at the screenshot above, we can see that the Raw representation
field event tells exactly how the module name is represented as an atom in Elixir. Lets play around with that:
Neat! This demonstrates that String
and :"Elixir.String"
are equivalent! If you define a module in your code by doing defmodule MyModule
or defmodule Foo.Bar
then you would be able to access it in the same way, :"Elixir.MyModule
and :"Elixir.Foo.Bar"
respectively.
By the way: the reason you need the quotes ""
in :"Elixir.String"
atom is because without them the dot .
would be ambigious. You need to use quotes when creating an atom that has any special characters.
This also shows that Elixir keeps track of Erlang vs Elixir modules by using the Elixir
prefix in the module name (you can scroll back up and see that was not present for :os
). In fact, all Pascal-case “names” Elixir are just syntactic sugar for Elixir
-prefixed atoms.
Wrapping up, the key take aways are that:
- All modules are atoms (
:os
,String
,MyModule
) - Not all atoms are modules (
:tyler
) - Erlang and Elixir modules are differentiated by the
Elixir
prefix that is present forElixir
modules