Discover Elixir & Phoenix

Back to All Courses

Lesson 2

Elixir basics

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"}

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!"

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!"

💡 Note: If there's nothing that evaluates to true in your cond-statement, it will raise an exception! Therefore it's usually a good practice to always add a final true-condition at the very end of your statement (like we did in our example above) to handle eventual exceptions.

Finally, there's also a case-statement that handles control flow in Elixir, but we'll learn more about that in the next chapter...