1. 23

  2. 4

    I only read the slide deck, but this is an excellent presentation of Either/Result-based error handling. Very useful for users of any language that takes a functional approach to error handling, e.g. Rust which uses the same model that is presented here.

    1. 3

      I skipped through the slides pretty quickly, so maybe I missed this being emphasized, but one of my favorite qualities of using sum types (things that can be one of a few defined variations, like None and Just/Some are possible instances of Maybe/Option) and pattern matching is that a few compilers will tell you when you have forgotten to cover certain failure cases. This is so much easier to manage than hoping you’re properly handling every type of exception that may or may not be thrown into and then through any of your code.

      1. 2

        It’s hard to choose just one, but this is pretty much my favorite Rust feature.

      2. 2

        This reminds me of stderr + stdout !

        1. 2

          I had read the slides before, so today I watched the video (the one from NDC 2014), and I found Scott to be an extremely good presenter: calm, methodical, pragmatic. I would highly suggest taking an hour to listen to him talk (two great jokes in the presentation as well!), and to take a gander at some of the F# for Fun and Profit articles, some of them are really good.

          1. 2

            I also only read the deck and completely agree, this is a superbly useful introduction to using Either.

            I’m a little sad that he papers over exception handling by saying “just don’t use exceptions”. Is it just common knowledge that you shouldn’t use exceptions in FP, and if so is there another article I might read, or is there a different way of handling them nicely that just doesn’t fit into this framework?

            1. 4

              You can handle exception-throwing code by using a try/catch that gives you either a Success or a Failure wrapping the exception. This is the approach std::expected<T> in the C++ Library Fundamentals TS is going to take. It’s basically Result<T, std::exception_ptr>.

              Of course, this forces you to do the wrapping (at least semi-)manually.

              1. [Comment removed by author]

                1. 2

                  I’d love to see examples of your Ruby use, do you have any you can share?

              2. 1

                I have been experimenting with OO design (along the line of Bernhardt’s “Boundaries”) and pulling in FP concepts to a Ruby web app.

                Almost all of the raises are in immutable values that raise if something tries to construct them in an immutable state, but the only rescues are in the outermost layers. They catch exceptions only to recognize when users have invalid input.

                The handful of raises in the mutable entities are asserts to catch known, specific regressions. None are caught.

                Some services (I don’t love the name - objects here have side effects but little or no state, suggestions welcome) raise for assertions and one piece of flow control to the calling service (happens only after 16k invocations and enables a Tell, Don’t Ask encapsulation).

                There’s only one place where I commonly use an exception for flow control: if a database fetch fails I raise rather than return nil. A few places catch this to 404 the user, and two create an appropriate new object (eg. the lookup was a cache miss). Yes, a Maybe monad is the Right Answer but this app is already a step away from idiomatic Ruby.

                This codebase is not yet public, but fingers crossed the RailsConf talk proposal I’m submitting is accepted and I present it there.

                There’s a smaller example of this strategy in my new gem TwoFactorAuth. I only have one raise in a value/model (app/models/two_factor_auth/client_data.rb). I’m at the end of a long day so I can’t remember clearly, but I think this is an oversight and can be replaced with the appropriate ActiveModel::Validation. The rest of the raises are all assertions that the developer has configured the gem correctly at startup time; there are some heinous gotchas in implementing U2F.

                This gem does not use the value/entity split of the previous project because it is all about validating random, possibly hostile user input and debugging would be insane if you could only see one error at a time. ActiveModel::Validations is a nice fit for a gem that will almost always be used with Rails. Because of that I decided that “Railyway Oriented Programming”, though attractive, would be a huge step away from idiomatic Ruby/Rails for little benefit.

                Hope these notes on my experiements and the thought process behind them helps. :)