Skip to content

Instantly share code, notes, and snippets.

@wulymammoth
Last active February 16, 2020 09:50
Show Gist options
  • Save wulymammoth/839019423be90118ff36d1bbab80e607 to your computer and use it in GitHub Desktop.
Save wulymammoth/839019423be90118ff36d1bbab80e607 to your computer and use it in GitHub Desktop.
Railway Programming in Elixir

Railway Programming in Elixir

Links:

How do we stop execution of functions if one fails?

def process_checkout(order) do
  order
  |> update_order()
  |> capture_payment()
  |> send_notification_email()
  |> update_stock_levels()
end

Tagged tuples to represent success and error states {:ok, _} | {:error, }

defp update_order({:ok, order}) do
  # do stuff
  {:ok, order}
end
defp update_order({:error, reason}) do
  # do stuff
  {:error, reason}
end
...repeat for each function

A different way...

defmodule Result do
  def bind({:ok, value}, func) do
    func.(value)
  end

  def bind({:error, _} = failure, _) do
    failure
  end
end

using bind (a monad)

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

The Elixir Way

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_reason(reason)
end

An example with Ecto

def process_checkout(order) do
  Repo.transaction(fn ->
    do_process_checkout(order)
    |> case do
      {:ok, order} -> order
      {:error, cs} -> Repo.rollback(cs)
    end)
end

defp do_process_checkout(order) do
  # Everything here returns an 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment