I found that often a good way to understand a concept is to try your hand at a toy implementation of that concept and so I’m kicking off a series of posts where I try to implement things I find interesting.

Flows with failures

A fairly common programming scenario requires a series of steps to be performed where each step requires the output of the previous one as its input. Depending on the exact nature of these steps some of them may fail, for example because they require network/database access and the network/database is currently unavailable. In such a case the step is unable to produce its output and the sequence needs to be interrupted prematurely, possibly returning some kind of error message.

Let us take this code as a starting point:

def index
  user = find_user
  scope = authorized_scope(user)
  posts = find_posts(scope)

  render status: 200, json: prepare_json(posts)
rescue UserNotFound
  render status: 400, json: error_json("User not found")
rescue DatabaseError, NetworkError
  render status: 500, json: error_json("Connectivity error")
end

In this case we are using exceptions to signal that a given function could not perform its role. While not necessarily bad it’s interesting to see what other options are out there.

Meanwhile in the functional world

Functional languages, where using exceptions is discouraged or simply not possible, have produced another way of dealing with such a flow. The idea hinges on creating a data structure that contains both the information about whether the processing was successful or not and in each case the particulars of the given situation.

This structure is called Either in Scala or Haskell and can take one of two shapes. A Right indicates success and contains the result of our process. A Left indicates failure and contains some information about the manner of failure, for example lack of authorization. On top of that it is useful to have functions that can treat both a Left and a Right uniformly so that subsequent computation steps do not have to include error handling and instead only deal with the success case.

Using this mechanism we would like our earlier example to look something like this:

def index
  result = find_user.
    flat_map { |user| authorized_scope(user) }.
    flat_map { |scope| find_posts(scope) }.
    map { |posts| prepare_json(posts) }

  case result
  when Right then render(status: 200, json: result.value)
  when Left then render(status: 400, json: error_json(result.value))
  end
end

The assumption is that find_user, authorized_scope and find_posts return Left or Right while prepare_json can’t fail.

How does this work?

A basic implementation of Either in Ruby might look something like the following:

class Right
  attr_accessor :value

  def initialize(value)
    @value = value
  end

  def map
    Right.new(yield(value))
  end

  def flat_map
    yield(value)
  end
end

class Left
  attr_accessor :value

  def initialize(value)
    @value = value
  end

  def map
    self
  end

  def flat_map
    self
  end
end

You might be wondering about the difference between map and flat_map – you use map when the block yields a regular value, and flat_map if it yields an Either. In the previous example, both authorized_scope and find_posts can fail and thus wrap their result with Either as opposed to prepare_json which returns a raw value. If you always used map the output would be a Right, containing a Right, containing a Right, and so on – not very useful. The names are no accident either – you can check for yourself that Left behaves a lot like an empty array, while Right behaves a lot like an array with a single element when using these methods.

By using Either as the output of the computation steps that can fail you create two code paths – a regular one, and a failure one. As long as the computation steps succeed the regular path is taken, but as soon as one of the steps fails the code switches to the failure path and propagates the error down.

Using an Either is basically an alternative to exceptions for handling some flow that can be interrupted by an error. The choice will normally boil down to what will be more readable or maintainable in a given scenario, but I think it is useful to know that a choice exists in the first place!

Posted by

Share this article

  • karolmajta

    I totally agree with approach of having fun and doing whatever (works great in my life!), but seriously… algebraic data types (or case classes if you wish) only make sense if you actually have a static type checker. This code is as much flawless as useless. There is nothing in the world preventing you from returning nil, 10, or Pony from functions that should return an `Either` as well as unexpectedly accepting them. But I’m OK with that :)

    In Scala and Haskell, `Left` and `Right` are actually of same type, namely `Either`. This is the beauty of this construct, with Ruby you cannot even get close to this — `Left` and `Right` are different types (although I admit, that the syntax of `case Right`… etc. nicely mimics pattern matching, so this quite well played). I’m still OK with all that stuff.

    The only thing that’s lacking is an example of how not to use a “toy”, but something that actually makes sense in the language you’re working with. In Python, to get sort-of “Eitherish” semantics, I would probably just return a pair from a function (left element representing `Left` and right representing `Right`). What would be idiomatic way in Ruby to do this? You know, the one that does not bend the language, but rather flows along.

    Oh, and BTW. I did an article on “monads” in Python so I am as guilty as you when it comes to writing about stuff that does not make much sense :) You can check it out here if you’re curious http://bit.ly/1FDnaRh. No hard feelings and keep up the good work!

    • Paweł Obrok

      Hi! I’m so excited – the first ever comment on a blog post by me!

      I think filter flows with a lot of exits like you can find in many web app controllers are actually a good use-case for something like this. The example in the post is supposed to be reminiscent of such a flow if maybe not as complex as they tend to get. Think about all those cases where you render one of ten different error codes depending on the exact thing that went wrong.

      Most ruby libraries use either exceptions or return nil for error flow and I think that is idiomatic. Well designed APIs like ruby’s often allow an optional block to specify what should happen in the error case (for example hash.fetch(some_key) { some_default_value }). You can also return a regular Array from a function and it can be destructured (a, b = some_fun) but I don’t think anyone will expect this.

      More generally I think the idea of having a unified result of an operation regardless if it’s successful or not plays really way into an OO design – much better than returning nil for example. The full benefits of any heavyweight approach like this can only be reaped when a piece of code grows in complexity but I feel they are well worth knowing about.

    • Paweł Obrok

      Hi! I’m so excited – the first ever comment on a blog post by me!

      I think filter flows with a lot of exits like you can find in many web app controllers are actually a good use-case for something like this. The example in the post is supposed to be reminiscent of such a flow if maybe not as complex as they tend to get. Think about all those cases where you render one of ten different error codes depending on the exact thing that went wrong.

      Most ruby libraries use either exceptions or return nil for error flow and I think that is idiomatic. Well designed APIs like ruby’s often allow an optional block to specify what should happen in the error case (for example hash.fetch(some_key) { some_default_value }). You can also return a regular Array from a function and it can be destructured (a, b = some_fun) but I don’t think anyone will expect this.

      More generally I think the idea of having a unified result of an operation regardless if it’s successful or not plays really way into an OO design – much better than returning nil for example. The full benefits of any heavyweight approach like this can only be reaped when a piece of code grows in complexity but I feel they are well worth knowing about.