2. |> Lack of memory -> Accurate manual allocation, but lost pieces of
memory + error-prone
|> CPU becomes faster -> GC (automatic memory management)
|> CPU doesn’t become faster, but with more cores –> Concurrent
execution, but shared resources
|> Synchronisation -> Hard to code + performance penalty
|> Better concurrent model and execution environment -> Erlang VM
(perfect for distributed software), but lack of metaprogramming,
polymorphism and first-class tooling in ecosystem
HISTORY
3.
4. ELIXIR IS GREAT FOR WRITING
HIGHLY PARALLEL, RELIABLE
APPLICATIONS.
Jose Valim
ELIXIR SUPPORTED BY IDIOMS AND CONVENTIONS
6. Immutable data is known data
|> No way to concurrence data mess
|> Because handle state gracefully
IMMUTABILITY
Performance Implications
|> Copying Data
|> Garbage Collection (per process)
17. CHARACTER LISTS
'abc'
# > 'abc'
List.to_tuple('abc')
# > {97, 98, 99}
[97, 98, 99]
# > 'abc'
?a
# > 97
is_list('abc')
# > true
|> In practice, char lists are used mostly when interfacing
with Erlang, in particular old libraries that do not accept
binaries as arguments
20. COMPREHENSIONS
MAPGENERATOR FILTER
list = [1, 2, 3, 4, 5, 6, 7, 8]
# > [1, 2, 3, 4, 5, 6, 7, 8]
for x <- list, y <- list, x >= y, rem(x * y, 10) == 0, do: {x, y}
# > [{5, 2}, {5, 4}, {6, 5}, {8, 5}]
for x <- ~w{b c}, into: %{ "a" => "A" }, do: { x, String.upcase(x) }
# > %{"a" => "A", "b" => "B", "c" => "C"}
21. ANONYMOUS FUNCTIONS
f = fn (parameter list) -> body end
f = fn (a, b) -> a + b end
# > #Function<12.52032458/2 in :erl_eval.expr/5>
f.(1, 2)
# > 3
swap = fn [a, b] -> [b, a] end
# > #Function<6.52032458/1 in :erl_eval.expr/5>
swap.([1, 2])
# > [2, 1]
foobar = fn
(0, 0) -> "FooBar"
(0, _) -> "Foo"
(_, 0) -> "Bar"
(_, _) -> "Nothing"
end
# > #Function<12.52032458/2 in :erl_eval.expr/5>
foobar.(1, 0)
# > "Bar"
f = fn -> fn -> "A" end end
# > #Function<20.52032458/0 in :erl_eval.expr/5>
f.().()
# > "A"
add = &(&1 + &2)
# > &:erlang.+/2
add.(1, 2)
# > 3
multiplier = 10
# > 10
Enum.map [1, 2, 3, 4], &(&1 * multiplier)
# > [10, 20, 30, 40]
22. NAMED FUNCTIONS
defmodule Tea do
def brew(tea, water_temperature 90)
def brew(tea, water_temperature) when tea == "Gyokuro" do
"Made in Japan. Brewing #{tea} (#{water_temperature} C)..."
end
def brew(tea, water_temperature) when tea == "Tieguanyin" do
"Made in Chaina. Brewing #{tea} (#{water_temperature} C)..."
end
def brew(tea, water_temperature) do
"Brewing #{tea} (#{water_temperature} C)..."
end
defp think(topic) do
"Thinking about #{topic}..."
end
end
Tea.brew("Puer")
# > "Brewing Puer (90 C)..."
Tea.brew("Gyokuro")
# > "Made in Japan. Brewing Gyokuro (90 C)..."
Tea.brew("Gyokuro", 60)
# > "Made in Japan. Brewing Gyokuro (60 C)..."
Tea.brew("Tieguanyin", 80)
# > "Made in Chaina. Brewing Tieguanyin (80 C)..."
Tea.think("Eternity") # Check spelling
# > ** (UndefinedFunctionError) function Tea.think/1 is
undefined or private
# > Tea.think("Eternity")
23. RECURSION
defmodule MyList do
def sum([]), do: 0
def sum([head | tail]), do: head + sum(tail)
end
list = [3, 2, 1]
# > [3, 2, 1]
sum = MyList.sum(list)
# > 6
|> Reducing algorithm
|> Mapping algorithm
24. PIPE OPERATOR
defmodule Tea do
defmodule Master do
def brew(tea), do: "Brewing #{tea} with optimal water temperature..."
end
def find(title), do: title
end
Tea.Master.brew(Tea.find("Puer"))
# > "Brewing Puer with optimal water temperature..."
"Puer" |> Tea.find |> Tea.Master.brew
# > "Brewing Puer with optimal water temperature..."
prepend_awesome = fn x -> "Awesome " <> x end
# > #Function<6.52032458/1 in :erl_eval.expr/5>
append_try_it = fn x -> x <> ", try it!" end
# > #Function<6.52032458/1 in :erl_eval.expr/5>
"Elixir" |> prepend_awesome |> append_try_it # Exception
# > ** (CompileError) iex:8: undefined function append_try_it/1
# > (elixir) expanding macro: Kernel.|>/2
# > iex:8: (file)
"Elixir" |> prepend_awesome.() |> append_try_it.()
# > "Awesome Elixir, try it!"
25. FROM SOURCE CODE TO VIRTUAL MACHINE
.EXS
.EX
ELIXIR
COMPILER
BYTECODE
.BEAM
HDD
ERLANG VM
26. MODULES
defmodule Tea.Container do
def prepare do
IO.puts "Prepare container..."
end
end
defmodule Tea.Pot do
@material "yixing clay" # mostly use as a constant, can't be
defined inside function definition
def prepare do
IO.puts "Prepare pot from #{@material}..."
end
end
defmodule Tea.Bowl do
def prepare do
IO.puts "Prepare bowl..."
end
end
defmodule Tea.Spoon do
def prepare do
IO.puts "Prepare spoon..."
end
end
defmodule Tea.Utensils do
alias Tea.Container
alias Tea.{Pot, Bowl}
alias Tea.Spoon, as: Scoop
def prepare do
Container.prepare
Pot.prepare
Bowl.prepare
Scoop.prepare
end
end
defmodule Tea.Master do
def prepare(utensils) do
utensils.prepare
end
def choose_tea do
IO.puts "Choose tea sort..."
end
def brew do
IO.puts "Brew tea..."
end
def taste do
IO.puts "Thruuup-thruup... so tasty!"
end
end
defmodule Tea.Style do
defmacro style(name) do
quote do
IO.puts "Tea story: #{unquote(name)} style"
end
end
end
defmodule Tea.Ceremony do
import Tea.Style
alias Tea.Master
alias Tea.Utensils
style :chaineese
def run do
Master.prepare(Utensils)
Master.choose_tea
Master.brew
Master.taste
end
end
Tea.Ceremony.run
# > Prepare container...
# > Prepare pot from yixing clay...
# > Prepare bowl...
# > Prepare spoon...
# > Choose tea sort...
# > Brew tea...
# > Thruuup-thruup... so tasty!
# > :ok
27. |> Duck Typing
|> Type = Primitive implementation + Modules
|> @type
|> @spec
TYPE SUMMARY
28. PROTOCOLS
defprotocol CaesarCypher do
def encrypt(stringlike, shift)
def rot13(stringlike)
end
defimpl CaesarCypher, for: List do
def encrypt([], _shift), do: []
def encrypt([head | tail], shift) do
[rem((head - 97 + shift), 26) + 97 | encrypt(tail, shift)]
end
def rot13(list), do: encrypt(list, 13)
end
defimpl CaesarCypher, for: BitString do
def encrypt(string, shift) do
to_charlist(string) |> _encrypt(shift) |> to_string
end
def rot13(string), do: encrypt(string, 13)
defp _encrypt([], _shift), do: []
defp _encrypt([head | tail], shift) do
[rem((head - 97 + shift), 26) + 97 | _encrypt(tail, shift)]
end
end
CaesarCypher.encrypt('zappa', 1)
# > 'abqqb'
CaesarCypher.rot13('zappa')
# > 'mnccn'
CaesarCypher.encrypt("zappa", 1)
# > "abqqb"
CaesarCypher.rot13("zappa")
# > "mnccn"
29. |> Elixir is lexically scoped
|> Basic unit – function body
|> Module has it’s own non visible for functions scope
|> Comprehensions and with create a separate lexical scope
SCOPES
30. MACROS
|> Language extension
|> Never use a macro when you can use a function
ELIXIR
SOURCE
CODE
ELIXIR
MACRO
EXPANSION
ELIXIR
COMPILER
COMPILE TIME
BEAM
BYTECODE
CORE
ERLANG
31. MACROS
defmodule Meta do
defmacro define_methods(names) do
IO.puts("Compile time: #{inspect(names)}")
for name <- names do
quote do
def unquote(name)() do
IO.puts("Run time: #{inspect(unquote(name))}")
"Hi, from #{unquote(name)} method"
end
end
end
end
end
defmodule M do
import Meta
define_methods [:alpha, :beta]
end
# > Compile time: [:alpha, :beta]
M.alpha
# > Run time: :alpha
# > "Hi, from alpha method"
M.beta
# > Run time: :beta
# > "Hi, from beta method"
35. PROCESS IS NOT ONLY ABOUT
CONCURRENCY, BUT ALSO ABOUT
DISTRIBUTION AND FAULT-TOLERANCE.
Jose Valim
FAIL FAST, ALLOW SUPERVISOR TO RESTART OR BURY FAILED PROCESS
41. PROCESSES LINKING
P1 P2
spawn_link(fn ->
end)
P1 P2p2 = spawn(fn ->
end)
# > #PID<0.113.0>
Process.link(p2)
# > true
raise "Dad, I'm dead"
# > ** (EXIT from #PID<0.81.0>) an
exception was raised:
# > ** (RuntimeError) Dad, I'm
dead
:timer.sleep(1000)
raise "Mom, I'm dead”
# > ** (EXIT from #PID<0.81.0>) an
exception was raised:
# > ** (RuntimeError) Mom, I'm
dead
# > :ok
42. PROCESSES LINKING
P1 P2
spawn_link(fn ->
end)
P1 P2p2 = spawn(fn ->
end)
# > #PID<0.113.0>
Process.link(p2)
# > true
raise "Dad, I'm dead"
# > ** (EXIT from #PID<0.81.0>) an
exception was raised:
# > ** (RuntimeError) Dad, I'm
dead
:timer.sleep(1000)
raise "Mom, I'm dead”
# > ** (EXIT from #PID<0.81.0>) an
exception was raised:
# > ** (RuntimeError) Mom, I'm
dead
# > :ok
43. PROCESSES LINKING
P1 P2
spawn_link(fn ->
end)
P1 P2p2 = spawn(fn ->
end)
# > #PID<0.113.0>
Process.link(p2)
# > true
raise "Dad, I'm dead"
# > ** (EXIT from #PID<0.81.0>) an
exception was raised:
# > ** (RuntimeError) Dad, I'm
dead
:timer.sleep(1000)
raise "Mom, I'm dead”
# > ** (EXIT from #PID<0.81.0>) an
exception was raised:
# > ** (RuntimeError) Mom, I'm
dead
# > :ok
44. L
L
L
L
|> Application discovery
|> Failure detection
|> Processes management
|> Hot code swapping
|> Server structure
OTP
ERLANG MNESIA
L
L
L
L
OTP+ + =
A
A
45. L
L
L
L
|> Application discovery
|> Failure detection
|> Processes management
|> Hot code swapping
|> Server structure
OTP
ERLANG MNESIA
L
L
L
L
OTP+ + =
A
A
P
P P
47. TASK
defmodule Tea.Master do
def brew do
IO.puts "Brewing tea..."
:timer.sleep(1000)
IO.puts "Tea is ready!"
"a cup of tea"
end
end
IO.puts "[Tea Master] Please sit down and take a rest"
IO.puts "[Tea Master] I need some time to brew the tea"
worker = Task.async(fn -> Tea.Master.brew() end)
IO.puts "[Guest] I have to clean my mind"
result = Task.await(worker)
IO.puts "The result is #{result}"
# > [Tea Master] Please sit down and take a rest
# > [Tea Master] I need some time to brew the tea
# > [Guest] I have to clean my mind
# > Brewing tea...
# > Tea is ready!
# > The result is a cup of tea
49. GENSERVER
defmodule Stack do
use GenServer
# Client API
def start_link do
GenServer.start_link(__MODULE__, :ok, [])
end
def push(pid, item) do
GenServer.cast(pid, {:push, item})
end
def pop(pid) do
GenServer.call(pid, :pop)
end
# Server API
def handle_call(:pop, _from, state) do
[item | next_state] = state
{:reply, item, next_state}
end
def handle_cast({:push, item}, state) do
next_state = [item | state]
{:noreply, next_state}
end
end
{:ok, stack} = Stack.start_link
# > {:ok, #PID<0.117.0>}
Stack.push(stack, "milk")
# > :ok
Stack.push(stack, "butter")
# > :ok
Stack.pop(stack)
# > "butter"
Stack.pop(stack)
# > "milk"
50. SUPERVISOR
defmodule Mathex do
use Application
# See http://elixir-lang.org/docs/stable/elixir/Application.html
# for more information on OTP Applications
def start(_type, _args) do
import Supervisor.Spec
# Define workers and child supervisors to be supervised
children = [
supervisor(Mathex.Repo, []),
supervisor(Mathex.Endpoint, []),
# Start your own worker by calling: Mathex.Worker.start_link(arg1, arg2, arg3)
# worker(Mathex.Worker, [arg1, arg2, arg3]),
]
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: Mathex.Supervisor]
Supervisor.start_link(children, opts)
end
# Tell Phoenix to update the endpoint configuration
# whenever the application is updated.
def config_change(changed, _new, removed) do
Mathex.Endpoint.config_change(changed, removed)
:ok
end
end