Slide 1

Slide 1 text

How Elixir is Transforming my Mind @nicholasjhenry, #myelixirstatus 12

Slide 2

Slide 2 text

Functional Programming approaches the mainstream 1 » composability » modularity » equational reasoning » parallelism 1 https://www.quora.com/What-advantages-do-functional-programming-languages-introduce @nicholasjhenry, #myelixirstatus 2

Slide 3

Slide 3 text

Becoming aware of these FP languages » Haskell » Scala » Elm » Clojure » and more... @nicholasjhenry, #myelixirstatus 3

Slide 4

Slide 4 text

So I started to explore... @nicholasjhenry, #myelixirstatus 4

Slide 5

Slide 5 text

@nicholasjhenry, #myelixirstatus 5

Slide 6

Slide 6 text

@nicholasjhenry, #myelixirstatus 6

Slide 7

Slide 7 text

@nicholasjhenry, #myelixirstatus 7

Slide 8

Slide 8 text

@nicholasjhenry, #myelixirstatus 8

Slide 9

Slide 9 text

@nicholasjhenry, #myelixirstatus 9

Slide 10

Slide 10 text

But nothing seemed to stick @nicholasjhenry, #myelixirstatus 10

Slide 11

Slide 11 text

Meet Elixir » a screencast original published by PeepCode (now on Pluralsight) » presented by José Valim (created of Elixir) » parse the meta-data for a collection of video streaming files » and that was the first day a transformation began @nicholasjhenry, #myelixirstatus 11

Slide 12

Slide 12 text

How Elixir is Transforming my Mind @nicholasjhenry, #myelixirstatus 12

Slide 13

Slide 13 text

This is not an overview of Elixir (or an introduction to Functional Programming) @nicholasjhenry, #myelixirstatus 13

Slide 14

Slide 14 text

These are the features that make Elixir fun for me (And I hope fun for you too) @nicholasjhenry, #myelixirstatus 14

Slide 15

Slide 15 text

Five things you need to know 1. Elixir runs on the Erlang VM 2. Functional Programming Language 3. Created by José Valim 4. It's not "Ruby on the Erlang VM" 5. Yes, there is a web framework - Phoenix @nicholasjhenry, #myelixirstatus 15

Slide 16

Slide 16 text

How Elixir has tranformed how I: 1. dispatch behaviour 2. work with data 3. think about concurrency @nicholasjhenry, #myelixirstatus 16

Slide 17

Slide 17 text

#myelixirstatus I have: » not deployed a production application » read way to many books » attended ElixirConf this year in Austin, Texas @nicholasjhenry, #myelixirstatus 17

Slide 18

Slide 18 text

1. How Elixir has transformed how I dispatch behaviour @nicholasjhenry, #myelixirstatus 18

Slide 19

Slide 19 text

how we dynamically choose behavior within our program -- Neal Ford, Functional Thinking @nicholasjhenry, #myelixirstatus 19

Slide 20

Slide 20 text

Before: leverage if statements to dynamically choose behaviour @nicholasjhenry, #myelixirstatus 20

Slide 21

Slide 21 text

Now: leverage pattern matching to dynamically choose behaviour @nicholasjhenry, #myelixirstatus 21

Slide 22

Slide 22 text

Example: Refactoring from testing conditions to matching patterns Define a function that creates a new customer: * name cannot be blank. * state cannot be blank. * domain cannot be blank. * enabled must be true to start with. @nicholasjhenry, #myelixirstatus 22

Slide 23

Slide 23 text

// original example if(name.isEmpty) { println("Name cannot be blank") null } else if(state.isEmpty) { println("State cannot be blank") null } else if(domain.isEmpty) { println("Domain cannot be blank") null } else { new Customer( 0, name, state, domain, true ) } @nicholasjhenry, #myelixirstatus 23

Slide 24

Slide 24 text

iex> Customer.create("Nicholas", "Québec", "firsthand.ca") %Customer{domain: "firsthand.ca", name: "Nicholas", state: "Québec"} @nicholasjhenry, #myelixirstatus 24

Slide 25

Slide 25 text

# poorly translated example defmodule Customer do defstruct name: nil, state: nil, domain: nil, enabled: true # %Customer{name: name, state: state, domain: domain} end @nicholasjhenry, #myelixirstatus 25

Slide 26

Slide 26 text

# poorly translated example defmodule Customer do defstruct name: nil, state: nil, domain: nil, enabled: true def create(name, state, domain) do end end @nicholasjhenry, #myelixirstatus 26

Slide 27

Slide 27 text

# poorly translated example defmodule Customer do defstruct name: nil, state: nil, domain: nil, enabled: true def create(name, state, domain) do if name == "" do IO.puts "Name cannot be blank" else if state == "" do IO.puts "State cannot be blank" else if domain == "" do IO.puts "Domain cannot be blank" else %Customer{name: name, state: state, domain: domain} end end end end end @nicholasjhenry, #myelixirstatus 27

Slide 28

Slide 28 text

Challenge » move from imperative implementation » we describing each step of the function » towards declarative implementation » we describe what we want @nicholasjhenry, #myelixirstatus 28

Slide 29

Slide 29 text

Declarative Example » a declarative language we use every day: » results in more expressive programs SELECT * FROM CUSTOMERS; @nicholasjhenry, #myelixirstatus 29

Slide 30

Slide 30 text

How? Pattern Matching » not regular expressions /hay/.match('haystack') #=> # » match scalar and non-scalar types » demonstrates why Elixir is not Ruby on the Erlang VM @nicholasjhenry, #myelixirstatus 30

Slide 31

Slide 31 text

= is not assignment iex> a = 10 iex> 10 = a irb> a = 10 => 10 irb> 10 = a SyntaxError: (irb):2: syntax error, unexpected '=', expecting end-of-input 10 = a ^ @nicholasjhenry, #myelixirstatus 31

Slide 32

Slide 32 text

= is a match operator iex> a = 10 iex> 20 = a ** (MatchError) no match of right hand side value: 10 » left-hand-side: "pattern" » right-hand-side: "term" @nicholasjhenry, #myelixirstatus 32

Slide 33

Slide 33 text

Pattern-matching on Tuples iex> {a, b, c} = {:hello, "world", 42} {:hello, "world", 42} @nicholasjhenry, #myelixirstatus 33

Slide 34

Slide 34 text

Pattern-matching on Tuples iex> {a, b, c} = {:hello, "world", 42} {:hello, "world", 42} iex> a :hello @nicholasjhenry, #myelixirstatus 34

Slide 35

Slide 35 text

Pattern-matching on Tuples iex> {a, b, c} = {:hello, "world", 42} {:hello, "world", 42} iex> a :hello iex> b "world" @nicholasjhenry, #myelixirstatus 35

Slide 36

Slide 36 text

Pattern-matching on Tuples iex> {a, b, c} = {:hello, "world", 42} {:hello, "world", 42} iex> a :hello iex> b "world" iex> c 42 @nicholasjhenry, #myelixirstatus 36

Slide 37

Slide 37 text

Mis-match Pattern iex> {:hello, b, c} = {:hello, "world", 42} {:hello, "world", 42} @nicholasjhenry, #myelixirstatus 37

Slide 38

Slide 38 text

Mis-match Pattern iex> {:hello, b, c} = {:hello, "world", 42} {:hello, "world", 42} iex> {:gidday, b, c} = {:hello, "world", 42} ** (MatchError) no match of right hand side value: {:hello, "world", 42} @nicholasjhenry, #myelixirstatus 38

Slide 39

Slide 39 text

Pattern matching used everywhere » binding values to variables (just demonstrated) » conditions and loops (coming-up) » function declarations and invocation (coming-up) » recursion (not covered today) @nicholasjhenry, #myelixirstatus 39

Slide 40

Slide 40 text

defmodule Customer do defstruct name: nil, state: nil, domain: nil, enabled: true def create(name, state, domain) do if name == "" do IO.puts "Name cannot be blank" else if state == "" do IO.puts "State cannot be blank" else if domain == "" do IO.puts "Domain cannot be blank" else %Customer{name: name, state: state, domain: domain} end end end end end @nicholasjhenry, #myelixirstatus 40

Slide 41

Slide 41 text

# use pattern matching with a case statement defmodule Customer do defstruct name: nil, state: nil, domain: nil, enabled: true def create(name, state, domain) do case {name, state, domain} do # some pattern matching here soon end end end @nicholasjhenry, #myelixirstatus 41

Slide 42

Slide 42 text

# use pattern matching with a case statement defmodule Customer do defstruct name: nil, state: nil, domain: nil, enabled: true def create(name, state, domain) do # case :term case {name, state, domain} do # clause (:head -> :body) {"", _state, _domain} -> IO.puts "Name cannot be blank" end end end @nicholasjhenry, #myelixirstatus 42

Slide 43

Slide 43 text

# use pattern matching with a case statement defmodule Customer do defstruct name: nil, state: nil, domain: nil, enabled: true def create(name, state, domain) do case {name, state, domain} do {"", _state, _domain} -> IO.puts "Name cannot be blank" {_name, "", _domain} -> IO.puts "State cannot be blank" end end end @nicholasjhenry, #myelixirstatus 43

Slide 44

Slide 44 text

# use pattern matching with a case statement defmodule Customer do defstruct name: nil, state: nil, domain: nil, enabled: true def create(name, state, domain) do case {name, state, domain} do {"", _state, _domain} -> IO.puts "Name cannot be blank" {_name, "", _domain} -> IO.puts "State cannot be blank" {_name, _state, ""} -> IO.puts "Domain cannot be blank" end end end @nicholasjhenry, #myelixirstatus 44

Slide 45

Slide 45 text

# use pattern matching with a case statement defmodule Customer do defstruct name: nil, state: nil, domain: nil, enabled: true def create(name, state, domain) do case {name, state, domain} do {"", _state, _domain} -> IO.puts "Name cannot be blank" {_name, "", _domain} -> IO.puts "State cannot be blank" {_name, _state, ""} -> IO.puts "Domain cannot be blank" _ -> %Customer{name: name, state: state, domain: domain} end end end @nicholasjhenry, #myelixirstatus 45

Slide 46

Slide 46 text

# use multi-clause functions defmodule Customer do defstruct name: nil, state: nil, domain: nil, enabled: true # multi-clause functions coming here soon end @nicholasjhenry, #myelixirstatus 46

Slide 47

Slide 47 text

# use multi-clause functions defmodule Customer do defstruct name: nil, state: nil, domain: nil, enabled: true def create("", _state, _domain), do: IO.puts "Name cannot be blank" end @nicholasjhenry, #myelixirstatus 47

Slide 48

Slide 48 text

# use multi-clause functions defmodule Customer do defstruct name: nil, state: nil, domain: nil, enabled: true def create("", _state, _domain), do: IO.puts "Name cannot be blank" def create(_name, "", _domain), do: IO.puts "State cannot be blank" def create(_name, _state, ""), do: IO.puts "Domain cannot be blank" end @nicholasjhenry, #myelixirstatus 48

Slide 49

Slide 49 text

# use multi-clause functions defmodule Customer do defstruct name: nil, state: nil, domain: nil, enabled: true def create("", _state, _domain), do: IO.puts "Name cannot be blank" def create(_name, "", _domain), do: IO.puts "State cannot be blank" def create(_name, _state, ""), do: IO.puts "Domain cannot be blank" def create(name, state, domain), do: %Customer{name: name, state: state, domain: domain} end @nicholasjhenry, #myelixirstatus 49

Slide 50

Slide 50 text

So, how has Elixir transformed how I dispatch behaviour? @nicholasjhenry, #myelixirstatus 50

Slide 51

Slide 51 text

Dispatching Behaviour Pattern matching in Elixir (and FP languages in general) moves me away from an imperative approach, towards a more declarative approach, producing more expressive code. @nicholasjhenry, #myelixirstatus 51

Slide 52

Slide 52 text

2. How Elixir has transformed how I work with data @nicholasjhenry, #myelixirstatus 52

Slide 53

Slide 53 text

Before Encapsulate data (state) within an object and mutate that data via method calls @nicholasjhenry, #myelixirstatus 53

Slide 54

Slide 54 text

Now Transform data with a pipeline of functions @nicholasjhenry, #myelixirstatus 54

Slide 55

Slide 55 text

We transform data all the time » HTTP request to an HTTP Response » SQL query to collection of objects » a CSV document to hash @nicholasjhenry, #myelixirstatus 55

Slide 56

Slide 56 text

Example: Count words iex> expected = %{ "one" => 1 , "fish" => 4 , "two" => 1 , "red" => 1 , "blue" => 1 } iex> Words.count("one fish two fish red fish blue fish") == expected true @nicholasjhenry, #myelixirstatus 56

Slide 57

Slide 57 text

Requirements 1. Count multiple occurances 2. Ignore punctuation 3. Include numbers 4. Normalize case @nicholasjhenry, #myelixirstatus 57

Slide 58

Slide 58 text

Sketch sentence |> Remove punctuation |> Normalize case |> Split into words |> Count words @nicholasjhenry, #myelixirstatus 58

Slide 59

Slide 59 text

Temporary variables module Words def count(sentence) do sentence1 = remove_punctuation(sentence) sentence2 = normalize_case(sentence1) sentence3 = split_into_words(sentence2) count_words(sentence3) end # private functions defined below end @nicholasjhenry, #myelixirstatus 59

Slide 60

Slide 60 text

Staircasing module Words def count(sentence) do count_words( split_into_words( normalize_case( remove_punctuation(sentence) ) ) ) end # private functions defined below end @nicholasjhenry, #myelixirstatus 60

Slide 61

Slide 61 text

Challenge rewrite the function without: - temporary variables - staircasing @nicholasjhenry, #myelixirstatus 61

Slide 62

Slide 62 text

Meet the pipe operator val |> my_function(a, b) same as: my_function(val, a, b) @nicholasjhenry, #myelixirstatus 62

Slide 63

Slide 63 text

You have probably used pipes before ls | wc -l @nicholasjhenry, #myelixirstatus 63

Slide 64

Slide 64 text

# refactored with the Pipe Operator module Words def count(sentence) do sentence end # private functions defined below end @nicholasjhenry, #myelixirstatus 64

Slide 65

Slide 65 text

# refactored with the Pipe Operator module Words def count(sentence) do sentence |> remove_punctuation end # private functions defined below end @nicholasjhenry, #myelixirstatus 65

Slide 66

Slide 66 text

# refactored with the Pipe Operator module Words def count(sentence) do sentence |> remove_punctuation |> normalize_case end # private functions defined below end @nicholasjhenry, #myelixirstatus 66

Slide 67

Slide 67 text

# refactored with the Pipe Operator module Words def count(sentence) do sentence |> remove_punctuation |> normalize_case |> split_into_words end # private functions defined below end @nicholasjhenry, #myelixirstatus 67

Slide 68

Slide 68 text

# refactored with the Pipe Operator module Words def count(sentence) do sentence |> remove_punctuation |> normalize_case |> split_into_words |> count_words end # private functions defined below end @nicholasjhenry, #myelixirstatus 68

Slide 69

Slide 69 text

So, how has Elixir transformed how I work with data? @nicholasjhenry, #myelixirstatus 69

Slide 70

Slide 70 text

Programming is transforming data, and the |> operator makes that transformation explicit. -- Dave Thomas, Programming with Elixir @nicholasjhenry, #myelixirstatus 70

Slide 71

Slide 71 text

3. How Elixir has transformed how I think about Concurrency @nicholasjhenry, #myelixirstatus 71

Slide 72

Slide 72 text

Concurrency vs. Parallelism 10 » Concurrency: dealing with multiple things at once » Parallelism: doing multiple things at once 10 http://concur.rspace.googlecode.com/hg/talk/concur.html#slide-5 @nicholasjhenry, #myelixirstatus 72

Slide 73

Slide 73 text

Before something to avoid @nicholasjhenry, #myelixirstatus 73

Slide 74

Slide 74 text

Now I actually use it. I use it often. @nicholasjhenry, #myelixirstatus 74

Slide 75

Slide 75 text

The Actor Model: a programming model for currency @nicholasjhenry, #myelixirstatus 75

Slide 76

Slide 76 text

Understanding the Actor Model 1. Actor == independent process 2. Shares nothing with any other process 3. processes send and receive messages @nicholasjhenry, #myelixirstatus 76

Slide 77

Slide 77 text

Processes on the Erlang VM » not OS processes » extremely lightweight » 100K's processes » utilizes all CPU cores @nicholasjhenry, #myelixirstatus 77

Slide 78

Slide 78 text

Explore concurrency in Elixir @nicholasjhenry, #myelixirstatus 78

Slide 79

Slide 79 text

Let's look at our example @nicholasjhenry, #myelixirstatus 79

Slide 80

Slide 80 text

defmodule FakeSearchEngine do def search(query) do result = case query do # {atom, query_time, text} "Ruby" -> {:ok, 100, "Ruby is a dynamic, reflective, object-oriented..."} "Python" -> {:ok, 2000, "Python is a widely used general-purpose, high-level..."} "Elixir" -> {:ok, 4000, "Elixir is a functional, concurrent, general-purpose..."} _ -> {:error, 10000, "Results not found" } end result = {_, query_time, _} :timer.sleep query_time result end end @nicholasjhenry, #myelixirstatus 80

Slide 81

Slide 81 text

iex> FakeSearchEngine.search("Ruby") {:ok, 100, "Ruby is a dynamic, reflective, object-oriented..."} @nicholasjhenry, #myelixirstatus 81

Slide 82

Slide 82 text

Challenge Run multiple searches concurrently ["Elixir", "Java", "Ruby"] @nicholasjhenry, #myelixirstatus 82

Slide 83

Slide 83 text

What you need to know spawn(fn -> expression_1 #... expression_n end) @nicholasjhenry, #myelixirstatus 83

Slide 84

Slide 84 text

iex> spawn(fn -> IO.inspect FakeSearchEngine.search("Ruby") end) @nicholasjhenry, #myelixirstatus 84

Slide 85

Slide 85 text

iex> spawn(fn -> IO.inspect FakeSearchEngine.search("Ruby") end) #PID<0.100.0> @nicholasjhenry, #myelixirstatus 85

Slide 86

Slide 86 text

iex> spawn(fn -> IO.inspect FakeSearchEngine.search("Ruby") end) #PID<0.100.0> {:ok, 100, "Ruby is a dynamic, reflective, object-oriented..."} @nicholasjhenry, #myelixirstatus 86

Slide 87

Slide 87 text

Multiple searches sequentially @nicholasjhenry, #myelixirstatus 87

Slide 88

Slide 88 text

Remember "each"? Ruby irb> a = [ "a", "b", "c" ] irb> a.each {|x| print "#{x} -- " } a -- b -- c -- => ["a", "b", "c"] Elixir iex> a = [ "a", "b", "c" ] iex> Enum.each(a, fn(x) -> IO.write "#{x} -- " end) a -- b -- c -- :ok @nicholasjhenry, #myelixirstatus 88

Slide 89

Slide 89 text

iex> Enum.each(["Elixir", "Java", "Ruby"], fn(query) -> ...> IO.inspect FakeSearchEngine.search(query) ...> end) @nicholasjhenry, #myelixirstatus 89

Slide 90

Slide 90 text

iex> Enum.each(["Elixir", "Java", "Ruby"], fn(query) -> ...> IO.inspect FakeSearchEngine.search(query) ...> end) {:ok, 4000, "Elixir is a functional, concurrent, general-purpose..."} @nicholasjhenry, #myelixirstatus 90

Slide 91

Slide 91 text

iex> Enum.each(["Elixir", "Java", "Ruby"], fn(query) -> ...> IO.inspect FakeSearchEngine.search(query) ...> end) {:ok, 4000, "Elixir is a functional, concurrent, general-purpose..."} {:error, 10000, "Results not found"} @nicholasjhenry, #myelixirstatus 91

Slide 92

Slide 92 text

iex> Enum.each(["Elixir", "Java", "Ruby"], fn(query) -> ...> IO.inspect FakeSearchEngine.search(query) ...> end) {:ok, 4000, "Elixir is a functional, concurrent, general-purpose..."} {:error, 10000, "Results not found"} {:ok, 100, "Ruby is a dynamic, reflective, object-oriented..."} :ok @nicholasjhenry, #myelixirstatus 92

Slide 93

Slide 93 text

Multi Searches Concurrently @nicholasjhenry, #myelixirstatus 93

Slide 94

Slide 94 text

iex> Enum.each(["Elixir", "Java", "Ruby"], fn(query) -> ...> spawn(fn -> IO.inspect FakeSearchEngine.search(query) end) ...> end) @nicholasjhenry, #myelixirstatus 94

Slide 95

Slide 95 text

iex> Enum.each(["Elixir", "Java", "Ruby"], fn(query) -> ...> spawn(fn -> IO.inspect FakeSearchEngine.search(query) end) ...> end) :ok @nicholasjhenry, #myelixirstatus 95

Slide 96

Slide 96 text

iex> Enum.each(["Elixir", "Java", "Ruby"], fn(query) -> ...> spawn(fn -> IO.inspect FakeSearchEngine.search(query) end) ...> end) :ok {:ok, 100, "Ruby is a dynamic, reflective, object-oriented..."} @nicholasjhenry, #myelixirstatus 96

Slide 97

Slide 97 text

iex> Enum.each(["Elixir", "Java", "Ruby"], fn(query) -> ...> spawn(fn -> IO.inspect FakeSearchEngine.search(query) end) ...> end) :ok {:ok, 100, "Ruby is a dynamic, reflective, object-oriented..."} {:ok, 4000, "Elixir is a functional, concurrent, general-purpose..."} @nicholasjhenry, #myelixirstatus 97

Slide 98

Slide 98 text

iex> Enum.each(["Elixir", "Java", "Ruby"], fn(query) -> ...> spawn(fn -> IO.inspect FakeSearchEngine.search(query) end) ...> end) :ok {:ok, 100, "Ruby is a dynamic, reflective, object-oriented..."} {:ok, 4000, "Elixir is a functional, concurrent, general-purpose..."} {:error, 10000, "Results not found"} @nicholasjhenry, #myelixirstatus 98

Slide 99

Slide 99 text

So, how has Elixir transformed how I think about concurrency? Now I spawn processes with the same ease and frequency as I instantiate objects in Ruby. @nicholasjhenry, #myelixirstatus 99

Slide 100

Slide 100 text

Begin your own transformation 1. Watch "Think Different(ly)" by Dave Thomas 3 2. Work through the Elixir Guides 4 3. Start practicing on Exercism.io 5 5 http://exercism.io/ 4 http://elixir-lang.org/getting-started 3 http://bit.ly/think-diff @nicholasjhenry, #myelixirstatus 100

Slide 101

Slide 101 text

Thank you! » Nicholas Henry » independent Ruby/Rails developer » @nicholasjhenry » http://blog.firsthand.ca » references will be posted along with a recording and slides @nicholasjhenry, #myelixirstatus 101