Daniel Morandini
Software Developer @KIM
philip giuliani, daniel morandini, andrea janes
philip giuliani, developer @ keep in mind
daniel morandini, developer @ keep in mind
andrea janes, researcher @ free university of bozen-bolzano
this is how we will spend our/your time:
conclusion and finish the buffet!
functional vs oop
(introduction to) processes
language highlights
introduction
goals
basically everyone already used it in some way
The WhatsApp architecture Facebook bought for 19 billion
* http://highscalability.com/blog/2014/2/26/the-whatsapp-architecture-facebook-bought-for-19-billion.html
uses erlang to implement its chat service, handling more than 2 billion montly active users
uses erlang to implement simpledb, a database service of the amazon elastic compute cloud
uses erlang in its sms and authentication systems
migrated from ruby to elixir and went from 150 servers to just 5
Ask questions any time!
* https://softwareengineering.stackexchange.com/questions/137716/what-were-the-historical-conditions-that-led-to-object-oriented-programming-beco
object-oriented programming:
* https://medium.com/@cscalfani/goodbye-object-oriented-programming-a59cda4c0e53 and https://stackoverflow.com/questions/1112773/what-are-the-core-concepts-in-functional-programming
functional programming:
* https://medium.com/@cscalfani/goodbye-object-oriented-programming-a59cda4c0e53 and https://stackoverflow.com/questions/1112773/what-are-the-core-concepts-in-functional-programming
one thing they sometimes teach you in programming classes is how to study requirements to extract classes and methods.
* https://www.quora.com/Is-the-object-oriented-programming-OOP-paradigm-overemphasized-or-overrated
60ies
90ies
* http://i.imgur.com/Q0vFcHd.png
* http://i.imgur.com/Q0vFcHd.png
[5] https://stackoverflow.com/questions/2078978/functional-programming-vs-object-oriented-programming
we picked five aspects to describe elixir:
a = 1 # 1
1 = a # 1 - also a valid statement
2 = a # MatchError
{:ok, message} = {:ok, "hello, elixir"}
message # "hello, elixir"
{:status, code} = {:status, 404}
code # 404
{:error, err, _} = {:error, "some error message"} # MatchError -
# tuples with different sizes
{:ok, :hi} = {:ok, "hi"} # MatchError
list = [1, 2, "last"]
[head|tail] = list
head # 1
tail # [2, "last"]
can also be used to prepend items to a list
list = [0|list]
list # [0, 1, 2, "last"]
list = [{:a, 1}, {:b, 2}] # - can also be written [a: 1, b: 2]
[a: x, b: y] = list # - same number of tuples to match
x # 1
y # 2
keyword lists
map = %{:name => "jose", :surname => "valim"} # elixir's creator
%{name: "jose", surname: "valim"} == map # true - convinient way to write
# maps when keys are atoms only
%{"name" => x} = map
x # "jose"
a = 1
a # 1
{a, b} = {"foo", 2}
a # "foo" - ...
how to match something without hardcoding?
using ^
a = 1
a # 1
{^a, b} = {"foo", 2} # MatchError - hurray
{^a, b} = {1, 2} # - ^ does not allow variable rebind
foo(bar(baz(new_function(other_function()))))
instead of writing
other_function()
|> new_function()
|> baz()
|> bar()
|> foo()
you can write
(As you do already in Linux if you call a command like ls | less)
case result do
{:ok, data} -> # do something with data
{:error, err} -> # do something with err
_ -> # whaat?
end
# match and bind `code`
def handle_response(:ok, %{status: code}) do
# match and bind `message`
def handle_response(:error, message) do
# match everything and bind data with a guard
def handle_response(_, data) when is_map(data) do
# match everything and bind data
def handle_response(_, data) do
parameters
case
with {:ok, url} <- build_url(),
{:ok, response} <- fetch_users(url),
{:ok, users} <- parse_response(response) do
do_something(users)
else
# error handling
end
with
there are also "if", "unless" and "cond"
# elixir
def sum([]), do: 0
def sum([h|t]), do: h + sum(t)
// golang
func sum(l []int) int {
result := 0
for _, v := range l {
result += v
}
return result
}
example: summing integers in a list
# result
sum([1, 2, 3, 4]) = 10
# recursion
def sum([]), do: 0
def sum([h|t]), do: h + sum(t)
# tail recursion
def sum(l), do: do_sum(l, 0)
defp do_sum([], acc), do: acc
defp do_sum([h|t], acc), do: do_sum(t, acc + h)
odd? = &(rem(&1, 2) != 0)
1..100_000 |> Enum.map(&(&1 * 3)) |> Enum.filter(odd?) |> Enum.sum # 7500000000
the std library provides also a lot of functions for handling with enumerables (such as list and map)
Stream - lazy evaluation
Enum - for eager usage
1..100_000 |> Stream.map(&(&1 * 3)) |> Stream.filter(odd?) |> Enum.sum # 7500000000
# spawn/1 creates and runs a function into a new process
pid = spawn(f) # #PID<0.145.0>
Process.alive?(pid) # false - f returned already..
self() # #PID<0.84.0> - what is this?
f = fn -> "hello world, from #{inspect self()}" end
let's start with an anonymous function..
..and spawn it into another process!
here = self() # #PID<0.84.0>
spawn(fn -> f.(here) end) # - f.() executes anonymous functions
# flush/0 flushes and prints all the messages in the mailbox
flush() # hello world, from #PID<0.127.0>
# send/2 Sends a message to the given dest and returns the message.
f = fn pid -> send(pid, "hello world, from #{inspect self()}") end
let's modify our function a bit
f = fn pid -> send(pid, {:mess, "hello world, from #{inspect self()}"}) end
let's start again with our function
here = self() # #PID<0.84.0>
spawn(fn -> f.(here) end)
receive do
# pattern matching
{:error, e} -> e
{:mess, m} -> m # hello world, from #PID<0.138.0>
end
block and wait for a message to come!
defmodule Counter do
def start(val), do: spawn(__MODULE__, :loop, [val])
def loop(state) do
receive do
{:status, pid} ->
send(pid, "status -> #{inspect state}")
loop(state)
:incr ->
loop(state + 1)
{:stop, pid} ->
send(pid, "stopping. last status -> #{inspect state}")
end
end
end
iex(3)> pid = Counter.start(0)
iex(4)> send(pid, {:status, self()})
iex(5)> flush
"status -> 0"
iex(6)> send(pid, :incr)
iex(7)> send(pid, {:status, self()})
iex(8)> flush
"status -> 1"
self() # #PID<0.120.0>
pid = spawn(f) # - using spawn/1
# 15:24:04.535 [error] Process #PID<0.108.0> raised an exception
# ** (RuntimeError) ouch
# (stdlib) erl_eval.erl:668: :erl_eval.do_apply/6
#
# - crashed and exited immediately
Process.alive?(pid) # false - crashed immediately
self() # #PID<0.120.0> - as nothing happend
# raise/1 causes a RuntimeError
f = fn -> raise("ouch") end
making a process crash using spawn
self() # #PID<0.120.0>
pid = spawn_link(f)
# ** (EXIT from #PID<0.104.0>) evaluator process exited with reason:
# an exception was raised:
# ** (RuntimeError) ouch
# (stdlib) erl_eval.erl:668: :erl_eval.do_apply/6
#
# 15:29:04.053 [error] Process #PID<0.110.0> raised an exception
# ** (RuntimeError) ouch
# (stdlib) erl_eval.erl:668: :erl_eval.do_apply/6
Process.alive?(pid) # false - crashed immediately, as expected
self() # #PID<0.126.0> - we crashed as well!
making a process crash using spawn_link
but.. why would you do that?
self() # #PID<0.126.0>
pid = spawn_link(f)
Process.flag(:trap_exit, true)
f = fn -> receive do :crash -> raise "ouch" end end
making a process crash using spawn_link and trapping exit signals
send(pid, :crash)
# 15:50:31.804 [error] Process #PID<0.140.0> raised an exception
# ** (RuntimeError) ouch
# (stdlib) erl_eval.erl:668: :erl_eval.do_apply/6
#
# - process crashed as expected but..
self() # #PID<0.126.0> - .. we did not!
# hang on
flush()
# {:EXIT, #PID<0.140.0>,
# {%RuntimeError{message: "ouch"},
# [{:erl_eval, :do_apply, 6, [file: 'erl_eval.erl', line: 668]}]}}
#
# - we received the exit message from the process !!
send(dest, message) # - from the official documentation
...
dest may be a remote or local PID, a (local) port, a locally registered name,
or a tuple {registered_name, node} for a registered name at another node.
Have you ever programmed something like this?
URL url = null;
try {
url = new URL(urlAsString);
} catch (MalformedURLException e) {
logger.error("Malformed URL", e);
}
HttpURLConnection connection = null;
try {
connection = (HttpURLConnection) url.openConnection();
} catch (IOException e) {
logger.error("Could not connect to " + url, e);
}
StringBuilder builder = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new ...
“Let it crash” refers to a coding philosophy that “if there are any errors, the process is automatically terminated, and this is reported to any processes that were monitoring the crashed process.”
supervisor
worker
supervisor
worker
worker
philip giuliani, developer @ keep in mind
daniel morandini, developer @ keep in mind
andrea janes, researcher @ free university of bozen-bolzano
philipgiuliani
@PhilipGiuliani
danielmorandini
@MorandiniDaniel
ajanes
By Daniel Morandini