9 ms·
Exception based error handling is so bad and unsafe that adopting functional error handling with Either, Try etc as implemented by functional addon libraries fo
by bedobi 3y ago
Exception based error handling is so bad and unsafe that adopting functional error handling with Either, Try etc as implemented by functional addon libraries for many languages, while not yet common, in time it will become the new default even in OO languages. (just like it's been the default in functional languages for decades)
Functional error handling types are much simpler, safer and more powerful.
Simpler because they don't rely on dedicated syntax- they're just regular objects no different to any other object.
Safer because unlike exceptions, they force callers to handle all potential outcomes, but no more. (no risk of ignoring errors and no risk of catching a higher level of error than desired, ubiquitous bugs in exception based error handling)
Powerful because they support map, flatmap, applicative etc, making it easy to eg chain multiple computations together in desired ways, which is unwieldy and bug prone when using exceptions.
> What is wrong about dedicated syntax
It adds complexity to the language! It could be that, when learning Java, Kotlin and any other language, we learn that methods return what they say they do... and that's that. No weird dedicated syntax and magic, special treatment for returning anything other than the happy path, and the HUGE complexity that comes with it, eg the dedicated syntax itself and how it behaves, differences between checked and unchecked exceptions, hierarchies of exceptions etc etc.
> Exceptions are easier
But that's the point, they're not.
Exceptions based error handling is unnecessary, hugely complex, doesn't compose at all, obfuscates or straight up hides what can go wrong with any given call, so leads to countless trivially preventable bugs... I could go on. And after decades of use, there's still no consensus about what exceptions should be or how they should be used. Exceptions are a failed experiment and I have no doubt that in ten years, Java, Kotlin and many other languages will acknowledge as much and move away from it the same way Joda Time outcompeted and replaced the horrible Java date and time library.
- misja111 3y agoException based error handling is unsafe when they are unchecked exceptions. Checked exceptions however are as safe as Either, Try, Monads, Applicatives or whatever. You are forced to declare them in your method signature, the caller is forced to either handle them or rethrow them + declare them as well. And I guess this is precisely why so many developers hate them; they don't like the extra work they have to do to catch all those edge conditions. This is why we see so many empty catch blocks or upcasting to Exception or even Throwable; it is laziness. I would also argue that checked exceptions are no more complex than Eithers, Try or Applicatives. Actually passing Eithers or Applicatives around everywhere can easily clutter your code as well, IMO it can be worse than checked Exceptions.
- renewiltord 3y agoUltimately, core language features that make things ergonomic enough for lazy developers to get it right will result in better software. And ones that are so unergonomic that only industrious developers will get them right will result in worse software. Modern languages allow these to be zero cost abstractions so there is little tradeoff.
- misja111 3y agoI have been coding in Scala/ Haskell for the last 10 years, before that 15 years in Java. What I'm seeing is that there are as many lazy developers in FP as there are in Java, maybe even more. And despite all the nice safeguards that FP provides, there are still plenty of ways for lazy devs to work around them. For instance the IO monad, which is used everywhere Scala/Cats. It can contain a result or an error. If you don't feel like checking for an error after you called some method, you can just pass it up and return the IO from your method. Does that sound familiar? It behaves just like a checked exception, the only difference is that methods don't need to declare any error or exception in their IO signature.
- mrkeen 3y ago> It behaves just like a checked exception, the only difference is that methods don't need to declare any error or exception in their IO signature. Then it behaves like an unchecked exception. Which is fine. 'Checking' is the act of modifying your source code to declare the error.
- bedobi 3y ago> Checked exceptions however are as safe No. If I add a checked UserNotFound exception to a getUser db call, you can bet someone higher up the stack will do try catch Exception e, so now they're catching OutOfMemory and who knows what else.
- vips7L 3y agoOutOfMemoryError isn't an exception so they would not be catching it.
- spion 3y agoExceptions are not unsafe. That said, not modelling them in the type system is a mistake. But the model has to be useful - knowing what functions can or cannot throw is useful, knowing what they throw, less so. (They are safe because of try-finally / try-with-resources)
- bedobi 3y agoThey are unsafe because they invariably result in people either ignoring them or catching more than they should, or less than they should, and the compiler happily lets you do that, EVEN when you're using checked exceptions.
- AnimalMuppet 3y agoHow will the compiler let you catch less than you should when using checked exceptions?
- spion 3y agoThere is no should/shouldn't. If you don't have a specific error in mind and how to handle it, you shouldn't handle it. In practice most errors can be left unhandled all the way to e.g. server response, so this works quite fine. `try-with-resources` is typically awkwardly implemented unfortunately (defer is nicer, the new `using` keyword in JS is quite nice too)
- bedobi 3y agoI don't mean to be uncharitable but it seems like most exception advocates here don't really understand the case us functional error handling advocates are making. We don't advocate handling errors at every level. They're usually sent to the boundary layers of the system, eg the http resource layer, and folded there into http responses. The safety case we're making has nothing to do with try catch finally, it is about signatures and what callers are forced and allowed to do.
- kaba0 3y agoChecked exceptions are exactly analogous of Result/Either types. They are just built into the language with syntactic sugar, automatically unwrap by default (the most common operation), can be handled on as narrow or wide scope as needed (try-catch blocks), does the correct thing by default (bubbling up), and stores stack traces! In my book, if anything, they are much much better! Unfortunately they don’t have a flawless implementation, but hopefully languages with first-class effects will change that.
- titzer 3y ago> Checked exceptions are exactly analogous of Result/Either types. No, they aren't. They are not compositional. You may want to write `f(g())` but there's no way to write the parameter type of `f` to make this work (in Java). That's because checked exceptions are an "effect" that would require extending the Java type system.
- The_Colonel 3y ago> Safer because unlike exceptions, they force callers to handle all potential outcomes, but no more. Which is bad. In 99% of cases I have no specific error handling for the given problem. Just let the process crash or be handled by application server / framework. Representing these kinds of error conditions in the code which I have no interest in handling is just noise.
- titzer 3y agoSituations vary a lot. In general there's no guarantee that a far outer scope is going to know how to deal with errors from deep inside some nested calls except for some very general logic, like failing a whole operation.
- kaba0 3y agoEven more commonly, there is no meaningful error handling to be done at a layer above the call.
- bedobi 3y agoI don't mean to be uncharitable but it seems like most exception advocates here don't really understand the case us functional error handling advocates are making. We don't advocate handling errors at every level. They're usually sent to the boundary layers of the system, eg the http resource layer, and folded there into http responses.
- bedobi 3y agoThe problem with that is you lose all ability to understand or control what a given endpoint will return in any given situation > I have no specific error handling for the given problem then pass it on and decide what to do with it at the system boundary in the db: fun getUser(uuid: UUID) : Either<Error, User> middle layers: pass around the either, you can map, flatmap etc on it to chain it with other computations there then in the resource layer return user .fold({ error -> when(error) { is DbError -> HttpResponse.InternalServerError() is UserNotFoundError -> HttpResponse.NotFound } }, { user -> HttpResponse.ok(user) }) Then, at every layer, each method explicitly says in the method signature what it returns. There’s no need to look around each and every line of every method in every layer, or in the framework, or some global exception handler, to figure out what will happen. Developers can tell from a glance at the resource method what the endpoint will return for each outcome, in context. Things like being unable to communicate with the DB or not finding a user are not exceptional, they're entirely expectable when you're calling a db to look for a user and should be modeled accordingly.
- asddubs 3y agoYou mean something like C++ Optional, right? Those are nice, if the language supports generics.
- wvenable 3y ago> Safer because unlike exceptions, they force callers to handle all potential outcomes, but no more. That's what wrong with them. Callers, in almost all cases, do not know what to do exceptional outcomes. So you force programmers into a situation where they're dealing with things they shouldn't be dealing with.
- deleted 3y ago[deleted]