Rigor Mortis - YOW! Conferences...Ludwig Wittgenstein 13 How far my efforts agree with those of...
Transcript of Rigor Mortis - YOW! Conferences...Ludwig Wittgenstein 13 How far my efforts agree with those of...
1
Dav
e Th
omas
@/+
prag
dave
(avoiding)Rigor Mortis
Dave is anOldFart
http://24.media.tumblr.com/tumblr_lr7ypweBfM1qa9b8ro1_500.png
Old FartsGet Stuck in Their Ways
http://www.runningheads.net/wp-content/uploads/2013/05/Curmudgeon_Logo.jpg
http://www.clipartbest.com/clipart-MTLL5pbac 4
So Do Young Ones
We Get
5
http
://w
ww
.clk
er.c
om/c
lipar
ts/W
/n/3
/A/j/
2/co
mfo
rtab
le-m
an.s
vg
We Get
6
http
://w
ww
.clk
er.c
om/c
lipar
ts/W
/n/3
/A/j/
2/co
mfo
rtab
le-m
an.s
vg
7
http
://w
ww
.clk
er.c
om/c
lipar
ts/W
/n/3
/A/j/
2/co
mfo
rtab
le-m
an.s
vg
Maintenance
Programmer
8
I Got
http
://w
ww
.clk
er.c
om/c
lipar
ts/W
/n/3
/A/j/
2/co
mfo
rtab
le-m
an.s
vg
The future is functional
The future is concurrent
9
Is teaching me a new vocabularyIs changing the way I think
10
Token Appeal to Authority
11
Ludwig Wittgenstein
12
Ludwig Wittgenstein
13
How far my efforts agree with those of other philosophers I will not decide. Indeed what I have here written makes no claim to novelty
in points of detail; and therefore I give no sources, because it is indifferent to me
whether what I have thought has already been thought before me by another.
Logico-Tractatus Philosophicus
Ludwig Wittgenstein Logico-Tractatus Philosophicus
14
How far my efforts agree with those of other philosophers I will not decide. Indeed what I have here written makes no claim to novelty
in points of detail; and therefore I give no sources, because it is indifferent to me
whether what I have thought has already been thought before me by another.
The limits of my language are the
limits of my worldLudwig Wittgenstein—Logico-Tractatus Philosophicus
15
16
How Has Learning Elixir Changed the Way I Think?
17
Functional |> Concurrent |> Pragmatic |> Fun
Functional |> Concurrent |> Pragmatic |> Fun
Different
Background
• Pattern matching
• Functions transform data
20
Pattern Matcha = 1
{ c, d } = { 2, 3 }
[ e, f, g ] = [ 4, 5, 6 ]
"Elixir " <> rest = "Elixir Rocks!" # rest => "Rocks!"
[ head | tail ] = [ 1, 2, 3, 4, 5, 6 ] # head => 1 # tail => [ 2,3,4,5,6 ]
21
Pattern Match
case File.open("myfile") do
{ :ok, device } -> IO.read(device, :line)
{ :error, reason } -> IO.puts "FAILED #{reason}"
end
22
Pattern Matchmy_fun = fn :plus, a, b -> a + b :times, a, b -> a * b end
IO.puts my_fun.(:plus, 3, 4) # => 7 IO.puts my_fun.(:times, 3, 4) # => 12
def other_fun(:minus, a, b), do: a - b def other_fun(:divide, a, b), do: a/b
23
Pattern Matching• Match based on shape and content
• Destructure data
• Recursive
24
For example…• fib(0) → 0
• fib(1) → 1
• fib(n) → fib(n-1) + fib(n-2)
25
Fibonaccidefmodule Fib do
def fib(0), do: 0 def fib(1), do: 1 def fib(n), do: fib(n-1) + fib(n-2)
end
IO.puts Fib.fib(10)
26
• fib(0) → 0
• fib(1) → 1
• fib(n) → fib(n-1) + fib(n-2)
27
defmodule Fib do def fib(0), do: 0 def fib(1), do: 1 def fib(n), do: fib(n-1) + fib(n-2) end
Programs Reflect Specification
28
Programs Reflect Specification
29
Implementation Reflects Transformation
Length of List
• Length of empty list is zero
• Length of list with head “h” and tail “t” is 1 + length(t)
30
31
• Length of empty list is zero
• Length of list with head “h” and tail “t” is 1 + length(t)
defmodule MyList do
def len([]), do: 0
def len([ _head | tail ]), do: 1 + len(tail)
end
IO.puts MyList.len [ 5, 4, 3 ]
Map
• Map of an empty list is an empty list
• Map of list with head “h” and tail “t” is a list whose head is func(h) and whose tail is map(t)
32
33
defmodule MyList do
def map([], _func), do: []
def map([ h | t ], func), do: [ func.(h) | map(t, func) ]
end
MyList.map [ 1,2,3,4,5], &(&1*&1)
• Map of an empty list is an empty list
• Map of list with head “h” and tail “t” is a list whose head is func(h) and whose tail is map(t)
More Practical
• Run-length encode a list of values:Runs of two or more of the same value “v” are replaced with { v, count }
[ 1, 2, 2, 2, 3, 4, 4, 5, 6, 6, 6, 6 ]
→ [ 1, {2, 3}, 3, {4, 2}, 5, {6, 4} ]
34
[ 1, 2, 2, 2, 3, 4, 4, 5, 6, 6, 6, 6 ]
→ [ ]
35
[ 2, 2, 2, 3, 4, 4, 5, 6, 6, 6, 6 ]
→ [ 1 ]
36
[ {2,2}, 2, 3, 4, 4, 5, 6, 6, 6, 6 ]
→ [ 1 ]
37
[ {2,3}, 3, 4, 4, 5, 6, 6, 6, 6 ]
→ [ 1 ]
38
[ 3, 4, 4, 5, 6, 6, 6, 6 ]
→ [ {2, 3}, 1 ]
39
[ 4, 4, 5, 6, 6, 6, 6 ]
→ [ 3, {2, 3}, 1 ]
40
[ {4,2}, 5, 6, 6, 6, 6 ]
→ [ 3, {2, 3}, 1 ]
41
[ 5, 6, 6, 6, 6 ]
→ [ {4,2} , 3, {2,3}, 1 ]
42
RLEdefmodule Rle do def encode(list), do: _encode(list, [])
end
RLE.encode [ 1, 2, 2, 2, 3, 4, 4, 5, 6, 6, 6, 6 ]
43
RLEdefmodule Rle do def encode(list), do: _encode(list, [])
end
RLE.encode [ 1, 2, 2, 2, 3, 4, 4, 5, 6, 6, 6, 6 ]
44
def _encode( [ a | tail ], result) do _encode(tail, [ a | result ]) end
RLEdefmodule Rle do def encode(list), do: _encode(list, [])
end
RLE.encode [ 1, 2, 2, 2, 3, 4, 4, 5, 6, 6, 6, 6 ]
45
def _encode( [ a | tail ], result) do _encode(tail, [ a | result ]) end
def _encode( [ {a, n}, a | tail ], result) do _encode( [ {a, n+1} | tail ], result ) end
RLEdefmodule Rle do def encode(list), do: _encode(list, [])
end
RLE.encode [ 1, 2, 2, 2, 3, 4, 4, 5, 6, 6, 6, 6 ]
46
def _encode( [ a | tail ], result) do _encode(tail, [ a | result ]) end
def _encode( [ {a, n}, a | tail ], result) do _encode( [ {a, n+1} | tail ], result ) end
def _encode([ a, a | tail ], result) do _encode( [ {a, 2} | tail ], result ) end
RLEdefmodule Rle do def encode(list), do: _encode(list, [])
end
RLE.encode [ 1, 2, 2, 2, 3, 4, 4, 5, 6, 6, 6, 6 ]
47
def _encode([], result), do: Enum.reverse(result)
def _encode( [ a | tail ], result) do _encode(tail, [ a | result ]) end
def _encode( [ {a, n}, a | tail ], result) do _encode( [ {a, n+1} | tail ], result ) end
def _encode([ a, a | tail ], result) do _encode( [ {a, 2} | tail ], result ) end
RLEdefmodule Rle do def encode(list), do: _encode(list, [])
end
RLE.encode [ 1, 2, 2, 2, 3, 4, 4, 5, 6, 6, 6, 6 ]
48
def _encode([], result), do: Enum.reverse(result)
def _encode( [ a | tail ], result) do _encode(tail, [ a | result ]) end
def _encode( [ {a, n}, a | tail ], result) do _encode( [ {a, n+1} | tail ], result ) end
def _encode([ a, a | tail ], result) do _encode( [ {a, 2} | tail ], result ) end
Input New input Result
[] «done» → [ values ]
[a,a,…] [ {a,2}, … ] → [ values ]
[{a,n}, a, … ] [ {a,n+1}, … ] → [ values ]
[b, …] [ … ] → [ b, values ]
RLE
49
State New State Result
[] «done» → [ values ]
[a,a,…] [ {a,2}, … ] → [ values ]
[{a,n}, a, … ] [ {a,n+1}, … ] → [ values ]
[b, …] [ … ] → [ b, values ]
RLE
50
RLEdefmodule Rle do
def encode(list) do list |> Enum.reduce([], &_encode/2) |> Enum.reverse end
def _encode(next, result) do case {next, result} do {a, [ a | rest ] } -> [ {a,2} | rest ] {a, [ {a,n} | rest ] } -> [ {a, n+1} | rest ] {a, rest } -> [ a | rest ] end end end
51
Not New
• Decision tables (1960s)
• State Machines (1960s)
• Blackboard systems (1980s)
52
53
1960s
RLEdefmodule Rle do
def encode(list) do list |> Enum.reduce([], &_encode/2) |> Enum.reverse end
def _encode(next, result) do case {next, result} do {a, [ a | rest ] } -> [ {a,2} | rest ] {a, [ {a,n} | rest ] } -> [ {a, n+1} | rest ] {a, rest } -> [ a | rest ] end end end
54
Transformation
RLEdefmodule Rle do
def encode(list) do list |> Enum.reduce([], &_encode/2) |> Enum.reverse end
def _encode(next, result) do case {next, result} do {a, [ a | rest ] } -> [ {a,2} | rest ] {a, [ {a,n} | rest ] } -> [ {a, n+1} | rest ] {a, rest } -> [ a | rest ] end end end
55
Event + State
RLEdefmodule Rle do
def encode(list) do list |> Enum.reduce([], &_encode/2) |> Enum.reverse end
def _encode(next, result) do case {next, result} do {a, [ a | rest ] } -> [ {a,2} | rest ] {a, [ {a,n} | rest ] } -> [ {a, n+1} | rest ] {a, rest } -> [ a | rest ] end end end
56
TransitionsEvent + State
Markdown
57
System Types ============
These types reflect resources in the underlying Erlang VM.
IDs and Ports -------------
A PID is a reference to a local or remote process, and a port is a reference to a resource (typically external to the application) that you'll be reading or writing.
Markdown defp parse([ %Line.Blank{}, %Line.Text{content: heading}, %Line.SetextUnderlineHeading{level: level}
| rest ], result) do
parse(rest, [ %Heading{content: heading, level: level} | result ]) end
58
Large Scale, Too
• REST Service Request: get_special_offers_for_user
• identify user from auth token• find local offers• find national offers• merge and respond
59
} asynchronous
Find local offers Find national offers
Lookup User
Format response
Request: auth token
Response: . . .
60
%{ get_offers: auth } -> %{ get_offers: auth, user: user_from(auth) }
%{ get_offers: auth, user: nil } -> %{ respond: :not_authorized }
%{ get_offers: auth, user: user } -> async_get_local_offers(user) async_get_national_offers(user)
%{ get_offers: auth, user_user, national: :error }, -> respond_with_error
%{ get_offers: auth, user_user, local: :error }, -> respond_with_error
%{ get_offers: auth, user_user, national: national, local: local }, -> format_response(user, national, local)
61
Enough Already
62
The Point
• Pattern matches are the rules in a state machine
• Each match specifies a transformation of state
• The transformation is the application of functions
63
Interesting Opportunities
• Easily made parallel
• DSL could define business flow
• granular reuse
• easier testing
• better error handling
64
Think Differently
65
Program Differently
66
Program Differently• New languages are an opportunity
• Let’s not recreate where we came from
• Let’s have fun
67
68
My name is Inigo Montoya. You killed my programming paradigm. Prepare to learn.
Dave ThomasPragmatic Programmers@/+pragdave