Stay in the Loop

Follow along on WhatsApp or Telegram — new posts, reflections, and the occasional spiral.

Elixir Cheatsheet

I’ve been trying to play around with Elixir, I’ve decided to make a little cheatsheet to remember the basics.

Source for documentation: HexaDocs.

In Elixir there is not concept of “assignment”, the = operator is a match operator, not an assignment operator.

iex(1)> hello = :hello
hello
iex(2)> {status, msg} = {:error, "You've dropped the ball!"}
{:error, "You've dropped the ball!"}
iex(3)> status
:error

Check if two things are equal ==:

iex(1)> true == :true  # See booleans - true is attached to the atom :true
true
iex(2)> 2 + 2 == 4
true

The pipe operator |> takes the output of a function and uses it as the first argument of the next function.

iex(1)> add_3 = fn a -> a + 3 end
#Function<42.105768164/1 in :erl_eval.expr/6>
iex(2)> iex(4)> [1, 3, 5] |>
...(2)> Enum.sum |>
...(2)> add_3.()    # Equivalent to: add_3.(Enum.sum([1, 3, 5]))
12

For more documentation, see the HexaDocs.

Whether something is true or false. Under the hood, booleans are just the atoms :true and :false.

Atom are constants. Their name is their value - and they are used similarly to enums in other languages. One of the most common atoms used in elixir is :ok.

Example use of atoms:

tup1 = {:error, "The sky is falling!"}                     # this is a tuple with an atom and a string in it.
tup2 = {:success, "Everything is under control."}
tup3 = {:something_else, "Davie Crocket"}

defmodule HandleMessage do
  def check_message({state, msg}) do               # destructure tuple with pattern matching
    case state do                                  # case based on the state atom
      :error -> "An error occured: '#{msg}' 😭"    # if the state is the :error atom -> do this.
      :success -> "Hurray! '#{msg}' 🎉"            # if the state is the :success atom -> do this.
      _ -> "I don't know what to do with this: '#{msg}' 🤷"    # otherwise -> do this.
    end
  end
end

IO.puts(HandleMessage.check_message tup1)
IO.puts(HandleMessage.check_message tup2)
IO.puts(HandleMessage.check_message tup3)
An error occured: 'The sky is falling!' 😭
Hurray! 'Everything is under control.' 🎉
I don't know what to do with this: 'Davie Crocket' 🤷

Strings in Elixir are inserted between double quotes and are encoded in UTF-8.

iex(1)> "¿Qué pasa?"
"¿Qué pasa?"

Lists in Elixir are linked lists and are set in square brackets. Many functions for lists are available in the Enum module. Lists can be of different types.

iex(1)> my_nums_list = [1, 2, 3, 4]
[1, 2, 3, 4]
iex(2)> my_random_stuff_list = [1, "Cheese", :billboard, 33.4]
[1, "Cheese", :billboard, 33.4]
iex(3)> Enum.map(my_nums_list, fn x -> IO.puts(x) end)
1
2
3
4
[:ok, :ok, :ok, :ok]
iex(4)> Enum.map(my_random_stuff_list, fn x -> IO.puts(x) end)
1
Cheese
billboard
33.4
[:ok, :ok, :ok, :ok]

From the hexdocs:

Tuples are intended as fixed-size containers for multiple elements. To manipulate a collection of elements, use a list instead. Enum functions do not work on tuples.

Keyword lists are lists of two-element tuples where the first element is an atom. They preserve order and allow duplicate keys — the opposite tradeoff from maps. You’ll see them most often as optional arguments at the end of a function call.

iex(1)> opts = [enabled: true, timeout: 30]
[enabled: true, timeout: 30]
iex(2)> opts == [{:enabled, true}, {:timeout, 30}]   # the [key: value] form is just sugar for tuples
true
iex(3)> opts[:timeout]
30

Maps are Elixir’s key-value data structure. Keys can be any type and must be unique — no preserved order, no duplicates.

iex(1)> cheese = %{name: "brie", price: 12.5}
%{name: "brie", price: 12.5}
iex(2)> cheese.name              # dot access only works when the key is an atom
"brie"
iex(3)> %{cheese | price: 14.0}  # update syntax - only works on keys that already exist
%{name: "brie", price: 14.0}
iex(4)> Map.put(cheese, :in_stock, true)   # Map.put/3 can add brand new keys
%{in_stock: true, name: "brie", price: 12.5}

Elixir has named functions, defined with def inside a module, and anonymous functions that get passed around like any other value.

defmodule Greeter do
  def hello(name) do
    "Hello, #{name}!"
  end
end

IO.puts(Greeter.hello("World"))
Hello, World!

Anonymous functions:

iex(1)> square = fn x -> x * x end
#Function<42.105768164/1 in :erl_eval.expr/6>
iex(2)> square.(4)                       # anonymous functions are invoked with a dot
16
iex(3)> Enum.map([1, 2, 3], &(&1 * &1))  # the capture operator is shorthand for a one-line fn
[1, 4, 9]

Integers have arbitrary precision — they grow as large as memory allows, no overflow to worry about. Underscores are allowed as a visual separator in large literals.

iex(1)> 1_000_000
1000000
iex(2)> 2 ** 100      # exponentiation
1267650600228229401496703205376
iex(3)> div(7, 2)     # integer division
3
iex(4)> rem(7, 2)     # remainder
1

A quick aside

Enjoying this post?

Come along on WhatsApp or Telegram — new posts, reflections, and the occasional spiral.

This is an example of a case statement:

tup1 = {:error, "The sky is falling!"}              # this is a tuple with an atom and a string in it.
tup2 = {:success, "Everything is under control."}
tup3 = {:something_else, "Davie Crocket"}

defmodule HandleMessage do
  def check_message({state, msg}) do               # destructure tuple with pattern matching
    case state do                                  # case based on the state atom
      :error -> "An error occured: '#{msg}' 😭"    # if the state is the :error atom -> do this.
      :success -> "Hurray! '#{msg}' 🎉"            # if the state is the :success atom -> do this.
      _ -> "I don't know what to do with this: '#{msg}' 🤷"    # otherwise -> do this.
    end
  end
end

IO.puts(HandleMessage.check_message tup1)
IO.puts(HandleMessage.check_message tup2)
IO.puts(HandleMessage.check_message tup3)
An error occured: 'The sky is falling!' 😭
Hurray! 'Everything is under control.' 🎉
I don't know what to do with this: 'Davie Crocket' 🤷

This can also be accomplished with pattern matching and overloading:

tup1 = {:error, "The sky is falling!"}
tup2 = {:success, "Everything is under control."}
tup3 = {:something_else, "Davie Crocket"}

defmodule HandleMessage2 do
  def check_message({:error, msg}) do
    "An error occured: '#{msg}' 😭"
  end

  def check_message({:success, msg}) do
    "Hurray! '#{msg}' 🎉"
  end


  def check_message({_, msg}) do
    "I don't know what to do with this: '#{msg}' 🤷"
  end
end

IO.puts(HandleMessage2.check_message tup1)
IO.puts(HandleMessage2.check_message tup2)
IO.puts(HandleMessage2.check_message tup3)
An error occured: 'The sky is falling!' 😭
Hurray! 'Everything is under control.' 🎉
I don't know what to do with this: 'Davie Crocket' 🤷

Jacob Shore

Hi, I'm Jacob. I'm a language nerd, developer, and spiritual wanderer.

Related Content