(learning by observing)
Erlang
hot code reload
pattern matching
behaviours
protocol
typespecs
processes
Supervisor
GenServer
macro
scheduler
BEAM
mailbox
pid
Registry
OTP
special forms
mix
distilery
node
cluster
via
monitor
:observer
Agent
Task
ETS
Mnesia
umbrella
(un)quote
hex
binaries
with
capture
multiple heads
|>
let is crash
tail call optimization
guards
struct
use
pin
BEAM process
Scheduler #1
Scheduler #2
Scheduler #3
Scheduler #4
[vrabac - ~]$ iex
Erlang/OTP 20 [erts-9.0] [source] [64-bit]
Interactive Elixir (1.6.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>
iex(1)> :crypto.rand_uniform(1, 10)
6
# main.exs
a = 1
b = 2
IO.inspect("#{a + b}")
$> elixir main.exs
"3"
# clock.exs
defmodule Clock do
def now() do
IO.inspect DateTime.utc_now()
end
end
$> iex clock.exs
Erlang/OTP 20 [erts-9.0] [source] [64-bit]
Interactive Elixir (1.6.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Clock.now
#DateTime<2019-03-31 05:44:40.524135Z>
[vrabac - running-scripts]$ iex clock.exs
Erlang/OTP 20 [erts-9.0] [source] [64-bit]
Interactive Elixir (1.6.1) - press Ctrl+C to exit (type h() ENTER for help)
# Compile a file
iex(2)> c "clock.exs"
warning: redefining module Clock (current version defined in memory)
clock.exs:1
[Clock]
# Recompile and reload a module
iex(3)> r Clock
warning: redefining module Clock (current version defined in memory)
clock.exs:1
{:reloaded, Clock, [Clock]}
# integer
1
# float
1.0
# atom
:name
# boolean - atom behind the scene
true || false
false == :false
# string: UTF-8 encoded binary
"hi"
# charlist: list of code points
'hi' == [104, 105]
# => true
# list
[1, 2, 3]
# tuple
{:ok, 1}
# map
%{a: 1, b: 2}
# anonymous function
square = fn(n) -> n * n end
square.(3)
# => 9
# PID
spawn(fn() -> 1 + 1 end)
# => #PID<0.132.0>
# Ref
spawn_monitor(fn() -> 1 + 1 end)
{#PID<0.90.0>, #Reference<0.919.270.234>}
# linked list syntax
[1] = [1 | []]
[1, 2] = [1 | [ 2 | []]]
[1, 2, 3] = [1 | [ 2 | [3 | []]]]
# concatenation
[1, 2] ++ [3, 4]
#=> [1, 2, 3, 4]
# map literal
user = %{first_name: "harry", last_name: "hole"}
# update field(s)
user = %{user | first_name: "marry"}
# => %{first_name: "marry", last_name: "hole"}
# dot access notation
user.first_name
# => "marry"
defmodule Stack do
def new() do
[]
end
def push(stack, item) do
[item | stack]
end
def pop(stack) do
[_head | tail] = stack
tail
end
end
stack = Stack.new()
stack = Stack.push(stack, 1)
stack = Stack.push(stack, 2)
Stack.pop(stack)
# => [1]
Stack.new()
|> Stack.push(1)
|> Stack.push(2)
|> Stack.pop()
# => [1]
Stack.pop(
Stack.push(
Stack.push(
Stack.new(),
1
),
2
)
)
defmodule Util do
def empty_array?([]) do
true
end
def empty_array?(_) do
false
end
end
defmodule Util do
def inc(n, step \\ 1) do
n + step
end
end
defmodule Math do
def positive?(n) when (is_float(n) or is_integer(n)) and n > 0 do
true
end
def positive?(_n) do
false
end
end
defmodule User do
defstruct [:first_name, :last_name]
def with_first_name(user, name) do
%{user | first_name: name}
end
end
user = %User{first_name: "Harry", last_name: "Hole"}
# => %User{first_name: "Harry", last_name: "Hole"}
john = User.with_first_name(user, "John")
# => %User{first_name: "John", last_name: "Hole"}
john.first_name
# => "John"
Map.get(john, :first_name)
# => "John"
1 = 1
2 = 1
# => ** (MatchError) no match of right hand side value: 1
a = 1
a
# => 1
a = 2
a
# => 2
3 = a
# => ** (MatchError) no match of right hand side value: 2
# overloading
def divide(l, 0) do
:error
end
def divide(l, r) do
l / r
end
# destructuring
{:ok, %Goth.Token{token: token}} = token.for_scope(scope)
# branching
case do_something() do
{:success, result} ->
# ...
{:error, reason} ->
# ...
end
$> pid = spawn(fn() -> IO.puts "Hi" end)
Hi
#PID<0.340.0>
$> Process.alive?(pid)
$> false
me = self()
pid = spawn(fn() ->
send(me, "hi")
end)
receive do
message ->
IO.inspect "Received: #{message}"
end
iex(26)> spawn_link(fn() -> 1/0 end)
** (EXIT from #PID<0.96.0>) shell process exited with reason: an exception was raised:
** (ArithmeticError) bad argument in arithmetic expression
:erlang./(1, 0)
17:04:55.141 [error] Process #PID<0.141.0> raised an exception
** (ArithmeticError) bad argument in arithmetic expression
:erlang./(1, 0)
Interactive Elixir (1.6.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>
iex(4)> Process.flag(:trap_exit, true)
true
iex(5)> spawn_link(fn() -> 1/0 end)
#PID<0.150.0>
17:06:06.437 [error] Process #PID<0.150.0> raised an exception
** (ArithmeticError) bad argument in arithmetic expression
:erlang./(1, 0)
iex(6)> flush
{:EXIT, #PID<0.150.0>, {:badarith, [{:erlang, :/, [1, 0], []}]}}
:ok
iex(1)> spawn_monitor(fn() -> 1/0 end)
{#PID<0.98.0>, #Reference<0.2335281372.2063597571.182390>}
iex(2)>
17:20:34.543 [error] Process #PID<0.98.0> raised an exception
** (ArithmeticError) bad argument in arithmetic expression
:erlang./(1, 0)
nil
iex(3)> flush
{:DOWN, #Reference<0.2335281372.2063597571.182390>, :process, #PID<0.98.0>,
{:badarith, [{:erlang, :/, [1, 0], []}]}}
:ok
defmodule Wallet do
defstruct [:uid, :transactions]
def new(uid) do
%Wallet{uid: uid, transactions: []}
end
def withdraw(wallet, amount) when amount > 0 do
%{wallet | transactions: [-amount | wallet.transactions]}
end
def deposit(wallet, amount) when amount > 0 do
%{wallet | transactions: [amount | wallet.transactions]}
end
def balance(wallet) do
Enum.sum(wallet.transactions)
end
def transactions(wallet) do
Enum.reverse(wallet.transactions)
end
end
Learning curve
Syntax
Performance
Concurency
Ecosystem
Documentation
Productivity
Maintainability
Debugging
Refactoring
Deployment
Testing
DSL