Crowdhailer

Crowdhailer

Creator of Raxx

Add error binding to function definitions, Experimental

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.

Most Liked

aseigo

aseigo

Personally, I think this leads to hard to reason about code due to the loss of locality between the function body and the else that may or may not be triggered. It’s the same reason goto is “considered harmful” (caveats and exceptions to that aside :slight_smile: )

This feels like a place where with would be a good solution. A bit of syntatic sugar around it to tease the behavior of with to automatically succeed on {:ok, anything} and fail on {:error, anything} would be nicer imho, and iirc someone recently did exactly that.

Also, I find that if I have long(er) functions where there are conditions somewhere in the middle that need to branch, such as on failure or success, that this is a signal that perhaps I should break that function there and send processing on to another function which will continue on success or return on failure.

IOW, each function goes as far as it can until need to react to a success/failure and at that point calls another function. So each function ends up being a bit-sized bit of code that does execute completely, allowing each function to be reasoned about simply and clearly.

Exceptions (or, what they really are: early returns) are a tool of “last resort” for me, used when they are really required only. Similarly for case/with/etc. If those start nesting, or a function ends up being a series of them, I take that as a code smell to start chopping that function up into more atomic sets of code to stuff into functions.

Where Next?

Popular in Libraries Top

scohen
Lexical Lexical is a next-generation language server for the Elixir programming language. Features Context aware code completion As-you...
New
deadtrickster
I’ve just released stable versions of my Prometheus Elixir libs: Elixir client [docs]; Ecto collector [docs]; Plugs instrumenter/Export...
New
benlime
I created a new library GitHub - benvp/ex_cva: Class Variance Authority for Elixir which aims to make it very easy to define different va...
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
tmbb
PhoenixWS - Websockets over Phoenix Channels Source code on Github here: https://github.com/tmbb/phoenix_ws Phoenix channels are a great...
New
ericlathrop
I built a silly site for Halloween that uses Phoenix Channels on the backend, and React on the frontend. I had many problems integrating ...
New
Crowdhailer
Experimenting with this code. OK.try do user &lt;- fetch_user(1) cart &lt;- fetch_cart(1) order = checkout(cart, user) save_or...
New
vic
Expat is a tiny experiment I did for extracting patterns and being able to reuse them (compose and share patterns between elixir librarie...
New
mattludwigs
Grizzly is a library for working with Z-Wave devices. Z-Wave is a low-frequency radio protocol for controlling smart home devices on a me...
New
maltoe
Hello! Came here to announce ChromicPDF, a pet project PDF generator I’ve been working on for the past few months. Why another PDF gener...
New

Other popular topics Top

sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
977 41022 311
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 45766 226
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
273 38985 115
New

Sub Categories:

We're in Beta

About us Mission Statement