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

Processes. OTP. Elixir

Sobolev Nikita
December 02, 2017
85

Processes. OTP. Elixir

Sobolev Nikita

December 02, 2017
Tweet

Transcript

  1. Для кого мой доклад? • Вы слышали про Erlang/Elixir •

    Вы читали туториал • Вы знакомы с другими функциональными языками
  2. OTP

  3. defmodule FpconfElixir do use Application def start(_type, _args) do import

    Supervisor.Spec, warn: false children = [ worker(FpconfElixir.AgentType, []), worker(FpconfElixir.GenStageType.Producer, [0]), worker(FpconfElixir.GenStageType.ProducerConsumer, []), worker(FpconfElixir.GenStageType.Consumer, []) ] opts = [strategy: :one_for_one, name: FpconfElixir.Supervisor] Supervisor.start_link(children, opts) end end
  4. defmodule FpconfElixir.SpawnType do def start_link do accepting_messages(0) end def accepting_messages(state)

    do receive do {:hello, message} -> IO.puts "Hello, #{message}" # Will accept only once without this: accepting_messages(state) {:counter} -> new_state = state + 1 IO.puts "New state is #{new_state}" accepting_messages(new_state) _ -> IO.puts "What?" accepting_messages(state) end end end
  5. pid = spawn fn -> FpconfElixir.SpawnType.start_link end send pid, {:hello,

    "world"} # => Hello, world send pid, {:counter} # => New state is 1 send pid, {:counter} # => New state is 2 send pid, {:other} # => What?
  6. defmodule FpconfElixir.TodoList do use GenServer def start_link do GenServer.start_link(__MODULE__, [],

    name: __MODULE__) end def add_task(task) when is_binary(task) do GenServer.cast(__MODULE__, {:add, task}) end def list_tasks, do: GenServer.call(__MODULE__, :list) # Private def handle_cast({:add, task}, tasks), do: {:noreply, [task | tasks]} def handle_call(:list, _from, tasks), do: {:reply, tasks, tasks} end
  7. defmodule FpconfElixir.AgentType do use Agent @doc """ Starts a new

    Agent process with an empty Map. """ def start_link do Agent.start_link(fn -> %{} end, name: __MODULE__) # this process is a singleton end def get(key) do Agent.get(__MODULE__, &Map.get(&1, key)) end def put(key, value) do Agent.update(__MODULE__, fn (state) -> Map.put(state, key, value) # happens on server end) end end
  8. ets

  9. defmodule FpconfElixir.GenStageType.Producer do use GenStage def start_link(initial \\ 0) do

    GenStage.start_link(__MODULE__, initial, name: __MODULE__) end def init(counter), do: {:producer, counter} def handle_demand(demand, state) do events = Enum.to_list(state..(state + demand - 1)) {:noreply, events, state + demand} end end
  10. defmodule FpconfElixir.GenStageType.ProducerConsumer do use GenStage def start_link do GenStage.start_link(__MODULE__, :state_doesnt_matter,

    name: __MODULE__) end def init(state) do {:producer_consumer, state, subscribe_to: [ FpconfElixir.GenStageType.Producer]} end def handle_events(events, _from, state) do numbers = events |> Enum.filter(&(rem(&1, 2) == 0)) {:noreply, numbers, state} end end
  11. defmodule FpconfElixir.GenStageType.Consumer do use GenStage def start_link do GenStage.start_link(__MODULE__, :state_doesnt_matter)

    end def init(state) do {:consumer, state, subscribe_to: [ FpconfElixir.GenStageType.ProducerConsumer]} end def handle_events(events, _from, state) do for event <- events do IO.inspect({self(), event, state}) end # As a consumer we never emit events {:noreply, [], state} end end
  12. Real world example A. Берем список репозиториев пользователя с Github

    B. Для каждого репозитория получим всех, кто ставил звездочку C. Сохраним их себе в базу для опытов
  13. File.stream!("source.txt", [], :line) |> Flow.from_enumerable() |> Flow.flat_map(&String.split/1) |> Flow.partition() #

    the magic happens here |> Flow.reduce(fn -> %{} end, fn(word, map) -> Map.update(map, word, 1, &(&1 + 1)) end) |> Enum.into(%{})