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 full-size slide

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

    View full-size slide

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

    View full-size slide

  4. Erlang
    software systems
    Elixir
    dev productivity

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  7. :timer.sleep(1000)

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  10. def public_fun
    defp private_fun

    View full-size slide

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

    View full-size slide

  12. true # :true
    false # :false
    nil # :nil

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  31. item.id
    item.name
    item.quantity

    View full-size slide

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

    View full-size slide

  33. list = [1, 2, 3]

    View full-size slide

  34. List.*
    Enum.*

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  54. concurrent Elixir

    View full-size slide

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

    View full-size slide

  56. scheduler scheduler scheduler scheduler
    BEAM
    CPU CPU CPU CPU

    View full-size slide

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

    View full-size slide

  58. send(pid, message)

    View full-size slide

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

    View full-size slide

  60. producer consumer
    random number

    View full-size slide

  61. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  65. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  70. server process
    long-running
    passive
    stateful

    View full-size slide

  71. OTP framework

    View full-size slide

  72. GenServer.start_link(Consumer, nil)

    View full-size slide

  73. defmodule Consumer do
    use GenServer
    ...
    end

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  77. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  80. websocket
    shopping list server
    notification

    View full-size slide

  81. websocket
    request handler
    shopping list server
    modification request
    notification

    View full-size slide

  82. shopping list server

    View full-size slide

  83. supervisor
    child 1 child 2 child 3

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  89. service supervisor
    list service 1 list service n

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  92. request handler
    request for shopping list 123

    View full-size slide

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

    View full-size slide

  94. backend system
    list service registry service supervisor

    View full-size slide

  95. shopping list service
    ShoppingList
    add/update/delete

    View full-size slide

  96. 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 full-size slide

  97. shopping list service
    websocket process
    websocket process
    websocket process
    websocket process

    View full-size slide

  98. subscriber
    shopping list service
    subscribe
    entries

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  102. list service
    subscription command

    View full-size slide

  103. subscription command
    prepare event
    commit

    View full-size slide

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

    View full-size slide

  105. Task
    GenServer
    Supervisor
    registered name
    Application
    Registry

    View full-size slide

  106. functional Elixir
    organizing code
    concurrent Elixir
    organizing runtime

    View full-size slide

  107. web-facing system

    View full-size slide

  108. 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 full-size slide

  109. strategy
    learn the basics
    produce
    improve

    View full-size slide

  110. bootstrap
    Elixir guides
    Phoenix guides

    View full-size slide

  111. produce
    toy project
    prototype
    gradual migration

    View full-size slide

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

    View full-size slide

  113. community
    Elixir forum
    IRC
    Slack

    View full-size slide

  114. learning resources
    Elixir
    Phoenix

    View full-size slide

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

    View full-size slide