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

Processes. OTP. Elixir

Avatar for Sobolev Nikita Sobolev Nikita
December 02, 2017
100

Processes. OTP. Elixir

Avatar for Sobolev Nikita

Sobolev Nikita

December 02, 2017
Tweet

More Decks by Sobolev Nikita

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(%{})