4 ms·
Always hated checked exceptions because there seems the basic assumption underlying them, that exceptions can usually be handled as near to the code throwing it
by warpspin 7y ago
Always hated checked exceptions because there seems the basic assumption underlying them, that exceptions can usually be handled as near to the code throwing it as possible, while in every real world project I ever was involved in, 90% of the possible exceptions usually had to be handled "far away".
So this means, you will have a whole bunch of re-throws, or exceptions wrapped in "higher design level" exceptions which you then throw to finally communicate the error condition to the proper component, polluting all signatures on the way with "throws".
Or you catch it as early as possible, wrap it in a RuntimeException, re-throw it unchecked and let the high level component properly handle it, effectively bypassing the "checked exceptions" system :D
Just my two cents, I know this might be a controversial position.
- dllthomas 7y agoI think the problem with checked exceptions was that we got a woefully limited language for talking about them. Checked exceptions with generics (and inference?) might be a very different experience. For instance, Java's checked exceptions break higher-order functions. A map function should be able to say "I can throw anything my argument can throw". Some wrapper functions might say "anything my argument can throw, minus FooException because I catch it, plus BarException because I might raise that myself."
- bedobi 7y agoWith functional error handling, you don't need neither checked nor unchecked exceptions, everything just returns objects, and the language and syntax used for talking about them is the same as the language and syntax used for everything else (map, filter, flatMap, fold etc etc).
- dllthomas 7y agoI was speaking to checked exceptions vs unchecked. "Exceptions at all" is a separate question. If we're going to dig into it, first we need to call out the occasional need to interrupt running pure code (eg. "the user hit ctrl-c"). As it can happen at any point, I think it's uncontroversial to say that return values are inappropriate? These are Haskell's "asynchronous exceptions", I think they do their job fairly well, and I don't know of a compelling exception-free alternative (although I'd be very interested to learn). So now we can restrict ourselves to "synchronous exceptions" - those raised because of the function we've called. I agree that we don't need them. They bring to the table early termination and transparent error propagation. There are contexts where these are desirable - if you disagree on that point let me know and we can dig in. In sufficiently expressive languages, we can do it ourselves - so that's plenty to show we don't need them. But we may want them. When we do it ourselves, we usually lack a way of telling the compiler which paths to privilege, and we often find some overhead compared with native exceptions. When exceptions are provided in the language, we get special syntax to distinguish them. You describe this as a drawback, but nonuniform syntax can help with legibility - if different things look different, I can more easily figure out what I'm looking at. Obviously there are tradeoffs here. Putting exceptions in the language also provides a distinction to the compiler in type checking, allowing different rules to apply. For instance, Java methods throw some set of exceptions, which get unioned (&c) in ways you can't speak about sets of types otherwise. Simply applying the ordinary value-level rules would be problematic (I hit this in Haskell sometimes). Ideally you can broaden your type rules to support what you need for all of it in one system, but it's not always clear they combine well. Even if it all can be made to fit, I could easily see a situation where assumptions need to be made for inference to work, and the most ergonomic assumptions are different for most values vs. bags of errors.
- bedobi 7y agoWith functional error handling, every method just returns an object. If the caller doesn't want to do anything with it, it can just pass it on somewhere else. Only once you actually care about filtering, mapping, flatMapping, folding etc do you actually need to handle the different cases. It's just so much better and easier to understand than exceptions! Doesn't require any special syntax or mental gymnastics to understand the execution path either.
- quelltext 7y agoI fail to see how this is different. Say you have a function f1 returning an Either a b value then another method f2 which calls f1 but doesn't handle the error case will simply flatMap or bind or whatever the language of choice is calling it and return yet another Either a b value until at some point up the call hierarchy someone actually decides to unwrap and if necessary do something with the error case. The function signatures have the same problem as the parent comment mentioned. flatMapping over an Either and returning the result is no different than adding "throws SomeException". Then Option types (since they were mentioned as well somewhere) are a bit of a different story all together. They make sense for describing absence/presence if values/results and are definitely useful. However they don't compete with Exceptions in any real way since they don't really work for error handling beyond the most basic cases (i.e. where handling the error somewhere doesn't matter/need to happen).