Elixir atoms are everywhere

In elixir-lang we have a primitive called atom. Atoms are nothing more than a constant string that behaves very differently compared to strings. When we declare an atom literal in our code, the Erlang VM checks if it is already in memory and reuses it. On the other side when a code with strings are evaluated it allocates a new string in memory to be passed on. It works just like ruby's symbols.

To show how it really works let's write some code and see how it performs using a tool called observer. On elixir terminal we can just fire :observer.start() and go to the tab called memory allocation. So for some fun we can write a simple comprehension code that creates a really huge list full of string with the same value.

iex(1)> for _ <- 1..1_000_000, do: "string"  

After executing this code we can see that we created a huge list with a million strings with the value string and we can notice the the memory of our erlang code just grew a lot. On my machine I just allocated about 225 mb.

string_allocation

If we run the code again but with atoms instead of strings we can see that we have a very different reality.

iex(1)> for _ <- 1..1_000_000, do: :string  

Just 60 mb was allocated. It happens because we still had to allocate the list that was generated during the comprehension command.

atom allocation

The main difference is that with strings we really allocated one million strings and we put them into a list. And with atoms we just allocated the value once and we put the reference of this value a million times into the list. Which is a sweet difference.

Atoms Limitations

Using atoms to reuse memory is nice, but there's a limitation. The Erlang vm stores the Atom inside a table internally and has a default limit of 1048576 atoms per instance. This value can be configured, but in the end it is not an optimization that we want to make.

Other cool uses

Elixir atoms are not meant to use as constant. In fact they are used to name somethings on the language, such as

1. Boolean Values

Erlang there is no boolean values like other languages. But to represent boolean values it utilizes two special atoms that are true and false. Because erlang atoms are just words that starts with lowercase letters this representation suits well.

1> 1 == 1.  
true  

But on elixir expands the usage of true and false to be used also without the colon (:).

iex(1)> true == :true  
true  
iex(2)> false == :false  
true  
iex(3)> is_atom(:false)  
true  
2. Module Names

Module names are also a representation of an atom. just to see the way we call erlang code on elixir. For example using the erlang math module to calculate the square root.

iex(1)> :math.sqrt(16)  
4.0  

We always call our functions by using atoms to to name the modules. We can see that on elixir by calling the funciton is_atom/1 on any elixir module such as GenServer. Notice also that on elixir if a token starts with upcase it is translated directly to atom, so it's possible to declare an atom with upcase value without using colons.

iex(1)> is_atom(GenServer)  
true  
iex(2)> is_atom(RandomName)  
true  
iex(1)> RandomName == :'Elixir.RandomName'  
true  

Now that we know that module names are just atoms, we can define modules with a lowercase name just by using an atom with a colon.

defmodule :hello do  
  def say_to(name \\ "World") do
    "Hello #{name}"
  end
end  
iex(1)> :hello.say_to("John")  
"Hello John"

Conclusion

Atoms uses very little memory and are unique across all the erlang node, using it to name modules and defining booleans values are a really smart approach. But they have very limited usage, it's not recommended to use atoms on the go for everything but for values that defines codes they are useful.