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.
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.
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.
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
false. Because erlang atoms are just words that starts with lowercase letters this representation suits well.
1> 1 == 1.
But on elixir expands the usage of true and false to be used also without the colon (
iex(1)> true == :true
iex(2)> false == :false
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.
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)> RandomName == :'Elixir.RandomName'
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
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.