• Dry-monad let’s us treats errors as values (result monad?). This is very useful. As it let’s use compose operations, etc.

    The particular phrasing “errors as values” seems to come from Go, Pike in particular, unless I’m mistaken and he got it from somewhere. But I find this description to be really poor. Exceptions are values, what this is actually describing is the locality of control-flow: when you receive an error, can you be certain that it came from the code you’re interacting with? The nasty thing about exceptions is that any point in the stack can raise, and this makes it exponentially difficult to comprehend what possible exceptions there are.

    Does using dry-rb mean that one needs to ensure their dependencies aren’t using exceptions or write wrappers/adapters to enforce the errors as values pattern?

    dry-rb is a toolkit that contains many useful things. They work well together, but you can generally use them independently as well. This question relates to dry-monads specifically. The answer is, yes when I write an Operation object with a monadic interface I capture exceptions inside it and translate them into Result monads. This is very easy to do. It makes the Operation object a boundary where I can actually provide a level of certainty that exceptions raised are truly exceptional.


    score 1 · canonical

    • But I find this description to be really poor. Exceptions are values, what this is actually describing is the locality of control-flow.

      That’s a good point. I used that phrasing as I expect it easier to understand that result monad, but it is conveying the wrong thing we care about, which is treating the error path a part of the normal control flow.


      The answer is, yes when I write an Operation object with a monadic interface I capture exceptions inside it and translate them into Result monads. This is very easy to do. It makes the Operation object a boundary where I can actually provide a level of certainty that exceptions raised are truly exceptional.

      I know it is straight-foward to map things, but it is hard to do so exhaustively as Ruby gems use exceptions for all sorts of things. So one either has to convert all StandardError exceptions or have to deal with the problems of using exceptions for control flow. Maybe the wide-net approach doesn’t lead to any issues?


      score 1 · canonical

      • Some libraries are more well-behaved than others and define a top-level Error class. Those that don’t (looking at you, jwt) are annoying but often just tagging what step in the process raised is sufficient to know how to handle it. I usually have an all-else-fails rescue for StandardError that I log for better handling.

        The benefit is that you get to choose how granular your handling needs to be. Going all-in on exceptions-as-control-flow leads to an explosion of possible branching paths that is unsustainable; but using Results allows you to compress layers into fewer choices.

        I view the tradeoff here as similar to using case…in pattern matching: okay sure it’s not exhaustive in the same way as other languages, but it’s still a meaningful improvement.


        score 1 · canonical