Learn Phoenix LiveView is the comprehensive tutorial that teaches you everything you need to build a complex, realistic, fully-featured web app with Phoenix LiveView. Click here to learn more!
In the previous extracts we covered the three most important differences between Ruby and Elixir. Then we had an intro to Elixir syntax, including a look at Elixir’s most basic datatypes, like strings, integers and floats.
In this and the coming extracts, we’ll look at some more of Elixir’s basic types. Let’s start with something that should be easy for a Ruby developer to understand: atoms.
Elixir atoms are essentially the same as Ruby symbols. They’re textual values written with a colon, :like_this
. They can also be written with double or single quotes, :"like this"
or :'like this'
.
In Ruby, strings are mutable while symbols are immutable. In Elixir this distinction is irrelevant, because - as we’ve seen - everything is immutable.
Elixir atoms and Ruby symbols serve a similar role - they represent constant values and names. We use them for things like keys in maps (maps are key-value data structures similar to Ruby hashes):
user = %{name: "Jimi", age: 27}
Within quotes, an atom can contain any character. Without quotes, atoms can only contain Unicode characters like letters, numbers, underscores, and @
.
That last point is a minor difference from Ruby - Ruby symbols can’t contain @
unless they’re written with quotes.
# Elixir
iex> :george@arrowsmith
:george@arrowsmith
# Ruby
irb> :george@arrowsmith
# syntax error
irb> :"george@arrowsmith"
:"george@arrowsmith"
I mentioned true
, false
and nil
in the last extract, but I skipped an interesting detail - these values are really just atoms! That is, true
, false
and nil
are nothing more than syntactic sugar for the atoms :true
, :false
, and :nil
.
iex> :true
true
iex> :false
false
iex> :nil
nil
This is totally different from Ruby, where true
, false
and nil
are singleton instances of TrueClass
, FalseClass
and NilClass
respectively, and have no inherent connection with symbols.
It also means that :false
and :nil
are considered ‘falsey’ in if
conditions etc.:
iex> if :false || :nil do
...> IO.puts("Are either of them true?")
...> else
...> IO.puts("Nope, both are false!")
...> end
# Nope, both are false!
Module names are atoms too. When you create a new module called, say, MyApp.User
:
defmodule MyApp.User do
# module content …
end
… you’re implicitly creating a new atom called :"Elixir.MyApp.User"
:
iex> :"Elixir.MyApp.User" == MyApp.User
true
An Elixir module’s name always implicitly starts with Elixir
, although you don’t necessarily have to write it:
iex> Elixir.Integer == Integer
true
iex> :"Elixir.Integer" == Integer
true
# When written like an atom, you have to include the "Elixir"
iex> :Integer == Integer
false
Since Elixir runs on the BEAM (the Erlang VM), we can also access modules from the Erlang STDLIB, whose names are lowercase atoms. We’ll see more of this in a later lesson; for now, here’s an example of using functions from Erlang’s math
module:
iex> :math.pi
3.141592653589793
iex> :math.sqrt(2)
1.4142135623730951
iex> :math.log10(10)
2.0
An important difference between Elixir and Ruby is that in Elixir, atoms are never garbage collected. Once an atom has been created, it will take up memory for as long as your program stays running. In Ruby, symbols are garbage collected (although this has only been true since Ruby 2.2.)
This means that you should never write Elixir code such that atoms can be created from user input. Otherwise, a malicious user could cause your app to crash by creating atoms until the program runs out of memory.