|
I think it's less strange than you think. In most languages that use errors as values, the tediousness is being directly attacked instead of trying to dodge around it. Haskell, in many of its uses, cleans up the tediousness so thoroughly that the code written using errors as values can be almost indistinguishable from code written using exceptions, and yet, nevertheless, the errors are values and no exception machinery is being deployed. It has been a general trend in pragmatic programming languages in the past couple of decades. Another huge example, in my opinion, is in typing. Static typing in the 20th century was terrible. Tedious, broken, and missing a lot of its value. So a lot of languages were written that basically amount to a "screw that, we're not using types", and they became very successful. But in the 21st century, a lot of work has been done directly attacking the tediousness and problematic aspects of using static types, while also getting more value out of them with safer languages that more pervasively enforce them and make them more reliable, thus more useful, etc. So we're seeing a resurgance of the popularity of very statically-typed languages... but it's not "moving backwards" because it's not the same thing as it used to be. Much like I don't expect dynamic languages to entirely go away, I wouldn't expect exceptions as we know them to go away either. But I expect "errors as values" to continue attracting more interest over time. In fact, as test34's sibling post sort of observes, there's some synergy between these two trends here. Making strong typing easier has made it easier to have strongly-typed, rich values that can be used as error values and used in various powerful ways. Now that there are languages where it's much easier to declare and fully exploit new types than it used to be, it's much easier to just go ahead and create a new error type as needed for some bit of code without it having to be a big production. |
I don't have experience with Haskell, but I have mixed feelings about monadic error handling in Scala for precisely this reason. It goes to great lengths to recreate the programming ergonomics of exceptions, with exactly the same drawbacks. Monadic error handling, aka "railway-oriented programming,"[0] splits your logic into two tracks: a "good" track, where all your happy path logic lives, and a "bad" track, which is automatically propagated alongside your happy path logic. In my experience, it induces the same programmer mistakes as exceptions do: errors get accidentally swallowed (especially where effects are constructed and transformed,) different errors that require different handling are accidentally treated the same, and programmers fall into the habit of seeing the error track as an inferior, second-class branch compared to the happy path.
It confuses me when programmers (not talking about you, because I don't know how you write code, but people I've worked with personally) bash exceptions and then use monadic error handling to achieve exactly the same trade-offs.
This hasn't turned me off of monadic error handling, but it has made me think of it as FP's version of exceptions, rather than an upgrade. Personally, I think exceptions are a good enough trade-off in most cases, but when you need to be more careful, it is better practice to give all paths the same prominence in code. FP provides a better way to do this: pattern matching. More verbose, yes; harder to spot the happy path when reading code, yes; encourages more careful and thorough thinking about errors, for me absolutely yes. YMMV.
[0] https://fsharpforfunandprofit.com/rop/