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

Metagrokking Elixir

Metagrokking Elixir

Webcamp Zagreb, 2017

Saša Jurić

October 04, 2017
Tweet

More Decks by Saša Jurić

Other Decks in Programming

Transcript

  1. Metagrokking Elixir
    @sasajuric
    aircloak.com

    View Slide

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

    View Slide

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

    View Slide

  4. Erlang
    software systems
    Elixir
    dev productivity

    View Slide

  5. View Slide

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

    View Slide

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

    View Slide

  8. :timer.sleep(1000)

    View Slide

  9. defmodule MyModule do
    def some_fun(a, b) do
    x = a + b
    y = a - b
    x * y
    end
    end

    View Slide

  10. def simple_fun(a,b), do: a + b
    def simple_fun(a,b), do:
    a + b

    View Slide

  11. def public_fun
    defp private_fun

    View Slide

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

    View Slide

  13. :an_atom

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  23. def fun(pattern_1, pattern_A), do: # ...
    def fun(pattern_2, pattern_B), do: # ...
    def fun(pattern_3, pattern_C), do: # ...
    # ...

    View Slide

  24. def fact(0), do: 1
    def fact(n), do: n * fact(n - 1)

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  29. some_expr |> some_fun(arg2, arg3)
    some_fun(some_expr, arg2, arg3)

    View Slide

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

    View Slide

  31. try do
    original_data
    |> trans_1(...)
    |> trans_2(...)
    ...
    |> trans_n(...)
    rescue _ ->
    original_data
    end

    View Slide

  32. item = %{
    id: 1,
    name: "beer",
    quantity: 6
    }

    View Slide

  33. item.id
    item.name
    item.quantity

    View Slide

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

    View Slide

  35. Map.*

    View Slide

  36. list = [1, 2, 3]

    View Slide

  37. List.*
    Enum.*

    View Slide

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

    View Slide

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

    View Slide

  40. add.(2, 3)

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  46. test.txt
    4 6 4
    44 65 7 8
    34 6 17 23
    33 34 76 3
    9 10

    View Slide

  47. "test.txt"
    |> File.read!()
    |> String.split()
    |> Enum.map(&String.to_integer/1)
    |> Enum.sum()

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  54. code

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  59. concurrent Elixir

    View Slide

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

    View Slide

  61. scheduler scheduler scheduler scheduler
    BEAM
    CPU CPU CPU CPU

    View Slide

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

    View Slide

  63. send(pid, message)

    View Slide

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

    View Slide

  65. producer consumer
    random number

    View Slide

  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

    View Slide

  67. defmodule Producer do
    def start(consumer_pid), do:
    spawn(fn -> loop(consumer_pid) end)
    ...
    end

    View Slide

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

    View Slide

  69. defmodule Producer do
    ...
    defp loop(consumer_pid) do
    number = produce_number()
    send(consumer_pid, {:number, number})
    loop(consumer_pid)
    end
    end

    View Slide

  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

    View Slide

  71. defmodule Consumer do
    def start(), do:
    spawn(fn -> loop(0) end)
    ...
    end

    View Slide

  72. defmodule Consumer do
    ...
    defp loop(sum) do
    new_sum =
    receive do
    {:number, number} ->
    consume(sum, number)
    end
    loop(new_sum)
    end
    end

    View Slide

  73. consumer_pid = Consumer.start()
    Producer.start(consumer_pid)

    View Slide

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

    View Slide

  75. server process
    long-running
    passive
    stateful

    View Slide

  76. OTP framework

    View Slide

  77. GenServer.start_link(Consumer, nil)

    View Slide

  78. defmodule Consumer do
    use GenServer
    ...
    end

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  83. # producer
    Consumer.notify(consumer_pid, number)

    View Slide

  84. shopping list 1
    shopping list 2
    shopping list 3
    shopping list 4
    shopping list 5

    View Slide

  85. websocket
    shopping list server
    notification

    View Slide

  86. websocket
    request handler
    shopping list server
    modification request
    notification

    View Slide

  87. shopping list server

    View Slide

  88. View Slide

  89. code

    View Slide

  90. supervisor
    child 1 child 2 child 3

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  96. service supervisor
    list service 1 list service n

    View Slide

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

    View Slide

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

    View Slide

  99. code

    View Slide

  100. request handler
    request for shopping list 123

    View Slide

  101. client
    list service
    process registry
    who is in charge of
    {:shopping_list_service, 123} ?
    register me as
    {:shopping_list_service, 123}

    View Slide

  102. backend system
    list service registry service supervisor

    View Slide

  103. code

    View Slide

  104. shopping list service
    ShoppingList
    add/update/delete

    View Slide

  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
    ...

    View Slide

  106. code

    View Slide

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

    View Slide

  108. subscriber
    shopping list service
    subscribe
    entries

    View Slide

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

    View Slide

  110. code

    View Slide

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

    View Slide

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

    View Slide

  113. list service
    subscription command

    View Slide

  114. subscription command
    prepare event
    commit

    View Slide

  115. GenServer server process (service)
    Supervisor starting/stopping/restarting
    registered name static service discovery
    Registry dynamic service discovery
    Application auto start

    View Slide

  116. Task
    GenServer
    Supervisor
    registered name
    Application
    Registry

    View Slide

  117. functional Elixir
    organizing code
    concurrent Elixir
    organizing runtime

    View Slide

  118. web-facing system

    View Slide

  119. code

    View Slide

  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

    View Slide

  121. strategy
    learn the basics
    produce
    improve

    View Slide

  122. bootstrap
    Elixir guides
    Phoenix guides

    View Slide

  123. produce
    toy project
    prototype
    gradual migration

    View Slide

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

    View Slide

  125. community
    Elixir forum
    IRC
    Slack

    View Slide

  126. learning resources
    Elixir
    Phoenix

    View Slide

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

    View Slide