Slide 1

Slide 1 text

FP Essentials with Elixir Elixir Meetup, Tallinn November/2018

Slide 2

Slide 2 text

3 concepts Functional Programming Essentials with Elixir Immutability Functions Declarative programming 3 concepts that shapes everything you do while programming in Elixir.

Slide 3

Slide 3 text

Ulisses Almeida Functional Programming Essentials with Elixir @ulissesalmeida

Slide 4

Slide 4 text

Ulisses Almeida Functional Programming Essentials with Elixir Author of Learn Functional Programming with Elixir The Pragmatic Bookshelf

Slide 5

Slide 5 text

Who We Are Functional Programming Essentials with Elixir The Coingaming Group are the crypto gaming pioneers who operate the world’s leading bitcoin gaming and betting brands.

Slide 6

Slide 6 text

3 concepts Functional Programming Essentials with Elixir Immutability Functions Declarative programming 3 concepts that shapes everything you do while programming in Elixir.

Slide 7

Slide 7 text

Why?

Slide 8

Slide 8 text

Parallelism Functional Programming Essentials with Elixir

Slide 9

Slide 9 text

Parallelism Functional Programming Essentials with Elixir Transistors number are increasing, Frequency and Performance are stable, Number of cores are increasing

Slide 10

Slide 10 text

Parallelism Functional Programming Essentials with Elixir Transistors number are increasing, Frequency and Performance are stable, Number of cores are increasing

Slide 11

Slide 11 text

Parallelism Functional Programming Essentials with Elixir Transistors number are increasing, Frequency and Performance are stable, Number of cores are increasing

Slide 12

Slide 12 text

AMD Ryzen Functional Programming Essentials with Elixir Up to 32 cores

Slide 13

Slide 13 text

Joe Armstrong Functional Programming Essentials with Elixir Your Erlang program should just run N times faster on an N core processor https://pragprog.com/articles/erlan g

Slide 14

Slide 14 text

Amdahl's Law Functional Programming Essentials with Elixir A large portion of your task/program must run in parallel to take advantage of multi-core architecture

Slide 15

Slide 15 text

Amdahl's Law Functional Programming Essentials with Elixir A large portion of your task/program must run in parallel to take advantage of multi-core architecture

Slide 16

Slide 16 text

Amdahl's Law Functional Programming Essentials with Elixir If we apply 2 cores, we can speed up twice the parallel portion 4 minutes 4 minutes 4 minutes 2 2 cores

Slide 17

Slide 17 text

Amdahl's Law Functional Programming Essentials with Elixir If we double the number of cores, we can double the speed of the parallel portion 4 minutes 4 minutes 4 minutes 2 4 minutes 1 2 cores 4 cores

Slide 18

Slide 18 text

Amdahl's Law Functional Programming Essentials with Elixir Double it again... 4 minutes 4 minutes 4 minutes 2 4 minutes 1 4 minutes 2 cores 4 cores 8 cores

Slide 19

Slide 19 text

Amdahl's Law Functional Programming Essentials with Elixir ...and again… It doesn't matter anymore. We can have at maximum 50% time improvement. 4 minutes 4 minutes 4 minutes 2 4 minutes 1 4 minutes 4 minutes 2 cores 4 cores 8 cores 16 cores

Slide 20

Slide 20 text

Amdahl's Law Functional Programming Essentials with Elixir A large portion of your task/program must be parallelable to take advantage of multi-core architecture

Slide 21

Slide 21 text

Functional Programming Essentials with Elixir .4 7,6 minutes .4 .4 .4 2 cores 4 cores 8 cores 16 cores 4 minutes 4 minutes 4 minutes 2 4 minutes 1 4 minutes 4 minutes 2 cores 4 cores 8 cores 16 cores 50% 95% 3,8 minutes 1,9 minutes .4 10x faster 2x faster

Slide 22

Slide 22 text

Threads Functional Programming Essentials with Elixir Is parallel programming hard or is something wrong in our way of thinking?

Slide 23

Slide 23 text

Complexity Functional Programming Essentials with Elixir How reduce complexity in a parallel world?

Slide 24

Slide 24 text

Functional Programming

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

3 concepts Functional Programming Essentials with Elixir Immutability Functions Declarative programming 3 concepts that shapes everything you do while programming in Elixir.

Slide 27

Slide 27 text

Functions

Slide 28

Slide 28 text

Pure Functions Functional Programming Essentials with Elixir Limited to arguments Immutable values add2 = fn (n) -> n + 2 end add2.(2) # => 4 code = 10 add2.(code) # => 12

Slide 29

Slide 29 text

Explicit arguments Functional Programming Essentials with Elixir Is it good or bad? # Ruby cart .add_product(product) .create_order .notify # Elixir cart = Cart.add(cart, product) order = Order.create(cart, order) Mailer.notify(order, to: user)

Slide 30

Slide 30 text

First-class citizens Functional Programming Essentials with Elixir Functions are like any other data type. You can return as functions results or use them as arguments. Enum.map( ["dogs", "cats", "flowers"], &String.upcase/1 ) # => ["DOGS", "CATS", "FLOWERS"]

Slide 31

Slide 31 text

|> Data transformation Functional Programming Essentials with Elixir Chaining functions can be hard to read. def capitalize_words(title) do join_with_whitespace( capitalize_all( String.split(title) ) ) end capitalize_words("the dark tower") # => "The Dark Tower"

Slide 32

Slide 32 text

|> Data transformation Functional Programming Essentials with Elixir Pipe operator clarify the order of the execution and the data transformation flow. def capitalize_words(title) do title |> String.split() |> capitalize_all() |> join_with_whitespace() end capitalize_words("the dark tower") # => "The Dark Tower"

Slide 33

Slide 33 text

Declarative Programming

Slide 34

Slide 34 text

Imperative programming Functional Programming Essentials with Elixir Focus on how things need to be done... // Javascript function capitalizeAll(list) { var newList = []; for (var i = 0; i < list.length; i++) { newList.push( capitalize(list[i]) ); } return newList; }

Slide 35

Slide 35 text

Imperative programming Functional Programming Essentials with Elixir ...and it works // Javascript var list = [ "dogs", "hot dogs", "Bananas" ]; capitalizeAll(list); // => ["Dogs", "Hot dogs", "Bananas"]

Slide 36

Slide 36 text

Declarative programming Functional Programming Essentials with Elixir Focus on what needs to be done... # Elixir defmodule StringList do def capitalize_all([]), do: [] def capitalize_all([first | rest]) do [ String.capitalize(first) | capitalize_all(rest) ] end end

Slide 37

Slide 37 text

Declarative programming Functional Programming Essentials with Elixir Many ways of being declarative # Elixir list = [ "dogs", "hot dogs", "Bananas" ]; Enum.map( list, &String.capitalize/1 ); # => ["Dogs", "Hot dogs", "Bananas"]

Slide 38

Slide 38 text

Declarative programming Functional Programming Essentials with Elixir Many ways of being declarative in imperative languages // Javascript var list = [ "dogs", "hot dogs", "Bananas" ]; list.map((i) => i.toUpperCase()) // => ["DOGS", "HOT DOGS", "BANANAS"] # Ruby list.map(&:upcase) # => ["DOGS", "HOT DOGS", "BANANAS"]

Slide 39

Slide 39 text

Immutability

Slide 40

Slide 40 text

Mutability Functional Programming Essentials with Elixir Values has a tendency to change after their creation. # Ruby list = [1, 2, 3, 4] list.pop puts list.inspect # => [1, 2, 3] list.push(1) puts list.inspect # => [1, 2, 3, 1]

Slide 41

Slide 41 text

Immutability Functional Programming Essentials with Elixir Values can't change after their creation. # Elixir list = [1, 2, 3, 4] new_list = List.delete_at(list, -1) IO.inspect(list) # => [1, 2, 3, 4] IO.inspect(new_list) # => [1, 2, 3] new_list = list ++ [5] IO.inspect(list) # => [1, 2, 3, 4] IO.inspect(new_list) # => [1, 2, 3, 4, 5]

Slide 42

Slide 42 text

Immutability Functional Programming Essentials with Elixir Immutability in other languages sometimes doesn't work as expected. # Ruby User = Struct.new(:name) list = [ User.new("Bob"), User.new("Anna") ] list.freeze list.push(User.new("July")) # => RuntimeError: can't modify frozen Array list[0].name = "Ted" puts list[0].inspect # =>

Slide 43

Slide 43 text

Sharing values with mutability Functional Programming Essentials with Elixir Let's share an account to multiple simultaneously operations. # Ruby Account = Struct.new(:amount) do def deposit(value) new_amount = amount + value sleep(1) self.amount = new_amount end def withdraw(value) new_amount = amount - value sleep(1) self.amount = new_amount end end

Slide 44

Slide 44 text

Sharing values with mutability Functional Programming Essentials with Elixir Doing simple operations it handles well. # Ruby ulisses = Account.new(100) ulisses.deposit(20) ulisses.withdraw(20) ulisses.amount # => 100

Slide 45

Slide 45 text

Sharing values with mutability Functional Programming Essentials with Elixir Let's increase a little bit the complexity # Ruby Thread.new { ulisses.deposit(20) } Thread.new { ulisses.withdraw(20) }

Slide 46

Slide 46 text

Sharing values with mutability Functional Programming Essentials with Elixir Little bit more... # Ruby ulisses = Account.new(100) threads = 5.times.map { Thread.new { ulisses.deposit(20) } } + 5.times.map { Thread.new { ulisses.withdraw(20) } } threads.each(&:join) puts ulisses.amount

Slide 47

Slide 47 text

Sharing values with mutability Functional Programming Essentials with Elixir The results are not consistent. Using threads with mutability are not safe by default. You need abstractions! # Ruby 5.times { # do all that stuff } # => 120 # => 80 # => 100 # => 80 # => 120

Slide 48

Slide 48 text

Sharing values with mutability Functional Programming Essentials with Elixir Of course. That wasn't a safe implementation using threads. Always look for a abstractions: Actor Model, CSP, Promises, Semaphore... concurrent-ruby java.util.concurrent PHP: spawn Python: concurrent.futures

Slide 49

Slide 49 text

Sharing values with immutability Functional Programming Essentials with Elixir What to do when you can't change a reference directly? # Elixir amount = 100 Task.async(fn -> amount = amount + 20 end) IO.inspect amount # => 100

Slide 50

Slide 50 text

Sharing values with immutability Functional Programming Essentials with Elixir Elixir forces you to use a safe abstraction. Agent allows you to store a state and serialize changes using a process. # Elixir {:ok, ulisses} = Agent.start_link(fn -> 100 end)

Slide 51

Slide 51 text

Sharing values with immutability Functional Programming Essentials with Elixir You can communicate to Erlang process using messages. The process consumes each message in their arrival order. Process Msg 1 Msg 2 Msg 3 Msg ... Another process State

Slide 52

Slide 52 text

Sharing values with immutability Functional Programming Essentials with Elixir We can create functions that update and check the process state. # Elixir deposit = fn pid, value -> Agent.update(pid, fn total -> Process.sleep(100) total + value end) end withdraw = fn pid, value -> Agent.update(pid, fn total -> Process.sleep(100) total - value end) End balance = fn p -> Agent.get(p, &(&1)) end

Slide 53

Slide 53 text

Sharing values with immutability Functional Programming Essentials with Elixir Now, we can update that account and check their balance. # Elixir deposit.(ulisses, 10) IO.inspect balance.(ulisses) # => 110

Slide 54

Slide 54 text

Sharing values with immutability Functional Programming Essentials with Elixir Let's do a similar thing as the Ruby example. {:ok, ulisses} = Agent.start_link(fn -> 100 end) withdraws = for _i <- 1..5, do Task.async(fn -> deposit.(ulisses, 20) end) end deposits = for _i <- 1..5, do Task.async(fn -> withdraw.(ulisses, 20) end) end tasks = withdraws ++ deposits Enum.each(tasks, &Task.await/1) IO.inspect balance.(ulisses) Agent.stop(ulisses)

Slide 55

Slide 55 text

Sharing values with immutability Functional Programming Essentials with Elixir We can do asynchronously. # Elixir Task.async(fn -> deposit.(ulisses, 20) end) Task.async(fn -> withdraw.(ulisses, 20) end) # ... IO.inspect balance.(ulisses) # => 100

Slide 56

Slide 56 text

Sharing values with immutability Functional Programming Essentials with Elixir The result is consistent since the messages are serialized for _i <- 1..5 do # do all that 5 times end # 100 # 100 # 100 # 100 # 100

Slide 57

Slide 57 text

Recap

Slide 58

Slide 58 text

Parallelism Functional Programming Essentials with Elixir Transistor number are increasing, Frequency and Performance are stable, Number of cores are increasing

Slide 59

Slide 59 text

Amdahl's Law Functional Programming Essentials with Elixir A large portion of your task/program must be parallelable to take advantage of multi-core architecture

Slide 60

Slide 60 text

Complexity Functional Programming Essentials with Elixir How reduce complexity in a parallel world?

Slide 61

Slide 61 text

3 concepts Functional Programming Essentials with Elixir Immutability Functions Declarative programming 3 concepts that shapes everything you do while programming in Elixir.

Slide 62

Slide 62 text

Be Part of a Revolution Functional Programming Essentials with Elixir . Facebook /coingaming.io LinkedIn company/coingaming Instagram /coingaming