Elixir is a dynamic, functional language designed for building scalable and maintainable applications
Lightweight threads of execution
Isolated
Exchange information via messages
parent = self()
# Spawns an Elixir process (not an operating system one!)
spawn(fn ->
send parent, {:msg, "hello world"}
end)
# Block until the message is received
receive do
{:msg, contents} -> IO.puts contents
end
spawn/1 takes a function which it will execute in another process.
Returns a PID (process identifier)
We can retrieve the PID of the current process by calling self/0
Process.alive?/1 can check if a process exists and is alive
iex> pid = spawn fn -> 1 + 2 end
#PID<0.43.0>
iex> Process.alive?(pid) # process is dead by now
false
iex> self()
#PID<0.41.0>
iex> Process.alive?(self())
true
We can send messages to a process with send/2 and receive them with receive/1
The process can be anyone, including the process itself
Send message to self
iex> send self(), {:hello, "world"}
{:hello, "world"}
iex> receive do
...> {:hello, msg} -> msg
...> {:world, msg} -> "won't match"
...> end
Each process has a mailbox to receive messages
Messages in mailbox are ordered by arrival time
If there are no messages in the mailbox, or if none of them matches any of the patterns, the current process will wait until a matching message arrives
receive/1 can specify a timeout if needed
Specify a timeout when receiving messages
receive do
{:hello, msg} -> msg
after
1000 -> "nothing happened"
end
Send and receive messages between processes
iex> parent = self()
#PID<0.41.0>
iex> spawn fn -> send(parent, {:hello, self()}) end
#PID<0.48.0>
iex> receive do
...> {:hello, pid} -> "Got hello from #{inspect pid}"
...> end
"Got hello from #PID<0.48.0>"
spawn version
defmodule Server do
def start do
pid = spawn(Server, :loop, [0])
:global.register_name(:counter, pid)
end
def loop(counter) do
receive do
{:next, sender} -> send sender, {:ok, counter}
loop(counter + 1)
after
2000 -> IO.puts "Just here, waiting..."
loop(counter)
end
end
end
defmodule Client do
def start do
counter_pid = :global.whereis_name(:counter)
loop(counter_pid)
end
def loop(counter_pid) do
send counter_pid, {:next, self()}
receive do
{:ok, counter} -> IO.puts "Got #{counter}"
after
1000 -> "No answer :("
end
loop(counter_pid)
end
end
$ iex
Erlang/OTP 18 [erts-7.2.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Interactive Elixir (1.2.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> c "server.ex"
[Server]
iex(2)> c "client.ex"
[Client]
iex(3)> Server.start
:yes
Just here, waiting...
Just here, waiting...
iex(4)> Client.start
Got 0
Got 1
Got 2
$ iex --sname one
Erlang/OTP 18 [erts-7.2.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Interactive Elixir (1.2.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(one@arwen)1>
$ iex --sname two
Erlang/OTP 18 [erts-7.2.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Interactive Elixir (1.2.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(two@arwen)1> Node.connect :"one@arwen"
true
iex(two@arwen)2> Node.list
[:one@arwen]
iex(two@arwen)3>
iex(one@arwen)1> c "server.ex"
[Server]
iex(one@arwen)2>
iex(two@arwen)3> c "client.ex"
[Client]
iex(two@arwen)4>
iex(one@arwen)2> Server.start
:yes
Just here, waiting...
Just here, waiting...
Just here, waiting...
iex(two@arwen)4> Client.start
Got 0
Got 1
Got 2
Got 3
# Determine your server machine ip with ipconfig (Win) or ifconfig (Mac, GNU/Linux)
$ iex --name server@192.168.0.1 --cookie elixir
Erlang/OTP 18 [erts-7.2.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Interactive Elixir (1.2.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(server@192.168.0.1)1>
# Determine your client machine ip with ipconfig (Win) or ifconfig (Mac, GNU/Linux)
$ iex --name client@192.168.0.2 --cookie elixir
Erlang/OTP 18 [erts-7.2.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Interactive Elixir (1.2.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(client@192.168.0.2)1> Node.ping :"server@192.168.0.1"
:pong
iex(client@192.168.0.2)1> Node.connect :"server@192.168.0.1"
true
iex(client@192.168.0.2)2> Node.list
[:server@192.168.0.1]
iex(client@192.168.0.2)3>
iex(server@192.168.0.1)1> c "server.ex"
[Server]
iex(server@192.168.0.1)2>
iex(client@192.168.0.2)3> c "client.ex"
[Client]
iex(client@192.168.0.2)4>
iex(server@192.168.0.1)2> Server.start
:yes
Just here, waiting...
Just here, waiting...
Just here, waiting...
iex(client@192.168.0.2)4> Client.start
Got 0
Got 1
Got 2
Got 3
It is a behaviour module for implementing the server part of a client-server relation
It is a process like any other Elixir process
It can be used to keep state, execute code asynchronously
It has a standard set of interface functions
It includes functionality for tracing and error reporting
defmodule Stack do
use GenServer
# Callbacks
def handle_call(:pop, _from, [h|t]) do
{:reply, h, t}
end
def handle_cast({:push, item}, state) do
{:noreply, [item|state]}
end
end
# Start the server
{:ok, pid} = GenServer.start_link(Stack, [:hello])
# This is the client
GenServer.call(pid, :pop)
#=> :hello
GenServer.cast(pid, {:push, :world})
#=> :ok
GenServer.call(pid, :pop)
#=> :world
GemServer version
defmodule GenServer.Server do
use GenServer
def start do
GenServer.start_link(GenServer.Server, 0, name: {:global, :counter})
end
def next do
GenServer.call({:global, :counter}, :next)
end
# Implementation
def handle_call(:next, _from, counter) do
{:reply, counter, counter + 1}
end
end
defmodule GenServer.Client do
def start do
loop()
end
def loop do
counter = GenServer.Server.next()
IO.puts "Got #{counter}"
loop()
end
end
$ iex
Erlang/OTP 18 [erts-7.2.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Interactive Elixir (1.2.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> c "server.ex"
[GenServer.Server]
iex(2)> c "client.ex"
[GenServer.Client]
iex(3)> GenServer.Server.start
{:ok, #PID<0.98.0>}
iex(4)> GenServer.Client.start
Got 0
Got 1
Got 2
Got 3
$ iex --sname one
Erlang/OTP 18 [erts-7.2.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Interactive Elixir (1.2.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(one@arwen)1> c "server.ex"
[GenServer.Server]
$ iex --sname two
Erlang/OTP 18 [erts-7.2.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Interactive Elixir (1.2.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(one@arwen)1> c "server.ex"
[GenServer.Server]
iex(one@arwen)2> c "client.ex"
[GenServer.Client]
iex(two@arwen)3> Node.connect :"one@arwen"
true
iex(two@arwen)4> Node.list
[:one@arwen]
iex(one@arwen)2> GenServer.Server.start
{:ok, #PID<0.96.0>}
iex(one@arwen)3>
iex(two@arwen)5> GenServer.Client.start
Got 0
Got 1
Got 2
Got 3
# Determine your server machine ip with ipconfig (Win) or ifconfig (Mac, GNU/Linux)
$ iex --name server@192.168.0.1 --cookie elixir
Erlang/OTP 18 [erts-7.2.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Interactive Elixir (1.2.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(server@192.168.0.1)1>
# Determine your client machine ip with ipconfig (Win) or ifconfig (Mac, GNU/Linux)
$ iex --name client@192.168.0.2 --cookie elixir
Erlang/OTP 18 [erts-7.2.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Interactive Elixir (1.2.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(client@192.168.0.2)1> Node.connect :"server@192.168.0.1"
true
iex(client@192.168.0.2)2> Node.list
[:server@192.168.0.1]
iex(client@192.168.0.2)3>
iex(server@192.168.0.1)2> c "server.ex"
[GenServer.Server]
iex(server@192.168.0.1)3>
iex(server@192.168.0.1)4> c "server.ex"
[GenServer.Server]
iex(client@192.168.0.2)5> c "client.ex"
[GenServer.Client]
iex(client@192.168.0.2)6>
iex(server@192.168.0.1)7> GenServer.Server.start
{:ok, #PID<0.96.0>}
iex(server@192.168.0.1)8>
iex(client@192.168.0.2)7> GenServer.Client.start
Got 0
Got 1
Got 2
Got 3
Only required to implement the callbacks and functionality we are interested in
call messages expect a reply from the server (and are therefore synchronous)
cast messages do not
http://miguelcoba.github.io/elixir-concurrency
Miguel Cobá