Metagrokking Elixir

Metagrokking Elixir

Webcamp Zagreb, 2017

78d85ea26770975ea8c497d7bd722d55?s=128

Saša Jurić

October 04, 2017
Tweet

Transcript

  1. Metagrokking Elixir @sasajuric aircloak.com

  2. toc Elixir primer thinking in FP concurrency facing the web

    learning tips
  3. https://bit.ly/elixir-wczg2017

  4. Erlang software systems Elixir dev productivity

  5. None
  6. defmodule Hello do def world() do IO.puts("Hello, World!") end end

    Hello.world()
  7. spawn(...) # Kernel.spawn(...) abs(...) # Kernel.abs(...) length(...) # Kernel.length(...)

  8. :timer.sleep(1000)

  9. defmodule MyModule do def some_fun(a, b) do x = a

    + b y = a - b x * y end end
  10. def simple_fun(a,b), do: a + b def simple_fun(a,b), do: a

    + b
  11. def public_fun defp private_fun

  12. integer = 42 float = 3.14 string = "Hello, World!"

  13. :an_atom

  14. true # :true false # :false nil # :nil

  15. hello_world = {"Hello", "World"}

  16. hello_world = {"Hello", "World"} pattern term match operator

  17. {hello, world} = hello_world # hello = "Hello", world =

    "World"
  18. {hello, _} = hello_world {hello, _world} = hello_world

  19. {"Hello", "World"} = hello_world

  20. File.read(some_path) # {:ok, contents} # {:error, reason}

  21. case some_expression do pattern_1 -> expr_1 expr_2 ... pattern_2 ->

    ... ... end
  22. case File.read(some_path) do {:ok, contents} -> do_something_with(contents) {:error, reason} ->

    report_error(reason) end
  23. def fun(pattern_1, pattern_A), do: # ... def fun(pattern_2, pattern_B), do:

    # ... def fun(pattern_3, pattern_C), do: # ... # ...
  24. def fact(0), do: 1 def fact(n), do: n * fact(n

    - 1)
  25. {:ok, contents} = File.read(some_path)

  26. def read!(path) do {:ok, contents} = File.read(path) contents end

  27. map = %{} map1 = Map.put(map, some_key, some_value) map2 =

    Map.put(map1, another_key, another_value)
  28. map = %{} map = Map.put(map, some_key, some_value) map =

    Map.put(map, another_key, another_value)
  29. some_expr |> some_fun(arg2, arg3) some_fun(some_expr, arg2, arg3)

  30. map = %{} |> Map.put(some_key, some_value) |> Map.put(another_key, another_value)

  31. try do original_data |> trans_1(...) |> trans_2(...) ... |> trans_n(...)

    rescue _ -> original_data end
  32. item = %{ id: 1, name: "beer", quantity: 6 }

  33. item.id item.name item.quantity

  34. moar_beers = %{item | quantity: 12}

  35. Map.*

  36. list = [1, 2, 3]

  37. List.* Enum.*

  38. Enum.map(enumerable, predicate) Enum.any?(enumerable, predicate) Enum.filter(enumerable, predicate) Enum.drop_while(enumerable, predicate) ...

  39. add = fn x, y -> x + y end

  40. add.(2, 3)

  41. Enum.map( [-1, 2, -3], fn el -> abs(el) end )

  42. &(&1 + &2) fn x, y -> x + y

    end
  43. Enum.map([-1, 2, -3], &abs(&1))

  44. &SomeMod.some_fun/3 fn x, y, z -> SomeMod.some_fun(x, y, z) end

  45. Enum.map([-1, 2, -3], &abs/1)

  46. test.txt 4 6 4 44 65 7 8 34 6

    17 23 33 34 76 3 9 10
  47. "test.txt" |> File.read!() |> String.split() |> Enum.map(&String.to_integer/1) |> Enum.sum()

  48. keyword = [{:foo, x}, {:bar, y}]

  49. keyword = [foo: x, bar: y] # [{:foo, x}, {:bar,

    y}]
  50. IO.inspect( some_term, [{:limit, 5}, {:pretty, true}] )

  51. IO.inspect( some_term, [limit: 5, pretty: true] )

  52. IO.inspect( some_term, limit: 5, pretty: true )

  53. https://github.com/sasa1977/metagrokking_elixir

  54. code

  55. list = new ShoppingList() list = ShoppingList.new() OO FP

  56. list.entries() ShoppingList.entries(list) OO FP

  57. list.add_entry(...) list2 = ShoppingList.add_entry(list, ...) OO FP

  58. val = obj.getter() {val, new_data} = MyMod.getter(data) OO FP

  59. concurrent Elixir

  60. # process 1 spawn(fn -> # process 2 end) #

    process 1
  61. scheduler scheduler scheduler scheduler BEAM CPU CPU CPU CPU

  62. my_pid = self() spawned_pid = spawn(fn -> ... end)

  63. send(pid, message)

  64. receive do pattern_1 -> # ... pattern_2 -> # ...

    # ... end
  65. producer consumer random number

  66. defmodule Producer do ... defp produce_number() do :timer.sleep(:rand.uniform(1000)) number =

    :rand.uniform(11) - 6 IO.puts "produced #{number}" number end end
  67. defmodule Producer do def start(consumer_pid), do: spawn(fn -> loop(consumer_pid) end)

    ... end
  68. defmodule Producer do ... defp loop(consumer_pid) do ... loop(consumer_pid) end

    end
  69. defmodule Producer do ... defp loop(consumer_pid) do number = produce_number()

    send(consumer_pid, {:number, number}) loop(consumer_pid) end end
  70. defmodule Consumer do ... defp consume(sum, number) do :timer.sleep(:rand.uniform(1000)) new_sum

    = sum + number IO.puts "sum = #{new_sum}" new_sum end end
  71. defmodule Consumer do def start(), do: spawn(fn -> loop(0) end)

    ... end
  72. defmodule Consumer do ... defp loop(sum) do new_sum = receive

    do {:number, number} -> consume(sum, number) end loop(new_sum) end end
  73. consumer_pid = Consumer.start() Producer.start(consumer_pid)

  74. produced 3 sum = 3 produced -2 produced -3 sum

    = 1 produced 4 sum = -2 sum = 2
  75. server process long-running passive stateful

  76. OTP framework

  77. GenServer.start_link(Consumer, nil)

  78. defmodule Consumer do use GenServer ... end

  79. defmodule Consumer do def init(_arg), do: {:ok, 0} ... end

  80. GenServer.cast( consumer_pid, {:number, number} )

  81. defmodule Consumer do ... def handle_cast({:number, number}, sum) do new_sum

    = consume(sum, number) {:noreply, new_sum} end end
  82. defmodule Consumer do def start_link(), do: GenServer.start_link(Consumer, nil) def notify(consumer_pid,

    number), do: GenServer.cast( consumer_pid, {:number, number} ) ... end
  83. # producer Consumer.notify(consumer_pid, number)

  84. shopping list 1 shopping list 2 shopping list 3 shopping

    list 4 shopping list 5
  85. websocket shopping list server notification

  86. websocket request handler shopping list server modification request notification

  87. shopping list server

  88. None
  89. code

  90. supervisor child 1 child 2 child 3

  91. supervisor worker supervisor worker worker worker 1. 2. 3. 4.

    5. 6.
  92. Supervisor.start_link( [ ChildModule1, ChildModule2, ChildModule3 ], strategy: :one_for_one )

  93. static :one_for_one :one_for_all :rest_for_one dynamic :simple_one_for_one

  94. Supervisor.start_link( [ChildModule], strategy: :simple_one_for_one )

  95. Supervisor.start_child( supervisor_pid, [arg1, arg2, ...] )

  96. service supervisor list service 1 list service n …

  97. Supervisor.start_link( [ShoppingList.Service], name: :service_supervisor, strategy: :simple_one_for_one )

  98. Supervisor.start_child( :service_supervisor, ... )

  99. code

  100. request handler request for shopping list 123

  101. client list service process registry who is in charge of

    {:shopping_list_service, 123} ? register me as {:shopping_list_service, 123}
  102. backend system list service registry service supervisor

  103. code

  104. shopping list service ShoppingList add/update/delete

  105. entry added list_id 1 entry_id: 1 name: eggs quantity: 12

    entry updated list_id 2 entry_id: 1 quantity: 6 entry deleted list_id 3 entry_id: 1 ...
  106. code

  107. shopping list service websocket process websocket process websocket process websocket

    process
  108. subscriber shopping list service subscribe entries

  109. shopping list service ShoppingList add/update/delete subscriber

  110. code

  111. shopping list service ShoppingList add/update/delete subscriber

  112. subscribe subscription add/update/delete command apply event

  113. list service subscription command

  114. subscription command prepare event commit

  115. GenServer server process (service) Supervisor starting/stopping/restarting registered name static service

    discovery Registry dynamic service discovery Application auto start
  116. Task GenServer Supervisor registered name Application Registry

  117. functional Elixir organizing code concurrent Elixir organizing runtime

  118. web-facing system

  119. code

  120. It’s small, it’s elegant, it’s thoughtful, it’s fun, and it’s

    very, very, very readable once you flip over into functional programming mentality, which took my intern a week. Steve Cohen, Pinterest One week. That is how long it took for our engineers, who had all (but one) worked with Rails for a few years, to be productive on a new Phoenix client project. Brian Cardarella, Dockyard
  121. strategy learn the basics produce improve

  122. bootstrap Elixir guides Phoenix guides

  123. produce toy project prototype gradual migration

  124. consult Elixir reference Phoenix reference Ecto reference Erlang reference Erlang

    vs Elixir syntax
  125. community Elixir forum IRC Slack

  126. learning resources Elixir Phoenix

  127. https://bit.ly/elixir-wczg2017 40% off Elixir in Action with code ctwwebczg17 at

    https://www.manning.com/books/elixir-in-action