Crowdhailer

Crowdhailer

Creator of Raxx

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

Experimenting with this code.

OK.try do
  user <- fetch_user(1)
  cart <- fetch_cart(1)
  order = checkout(cart, user)
  save_order(order)
end

Ok.with/1 supports an else block that can be used for handling error values.

OK.with do
  a <- safe_div(8, 2)
  _ <- safe_div(a, 0)
else
  :zero_division -> # matches on reason
    {:ok, :inf}     # must return a new success or failure
end

The cart example above is equivalent to

with {:ok, user} <- fetch_user(1),
     {:ok, cart} <- fetch_cart(1),
     order = checkout(cart, user),
     {:ok, order_id} <- save_order(order)
do
   {:ok, order_id}
end

I have an implementation marked as beta as part of my OK project.

The Elixir with keyword has been very helpful for handling code with lots of branches, normally many error conditions. Such as the example above.

In the with example I find it is strange that the majority of my code my code lives in a list of arguments. It starts to get very lumpy if there are receive blocks or anonymous functions to get these values. Also in 90% of cases I am matching on an :ok tuple so don’t want to repeat that.

For these reasons I have been using the alternative macro from OK. It is far more restrictive because it will only match on :ok/:error tuples. However I like the restriction because it means that no matter how complex the block it will also only return :ok/:error tuples.
I am not sure that try is the best name. Alternatives that I am considering are

  • when, seams so make sense linguistically but already a keyword in elixir
  • with, familiar to elixir users
  • for, monadic for comprehension which is what this is but that might not be a very accessible term.
  • try, As is, however it doesn’t catch errors so the name could be misleading

Most Liked

ibgib

ibgib

I like OK.with because

  1. It let’s regular elixir users immediately grok that it’s like with.
  2. It’s idiomatic English! “I’m OK with that.”

(Try sounds like a try/catch/rescue replacement as opposed to a with replacement)

chrismccord

chrismccord

Creator of Phoenix

This is a great lib for playing with macros! That said, I’m personally :thumbsdown: on efforts around alternatives to with, as I haven’t seen justified benefits.

It’s worth pointing out that you should call out to functions in those cases. Also where you are seeing repetition, I am seeing explicit matching. The issues with Ok is that is hides the true match, and it gets more confusing if you were include a match within the 2nd elem, i.e. %{key: val} <- .... Is the rhs returning a tuple or map? It also breaks down the moment you want to match on something not in an :ok tuple. Using with, you simply add a new clause, with Ok you need to rewrite the entire block.

Crowdhailer

Crowdhailer

Creator of Raxx

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.

Examples

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"]}
Crowdhailer

Crowdhailer

Creator of Raxx

Version 1.6.0 released, with two main improvements.

  • Much better errors, showing code snippet, expected and actual values

      Binding to variable failed, '{:bad, 6}' is not a result tuple.
    
      Code
        b <- bar(a)
    
      Expected signature
        bar(a) :: {:ok, b} | {:error, reason}
    
      Actual values
        bar(a) :: {:bad, 6}
    
  • required/2 to turn nilable values to result tuples

      maybe_port = Map.get(config, :port)
      {:ok, port} | {:error, :port_number_required} = OK.required(maybe_port, :port_number_required)
vic

vic

Asdf Core Team

Searching happy path brought me here :D, turns out, I was experimenting with topics like this some time ago, actually some of my first macro libraries are either similar to Ok or explore something along the lines.

  • ok_jose
    This one was my first macro library, I just wanted a result monad, you know for piping ok tagged tuples. But one of my main focus was not to introduce (nor override) existing |> elixir operators.
    Mostly like Ok and also allowed you to define other patterns besides :ok/:error tagged tuples.
    See the defpipe on the README, for example
@doc "Pipes a valid changeset or its ok tagged tuple"
defpipe pipe_valid_changeset do
   valid = %Ecto.Changeset{valid?: true) -> valid
   {:ok, record} -> record
end

# then you can pipe functions that expect valid changesets
{:ok, %User{}}
|> cast(params, [:email, :password])
|> validate_password_conformation()
|> Repo.create
|> new_user_token()
|> pipe_valid_changeset() # no new syntax, just this guy rewrites the pipe
  • happy
    Then for cases that were code was not that homogeneous (not always returning :ok/:error tagged tuples) I just wanted to avoid lots of nested case and ended up doing something along the lines of with (back before it landed in Elixir 1.2, which I’ve been pretty much happy_with, except for its commas between match expressions)

  • pit
    This one is a bit weird (most things I do are), it was an experiment for having a with made for pipes. That is, you could specify a foo() |> pit(value <- pattern) |> bar() and let bar() take the value from the pattern matched result from foo.

Anyways, I guess it’s an interesting topic, and looks like for others too, since we have some people crafting this kind of things that do similar things (see the ones linked on Ok's README), if you find any other similar, I’d be interested in looking at it. :slight_smile:

BTW, great work on Ok @Crowdhailer, I’d go for OK.with since it’s closer to what with does and when reminds me of guards.

Where Next?

Popular in Libraries Top

hpopp
After just over two years in development, this latest version of Pigeon is what I finally consider done in regards to my original vision ...
New
pkrawat1
Hey guyz We at @aviabird are working on a payment library in elixir/phoenix. We are targeting March 2018 to add 56 Gateways to it. Have...
New
mathieuprog
Hello :wave: Allow me to introduce you to Tz, an alternative time zone database support to Tzdata. Why another library? First and fore...
New
kelvinst
Hey everyone! Well, we made this lib a while ago and now we decided to finally go out and public with it! It’s a tool for creating and m...
New
arkgil
Hi all! I’m happy to announce that Telemetry v0.3.0 is out! This release marks the conversion from Elixir to Erlang so that all the libr...
New
danschultzer
In short Plug n’ play OAuth 2.0 provider library. Just set up a resource owner schema with Ecto (your user schema), install the dependen...
New
Qqwy
Solution is a library to help you with working with ok/error-tuples in case and with-expressions by exposing special matching macros, as ...
New
tmbb
PhoenixWS - Websockets over Phoenix Channels Source code on Github here: https://github.com/tmbb/phoenix_ws Phoenix channels are a great...
New
handnot2
Samly can be used to enable SAML 2.0 Single Sign On in a Plug/Phoenix application. This library uses Erlang esaml to provide plug enabl...
New
ostinelli
Let’s write a database! Well not really, but I think it’s a little sad that there doesn’t seem to be a simple in-memory distributed KV da...
New

Other popular topics Top

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 27727 240
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New

Sub Categories:

We're in Beta

About us Mission Statement