I mentioned in my Hello, world! post that I built this site using Hanami. This site may seem simple on the surface; it is, after all, just a blog. However, the backend is packed with features that help me day to day. I have built a tool that lets me cross-post to both Bluesky and Mastodon. I have a private journal where I can keep notes throughout the day. There is a custom analytics engine to provide me with insights on how well my blog posts are doing. There is a messaging backend that lets readers reach me through my contact form without cluttering my inbox. I have also built a task manager that syncs with both Linear and GitHub Issues and helps me track what I need to do. Most importantly, I have built an activity feed that captures my GitHub commits, journal entries, blog posts, and tasks. It helps me answer the question "What did I do between ___ and ___ dates?", which, believe it or not, is hard for me to answer on my own, even for last week. The application is open source, and you can find it on GitHub.
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?
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.
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.
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