Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Functional Programming Essentials with Elixir

Functional Programming Essentials with Elixir

The world is changing, new languages comes and go, however, a paradigm change is something rare. We need to understand this change to be ready to develop software for the next decades. In this talk, you’ll learn why functional programming matters. You’ll see 3 essential concepts of functional programming: immutability, declarative code and pure functions. We’ll explore how functional programming concepts will help you create software with high parallelism for better use of modern hardware.

Ulisses Almeida

November 08, 2018
Tweet

More Decks by Ulisses Almeida

Other Decks in Programming

Transcript

  1. 3 concepts Functional Programming Essentials with Elixir Immutability Functions Declarative

    programming 3 concepts that shapes everything you do while programming in Elixir.
  2. Ulisses Almeida Functional Programming Essentials with Elixir Author of Learn

    Functional Programming with Elixir The Pragmatic Bookshelf
  3. 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.
  4. 3 concepts Functional Programming Essentials with Elixir Immutability Functions Declarative

    programming 3 concepts that shapes everything you do while programming in Elixir.
  5. Parallelism Functional Programming Essentials with Elixir Transistors number are increasing,

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

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

    Frequency and Performance are stable, Number of cores are increasing
  8. 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
  9. 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
  10. 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
  11. 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
  12. 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
  13. 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
  14. 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
  15. 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
  16. 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
  17. 3 concepts Functional Programming Essentials with Elixir Immutability Functions Declarative

    programming 3 concepts that shapes everything you do while programming in Elixir.
  18. 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
  19. 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)
  20. 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"]
  21. |> 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"
  22. |> 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"
  23. 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; }
  24. Imperative programming Functional Programming Essentials with Elixir ...and it works

    // Javascript var list = [ "dogs", "hot dogs", "Bananas" ]; capitalizeAll(list); // => ["Dogs", "Hot dogs", "Bananas"]
  25. 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
  26. 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"]
  27. 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"]
  28. 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]
  29. 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]
  30. 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 # => <struct User name="Ted">
  31. 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
  32. 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
  33. 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) }
  34. 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
  35. 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
  36. 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
  37. 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
  38. 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)
  39. 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
  40. 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
  41. 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
  42. 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)
  43. 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
  44. 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
  45. Parallelism Functional Programming Essentials with Elixir Transistor number are increasing,

    Frequency and Performance are stable, Number of cores are increasing
  46. 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
  47. 3 concepts Functional Programming Essentials with Elixir Immutability Functions Declarative

    programming 3 concepts that shapes everything you do while programming in Elixir.
  48. Be Part of a Revolution Functional Programming Essentials with Elixir

    . Facebook /coingaming.io LinkedIn company/coingaming Instagram /coingaming