Introduction to Elixir

Functional Programming

Immutable Data

iex(1)> list = [1,2,3,4]
[1, 2, 3, 4]
iex(2)> List.delete(list, 2)
[1, 3, 4]
iex(3)> list
[1, 2, 3, 4]
iex(4)> list = List.delete(list, 2)
[1, 3, 4]
iex(5)> list
[1, 3, 4]

So, no Side-Effects, and no such a thing like...

Functions Everywhere

Based on Erlang

Actor Model

Handles High Concurrency

Ready to Fault-tolerance

Hot code swap

Ready to easy distribution

OTP

And how does it look like?

Basic Types

Numbers

10
1_000
-10
10.5
...

Booleans

true
false

Atoms

:one
:two
:hello

Strings

iex> x = "hello"

iex> x <> " world"
"hello world"

iex> "#{x} world"
"hello world"

Binaries

iex> <<0, 1, 2, 3>>
<<0, 1, 2, 3>>

iex(1)> "hello" <> <<1>>
<<104, 101, 108, 108, 111, 1>>
iex(2)>

iex(1)> <<104, 101, 108, 108, x>> = "hello"  
"hello"
iex(2)> x
111

Anonymous functions

iex> add = fn a, b -> a + b end
#Function<12.71889879/2 in :erl_eval.expr/5>
iex> add.(2, 2)
4

Collections

Lists

iex> [1, 2, true, 3]
[1, 2, true, 3]

Tuples

iex> {:ok, "hello"}
{:ok, "hello"}

Key-value lists/Dictionaries

Keymaps

iex> list = [{:a, 1}, {:b, 2}]
[a: 1, b: 2]
iex> list == [a: 1, b: 2]
true

Giving an example of use...

query = from w in Weather,
      where: w.prcp > 0,
      where: w.temp < 20,
      select: w

Maps

iex> map = %{:a => 1, 2 => :b}
%{2 => :b, :a => 1}
iex> map[:a]
1

Structs

Structs

iex(2)> defmodule Person do
...(2)>   defstruct [:name, :age]
...(2)> end
{:module, Person,
 <<70, 79, 82, 49, 0, 0, 8, 248, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 186,
   131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115,
   95, 118, 49, 108, 0, 0, 0, 4, 104, 2, ...>>, %Person{age: nil, name: nil}}

iex(3)> no_one = %Person{}
%Person{age: nil, name: nil}
iex(4)> me = %Person{name: "Kleber", age: 26} 
%Person{age: 26, name: "Kleber"}
iex(5)>

Pattern Matching

Pattern Matching

iex(6)> a = 1
1
iex(7)> 1 = a
1
iex(8)> 2 = a
** (MatchError) no match of right hand side value: 1

iex(9)> {1, a} = {2, 3}
** (MatchError) no match of right hand side value: {2, 3}
iex(9)> {1, a} = {1, 3}
{1, 3}
iex(10)> a
3

iex(11)> "helllo" <> <<1>>
<<104, 101, 108, 108, 108, 111, 1>>
iex(12)> <<104, 101, 108, 108, x>> = "hello"
"hello"
iex(13)> x
111

Using in function arguments

defmodule Person do
  defstruct [:name, :blood_type]
end

defmodule BloodTypeDonation do
  def receive_donation(%Person{blood_type: "a"}) do
    # do things specific for the blood type A
  end

  def receive_donation(%Person{blood_type: "b"}) do
    # do things specific for the blood type B
  end
end

Processes

Spawning Processes

iex(6)> spawn fn -> IO.inspect(self()) end
#PID<0.96.0>
#PID<0.96.0>

iex(7)> IO.inspect(self())
#PID<0.83.0>
#PID<0.83.0>

Linking Processes

Linking Processes

iex(3)> spawn fn -> raise "Godzilla attacked and you were not able to notify anyone, you died in vein" end
#PID<0.109.0>
09:54:15.386 [error] Process #PID<0.109.0> raised an exception
** (RuntimeError) Godzilla attacked and you were not able to notify anyone, you died in vein
    :erlang.apply/2
iex(4)> spawn_link fn -> raise "Godzilla attacked, but you notified someone above you, you're now a hero =](but a dead one =[)" end                                      



09:55:36.756 [error] Process #PID<0.111.0> raised an exception
** (RuntimeError) Godzilla attacked, but you notified someone above you, you're now a hero =](but a dead one =[)
    :erlang.apply/2
** (EXIT from #PID<0.105.0>) an exception was raised:
    ** (RuntimeError) Godzilla attacked, but you notified someone above you, you're now a hero =](but a dead one =[)
        :erlang.apply/2

OTP

Supervisors

Supervisors

defmodule MySupervisor do
  use Supervisor

  def start_link do
    Supervisor.start_link(__MODULE__, :ok)
  end

  def init(:ok) do
    children = [
      worker(MySupervisionedModule, [])
    ]

    supervise(children, strategy: :one_for_one)
  end
end

defmodule MySupervisionedModule do
  def start_link do
    # Do something ...
  end
end

GenServers

GenServers

defmodule SumServer do
  use GenServer

  def handle_call(:get, _caller_pid, number) do
    {:reply, number, number/2}
  end

  def handle_cast({:sum, number_to_sum}, current_value) do
    new_value = current_value + number_to_sum
    {:noreply, new_value}
  end
end

iex(29)> {:ok, pid} = GenServer.start_link(SumServer, 0)
{:ok, #PID<0.272.0>}
iex(30)> GenServer.call(pid, :get)                      
0
iex(31)> GenServer.cast(pid, {:sum, 10})                
:ok
iex(32)> GenServer.call(pid, :get)      
10.0
iex(33)> GenServer.call(pid, :get)      
5.0

And some Elixir abstractions over OTP

Agents

Agents

iex(47)> {:ok, agent} = Agent.start_link fn -> 10 end
{:ok, #PID<0.298.0>}
iex(48)> Agent.update(agent, fn current -> current + 10 end)
:ok
iex(49)> Agent.get(agent, fn current -> current end)
20

Tasks

Tasks

iex(7)> counter = fn() -> for n <- 1..5, do: IO.puts n end
#Function<20.52032458/0 in :erl_eval.expr/5>
iex(8)> Task.async counter                                 
1
2
3
4
5
%Task{owner: #PID<0.108.0>, pid: #PID<0.120.0>, ref: #Reference<0.0.3.1006>}

iex(7)> task = Task.async(fn() -> Enum.reduce(1..10, fn(x,y) -> x + y end) end)
%Task{owner: #PID<0.87.0>, pid: #PID<0.101.0>, ref: #Reference<0.0.1.523>}
iex(8)> IO.puts Task.await(task)                                               
55
:ok

Tasks

defmodule Talker do
  def talk_alone() do
    IO.puts "hello world!"
    :timer.sleep 500
    IO.puts "I SAID: HELLO....WORLD!"
    :timer.sleep 5
    IO.puts "OK, I GIVE UP"
  end
end

iex(11)> Task.async &Talker.talk_alone/0
hello world!
%Task{owner: #PID<0.87.0>, pid: #PID<0.126.0>, ref: #Reference<0.0.1.973>}
I SAID: HELLO....WORLD!
OK, I GIVE UP

introduction-elixir

By Kleber Nascimento Gueriero