This example overrides Kernel.def/2, so I am aware it is only suitable as an experiment. However here are the results.
Given the functions fetch_user and fetch_cart that both return {:ok, value} or {:error, reason}.
defmodule MyApp do
use OK.Kernel
def checkout(user_id, cart_id) do
user <- fetch_user(user_id) # `<-` will bind user when fetch_user returns {:ok, user}
cart <- fetch_cart(cart_id) # `<-` will shortcut to else clause if returned {:error, reason}
order = checkout(cart, user) # Lines without `<-` behave normally
order.invoice_id
else
:user_not_found ->
IO.puts("No user for user_id: #{user_id}")
nil
:cart_not_found ->
IO.puts("User has no cart")
nil
end
end
By using OK.Kernel errors can be grouped in what I consider to be a very natural way.
I guess it’s natural because it looks like how I would arrange raise and catch in previous languages. However with the magic of Elixir macros I can do it all without using exception raising.
To try it yourself add the release candidate to your mix.exs
{:ok, "~> 1.7.0-rc.1"}
Comments welcome.
My motivation for trying this was to separate unhappy paths due to code errors ( I still raise exceptions for them) and the unhappy path due to bad input.


















