In order to try out Elixir for the first time, you can use the built-in **interactive Elixir shell **(IEx) through the terminal. This allows you to try simple commands before writing a full application.
$ iex
This is what you should see when you run the "iex"-command.
Basic data types
Elixir supports the basic data types that other programming languages have. You can use integers (whole numbers), floats (decimal numbers), booleans (true
or false
) and **strings **(which are wrapped in double quotes, are UTF-8 encoded, and support line breaks).
Go ahead and try out the following commands in your IEx shell:
iex(1)> 5
5
iex(2)> 4.32
4.32
iex(3)> true
true
iex(4)> "Hello World!"
"Hello World!"
iex(5)> "Hello
...(5)> World!"
"Hello\nWorld!"
Note that in the last example, we use Shift + Enter to run a command that spans over multiple lines.
You can also use variables and basic **operators **to compare values or concatenate strings:
iex(1)> my_number = 6
6
iex(2)> my_number
6
iex(3)> my_number >= 7
false
iex(4)> my_number == 6
true
iex(5)> my_name = "Tristan"
"Tristan"
iex(6)> "Hello " <> my_name
"Hello Tristan"
iex(7)> "Hello #{my_name}"
"Hello Tristan"
Atoms
Atoms are a special kind of data type that resemble constants (immutable variables), but where their only value is the name given to them. They are similar to symbols in the Ruby programming language.
iex(1)> :my_atom
:my_atom
iex(2)> :my_atom == "my_atom"
false
iex(3)> :my_atom == true
false
iex(4)> :my_atom == :my_atom
true
iex(5)> :my_other_atom = 4
** (MatchError) no match of right hand side value: 4
In the last example we see that we cannot assign a certain value to an atom. Again, their only value is their name!
At this point you might wonder what the point of having atoms even is. One frequent use case is when an Elixir program has to return the status of a request, and you get either an :error
(the request failed) or an:ok
(the request was successful).
Lists
Lists in Elixir can contain any data type, and be as long as you want. You can concatenate two lists with ++
, or subtract a list from another with --
.
iex(1)> [5, "Elixir", :ok]
[5, "Elixir", :ok]
iex(2)> [1, 2] ++ [3]
[1, 2, 3]
iex(3)> [1, 2, 3] -- [2]
[1, 3]
It's worth noting that Elixir is an immutable language, so when you perform these operations, the original list is never modified. Instead, a new list is returned, and it's up to you to store it in a variable:
iex(1)> my_list = [1, 2, 3]
[1, 2, 3]
iex(2)> my_list ++ [4]
[1, 2, 3, 4]
iex(3)> my_list
[1, 2, 3]
Lists in Elixir can also be broken up into two parts: the head (which can be extracted with the hd
-function) and the tail (which can be extracted with the tl
-function). The head is the list's first element, and the tail is the rest:
iex(1)> hd [5, "Banana", :ok]
5
iex(2)> tl [5, "Banana", :ok]
["Banana", :ok]
Tuples
Tuples are similar to lists, but they store their elements contiguously in memory. This makes accessing tuple elements faster than list elements, however, modifying them is slower. You can see a tuple as an aggregation of values to form some kind of resource, whereas lists are used to enumerate things.
iex(7)> {"Tristan", 24}
{"Tristan", 24}
You'll notice that many Elixir functions return tuples to differentiate between successes and failures, e.g: {:ok, value}
or {:error, error_message}
Maps
Maps are Elixir's key-value store. The keys can be of any type and they point to a value using =>
.
iex(1)> company_phones = %{:google => "pixel", :apple => "iphone"}
%{apple: "iphone", google: "pixel"}
iex(2)> company_phones.apple
"iphone"
Most of the time, you'll probably want the keys in your map to be atoms, like in the example above. When that's the case, there's a nice shorthand syntax where we can use a colon instead of "=>
" to point to the values, and omit the :
-character at the beginning of our atoms:
iex(3)> company_phones = %{google: "pixel", apple: "iphone"}
%{apple: "iphone", google: "pixel"}
Structs
A struct is very similar to a map, but it comes with some extra functionality -- you can limit the the number of keys and give them default values.
In order to try structs out, we need to create an Elixir-file, so go ahead and create a new file called user.ex
. In it, we will define a new module where we also define our struct:
# user.ex
defmodule User do
defstruct name: "Tristan", age: 24
end
Now go back to the IEx shell and run the command c("user.ex")
. You will notice that Elixir creates a new file called Elixir.User.beam
next to your user.ex
-file, and loads the module into IEx! You can now create a new User
-struct by typing %User{}
, and you'll see that it automatically gets all the default values:
Conditionals
The basic if
and else
-statements are similar to Ruby and work just like you'd expect. Note that we need to use end
to specify where the if
-statements ends.
iex(4)> if 5 > 4 do
...(4)> "It's higher!"
...(4)> else
...(4)> "It's lower"
...(4)> end
"It's higher!"
You can even use the inverse unless
to make your code more readable:
iex(6)> unless 5 > 6 do
...(6)> "The laws of mathematics still work!"
...(6)> end
"The laws of mathematics still work!"
What about "else if"?
In Elixir, there's no "else if". If you have a flow that has more than just two outcomes, you'll probably want to use cond
instead:
iex(5)> cond do
...(5)> 5 > 6 ->
...(5)> "This isn't true"
...(5)> 5 == 8 ->
...(5)> "Neither is this"
...(5)> true ->
...(5)> "But this is!"
...(5)> end
"But this is!"
Finally, there's also a case
-statement that handles control flow in Elixir, but we'll learn more about that in the next chapter...