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

Railway Programming in Elixir with with

Chris Bell
October 27, 2016

Railway Programming in Elixir with with

Slides from my talk at the Empire City Halloween event.

Chris Bell

October 27, 2016
Tweet

More Decks by Chris Bell

Other Decks in Technology

Transcript

  1. defp update_order({:ok, order}) do # Do logic
 {:ok, order} end


    defp update_order({:error, reason}) do
 # Pass through {:error, reason} end
 # Repeat for every method
  2. defmodule Result do def bind({:ok, value}, func) do func.(value) end

    def bind({:error, _} = failure,_) do 
 failure
 end end
  3. import Result
 
 def process_checkout(order) do order |> bind(&update_order/1) |>

    bind(&capture_payment/1) |> bind(&send_notification_email/1) |> bind(&update_stock_levels/1)
 end
  4. import Result
 
 def process_checkout(order) do order |> bind(&update_order/1) |>

    bind(&capture_payment/1) |> bind(&send_notification_email/1) |> bind(&update_stock_levels/1)
 end Put down that Monad, theres another way.
  5. Allows us to execute a set of clauses only if

    the previous clause matches
  6. def process_checkout(order) do with {:ok, order} <- update_order(order), {:ok, order}

    <- capture_payment(order), {:ok, order} <- send_notification_email(order), {:ok, order} <- update_stock_levels(order), do: {:ok, order}
 end
  7. If the clause doesn’t match, the non matching value is

    returned and all subsequent steps are skipped.
  8. As of Elixir 1.3 we can also use an else

    block to handle non- matching clauses
  9. def process_checkout(order) do with {:ok, order} <- update_order(order), {:ok, order}

    <- capture_payment(order), {:ok, order} <- send_notification_email(order), {:ok, order} <- update_stock_levels(order) do 
 {:ok, order} else {:error, %Changeset{} = cs} -> handle_error(cs) {:error, reason} -> handle_other_error(reason) end
 end
  10. def process_checkout(order) do Repo.transaction(fn ->
 do_process_checkout(order)
 |> case do
 {:ok,

    order} -> order
 {:error, cs} -> Repo.rollback(cs)
 end
 end)
 end
 
 def do_process_checkout(order) do
 # Everything here returns a Ecto.Changeset with {:ok, order} <- update_order(order), {:ok, order} <- capture_payment(order), {:ok, order} <- send_notification_email(order), {:ok, order} <- update_stock_levels(order),
 do: {:ok, order}
 end