Sponsor us and add your link here. Help this open-source site — with open traffic stats — stay alive.

Octallium - Functional with Elx


Octallium - Functional with Elx

Match Operator and Pattern Matching

Elixir uses the = sign not for assignments but for matching both sides (left and right) into the same value.

It’s called the match operator

a = 1
1 = a

You can also match by patterns, it’s called pattern matching

[a, a] = [1, 1]

The following code however will not work because the match operator matches the value 1into d, and then uses it to match against 2, which causes a match error between the right and left side

d = 1
2 = d

The same thing applies when matching against incompatible patterns, this will try to both math the values 1 and 2into the same variable c, which will cause a MatchError, and the value of c does not change because all variables in Elixir are immutable by default.

# this will not work properly
try do
  [c, c] = [1, 2]
  e -> IO.inspect(e)

However if we try to match an variable twice, we actually can change the value of it:

immutable_variable = 1
immutable_variable = 20

This happens because Elixir thinks that we want to bound the new value to the variable on the left, for this to not happen, we can use the pin (^) operator:

# which is equivalent to "10 = immutable_variable"
try do
  ^immutable_variable = 10
  e -> IO.inspect(e)

This immutability allows for applications to easily scale operations on these values, since we can copy easily this value across different threads/cores of an actual computer

Actor Model

An actor receives a message and can process it and send message to other actors or emit a response. It’s considered an isolated unit that gets something, process it and gives back.

On the Erlang VM, the run inside processes which in turn exchanges immutable data.

And the Erlang VM can be run on a cluster of machines, which means that we can scale horizontally across the globe if we want.

These processes are not OS processes, they are more similar to virtual threads.

  1. Actors run in processes which are isolated from each other
  2. Each process is identified by a PID (Process Identifier)
  3. Allows inter-communication of processes by message passing
  4. Since data is immutable, we do not have to worry about changing state between actors, since all computation runs using copies of data
  5. Each process has it’s Stack & Heap allocation, meaning each process has it’s own GC and exceptions does not halt the entire program (only a process)

How processes works

  1. Receives messages in mailbox
  2. Executed FIFO
  3. Very cheap to create (less than 3kb memory)
  4. Always communicate by message passing

When using iex, we have an interactive repl which itself is a process

We can check the PID of the running process by using the builtin function self/0



Elixir have two ways to run code in projects, interpreted (frequently used for scripts) and compiled.

interpreted will pass line by line and execute it instantly on the Erlang VM. compiled will convert the code into bytecode (.beam file), which in turn can be executed directly on the Erlang VM.

The main difference is the speed, when running in interpreted mode, Elixir have to do two steps and thus it’s slower, however if you run directly the bytecode, this will be much faster.

Also, scripting is frequently used when we don’t want that code to be compiled together with the application when deploying, such as database migrations, testing etc…

To run in interpreted mode, name your files with .exs extension. For compiling, name with .ex extension.

Functions in Elixir are identified by arity (number of arguments) also, so we can have two functions with the same name but with different arity.

Data Types - Atoms

Represents an symbol for something, it’s a variable which the name of the variable is the same as it’s value.

// in javascript
const nike = "nike"
# instead of writing
nike = "nike"

# we can use:
# here are some valid synthax for atoms
at = :an_atom
at2 = :"Also an atom but with spaces!"


They are very useful for pattern matching, it’s common to use atoms such as :ok and :error such as below:

defmodule SomeLib do
  @moduledoc """
    iex> SomeLib.divide(5, 2)
    {:ok, 2.5}

    iex> SomeLib.divide(10, 0)
    {:error, "cannot divide by 0"}
  def divide(dividend, divisor) when is_integer(dividend) and is_integer(divisor) do
    if divisor == 0 do
      {:error, "cannot divide by 0"}
      {:ok, dividend / divisor}
{:ok, result} = SomeLib.divide(10, 2)

{:error, reason} = SomeLib.divide(5, 0)


Data Types - String

If we use double quotes ("), that is considered a String (or binary string) by Elixir such as above:

IEx.Info.info("Hi there")

However, single quotes (') produces a character list:

IEx.Info.info(~c"Hi again")

We can pattern match on Strings:

"H" <> rest = "Hi there"
# Elixir again tries to match LHS with RHS, thus bounding "i there" to `rest`


The raw representation of Strings are binary values, inside double angle brackets:

an_string = <<72, 105>> <> " mom"

<> = an_string

<> = an_string

# is head equals to code point of "H"
head == ?H

Data Types - Charlist

chars = ~c"World"
chars |> IEx.Info.info()
~c"Hello " ++ chars

Data Types - Process

my_pid = self()

Data Types - Lists

Are actually considered Linked Lists (which theyselves are recursive in nature)

list = ["a", "b", "c"]
try do
  e -> IO.inspect(e)

# We receive error because this is actually a linked lists, thus index operations does not works
# We can access it through builtin functions of std lib

Enum.at(list, 0)

To show the documentation of the function we can run:

import IEx.Helpers

# in IEx console we can use just "h()"
[f, s, t] = list


# returns the head of list
# returns the tail of list
[h | t] = list

Data Types - Tuples

# similar to lists, but used for static few elements
{a, b} = {1, 2}
{:reply, msg, state} = {:reply, "Abe found", ["Abe", "John", "Mary"]}

Data Types - Keyword List

an actual list but with key and value, where the key are atoms:

data = [a: 1, b: 2]

it’s exactly the same as using tuples inside lists:

[{:a, 1}, {:b, 2}] == data

You can find functions to manage this data type under Keyword standard module of Elixir.

Data Types - Maps

Very similar to keyword lists, but offers more features such as using different data types for keys:

my_map = %{a: 1, b: 2, c: 3}
%{a: first, b: second, c: _} = my_map
%{c: third} = my_map
# we can use other data types as key
map2 = %{"first" => 1, "second" => 10}
# to extract elements from a map based on key
%{"second" => second_elem} = map2
Map.get(map2, "first")
map2 = %{map2 | "first" => 2}
my_map = %{my_map | c: 20}

Data Types - Struct

Structs always take the name of the Module it’s in:

defmodule User do
  defstruct username: "", email: "", age: nil
user1 = %User{username: "Abe", age: 20, email: "abe@email.com"}
%{username: username} = user1
user1 = %{user1 | age: 21}


Avoid mutation of data, recursion is a common tool in functional programming for doing repetitive tasks.

Elixir offers recursion easily and has builtin optimization in the compiler to avoid performance issues and high memory usage:

defmodule PrintDigits do
  # Base case
  def upto_asc(0), do: :ok

  def upto_asc(num) when is_integer(num) do
    cond do
      num > 0 -> upto_asc(num - 1)
      # recursive call happens first (Head Recursion)
      num < 0 -> upto_asc(num + 1)


  # Base case
  def upto_desc(0), do: :ok

  def upto_desc(num) when is_integer(num) do

    cond do
      num > 0 -> upto_desc(num - 1)
      # recursive call happens last (Tail recursion)
      num < 0 -> upto_desc(num + 1)
defmodule SumDigits do
  def upto(0), do: 0

  def upto(num) when is_integer(num) do
    upto(num - 1) + num

  def upto_tail_rec(num, acc \\ 0)
  def upto_tail_rec(0, acc), do: acc

  def upto_tail_rec(num, acc) do
    upto_tail_rec(num - 1, acc + num)

By making a recursive function tail recursive, we can achieve much more performance, since the ELixir compiler can Reuse the Stack, instead of creating multiple instances.

defmodule Factorial do
  def call(1), do: 1

  def call(num) do
    num * call(num - 1)

  def call_rec(num, acc \\ 1)
  def call_rec(1, acc), do: acc
  def call_rec(num, acc), do: call_rec(num - 1, acc * num)
defmodule ReverseNum do
  def call(num, acc \\ 0)
  def call(0, acc), do: acc
  def call(num, acc), do: call(div(num, 10), acc * 10 + rem(num, 10))

Now let’s apply the recursion to lists!

defmodule ListsExercises do
  @spec sum(list(number())) :: number()
  def sum([]), do: 0
  def sum([h | t]), do: h + sum(t)

  def double_num(num), do: num * 2

  def sum_rec(nums, acc \\ 0)
  def sum_rec([], acc), do: acc
  def sum_rec([h | t], acc), do: sum_rec(t, acc + h)

  def reverse([]), do: []
  def reverse([h | t]), do: reverse(t) ++ [h]

  @spec reverse_rec([any()], [any()]) :: [any()]
  def reverse_rec(elems, acc \\ [])
  def reverse_rec([], acc), do: acc
  def reverse_rec([h | t], acc), do: reverse_rec(t, [h | acc])

  @spec map_rec([any()], (any() -> any()), [any()]) :: [any()]
  def map_rec(elems, fun, acc \\ [])
  def map_rec([], _, acc), do: acc |> reverse_rec()
  def map_rec([h | t], fun, acc), do: map_rec(t, fun, [fun.(h) | acc])

  @spec concat([any()], [any()]) :: [any()]
  def concat(src, dst), do: concat_func(src |> reverse_rec(), dst)
  defp concat_func([], dst), do: dst
  defp concat_func([h | t], dst), do: concat_func(t, [h | dst])

  def flat_map(elems, fun, acc \\ [])
  def flat_map([], _, acc), do: acc

  def flat_map([h | t], fun, acc) do
    flat_map(t, fun, concat(acc, fun.(h)))
[1, 2, 2, 2, 2, 2, 8] |> ListsExercises.sum()
[1, 2, 2, 2, 2, 2, 8] |> ListsExercises.sum_rec()
["a", "b", "c"] |> ListsExercises.reverse()
["a", "b", "c"] |> ListsExercises.reverse_rec()
ListsExercises.map_rec([1, 2, 3, 4, 5], fn x -> x * x end)
ListsExercises.map_rec([a: 1, b: 2], fn {k, v} -> {k, -v} end)
ListsExercises.map_rec([1, 2, 3, 4, 5, 6], &amp;ListsExercises.double_num/1)
# same as ++ operator
ListsExercises.concat([1, 2, 3], [4, 5, 6])
ListsExercises.flat_map([:a, :b, :c], fn x -> [x, x] end)
defprotocol ToRed do
  @spec call(String.t()) :: String.t()
  def call(value)

defimpl ToRed, for: BitString do
  def call(self), do: self <> " but in red"