OK - elegant error handling with result monads (alternative to Elixir `with` special form)

Tags: #<Tag:0x00007f8ebfe3c880> #<Tag:0x00007f8ebfe3a198>


To be honest I wish I could have used yield or return but out of the options I had the others all seamed worse. i.e. else and rescue.


Well, you ‘could’. ^.^

OK.for do
  a <- safe_div(8, 2)
  b <- safe_div(a, 2)
  yield a + b

You’d not be able to use a function named ‘yield’ though, but I think that’s fine.

I’d still just vote for:

OK.for do
  a <- safe_div(8, 2)
  b <- safe_div(a, 2)
  a + b

It is perfectly and normally elixiry. :slight_smile:


I certainly don’t disagree with you. I’m going to use it in some code and see how I feel about it. I’m also working on a writer monad for sending messages and am going to see how that looks with the separating after or not


I made a block syntax for monad’y style do expressions in expede’s exceptional library as a PR if you want to see usage (though this one is for different style of error handling, the syntax is the same though): https://github.com/expede/exceptional/pull/18


Version 1.9.0 released with new OK.try macro for normalising return values.

The syntax here is chosen to be basically the same as a natural try block but in this case without any errors being raised.


require OK
import Raxx

OK.try do
  user <- fetch_user(1)             # `<-` operator means func returns {:ok, user}
  cart <- fetch_cart(1)             # `<-` again, {:ok, cart}
  order = checkout(cart, user)      # `=` allows pattern matching on non-tagged funcs
  saved_order <- save_order(order)
  response(:created)                # Value will be returned unwrapped
  :user_not_found ->
  :could_not_save ->

This is a very nice way to write code that reliably returns the same type of value.
My favourite feature of this is it is familiar but raise can now be used for exceptions that should never happen.
i.e. programmer errors that should end up in bug tracking software as soon as possible.

With OK.for that was introduced in 1.8.0 I think that these are sane replacements for OK.with which had variable behaviour dependant on which code blocks had been defined.


Version 1.9.1 released to fix warnings.

An warning that could occur when using pipes ~>> has been rectified.

The following code now will not raise any errors.

{:ok, num}
~>> double()


Version 1.9.3 released to deprecate OK.with/1

The general purpose behaviour of OK.with has been replaced with two separate options

  • OK.for When a return value should be wrapped as an ok/error tuple
  • OK.try When error handling should be attempted, such as transforming errors to a 500 response.

See docs for details: https://hexdocs.pm/ok/1.9.3/readme.html


Version 1.10.0: Add map/2 and ~> to treat result tuples as Functors.

The latest version of ok (1.10.0) has been release. It adds functionality to transform a value within an :ok tuple. e.g.


iex> {:ok, 5} ~> Integer.to_string
{:ok, "5"}

iex> {:error, :zero_division_error} ~> Integer.to_string
{:error, :zero_division_error}

iex> {:ok, "a,b"} ~> String.split(",")
{:ok, ["a", "b"]}


Version 1.11.0 Add map_all/2 for working through lists.

Transform every element of a list with a mapping function.
The mapping function must return a result tuple.

If all of the result tuples are tagged :ok, then it returns a list tagged with :ok.
If one or more of the result tuples are tagged :error, it returns the first error.


  iex> OK.map_all(1..3, &safe_div(6, &1))
  {:ok, [6.0, 3.0, 2.0]}

  iex> OK.map_all([-1, 0, 1], &safe_div(6, &1))
  {:error, :zero_division}