5 ms·
I also prefer option 3 over option 4. It bothers me that the article recommends exceptions without seeming to understand their drawbacks (the major one: as soon
by jblow 11y ago
I also prefer option 3 over option 4. It bothers me that the article recommends exceptions without seeming to understand their drawbacks (the major one: as soon as you use exceptions you suddenly need to apply nonlocal reasoning everywhere in order to understand what your program will do at any point. They turn your program from a simple local thing into a complex nonlocal thing, which is not a good idea if you want to understand it well.)
This article seems not to have a lot in the way of new contribution; it is just parroting the oft-repeated idea that you need exceptions to pass "error information" up several layers of abstraction. But here is what I think about this:
1) In this age when we are realizing strong typing is a good idea, that hidden state is a bad idea, and that in general you should be very specific about what is going on, why are we even conceptualizing this as "error information"? Why instead, when we try to open a file, do we not return "all information you might need to know in the case of opening a file" (which includes what happened if it didn't open properly). As soon as you make that conceptual switch, all this hand-wringing goes away. It's a non-problem. You certainly shouldn't add heinous complications to your program to solve this non-problem.
1a) This conceptual change also helps disambiguate between what the article calls "hard errors" and "soft errors". In portions of the code where you have attempted an operation that might have failed, and you are not completely sure that it didn't fail, you have the full body of "what happened" information (it is a small struct or whatever). After the situation has been checked and you know it is exactly what you need to be, you may drop the other information and pass the raw file handle. At this point it is clear that these parts of the code should only be executed if the file handle is valid, and if that is not true, the programmer made an error. This is analogous to the situation with nullable and non-nullable pointers (in some languages you would even use the same mechanisms to deal with null pointers and invalid file handles, etc, but I am not sure this is really helpful.)
2) If one insists on not making the simplifying leap from (1), well, maybe the other problem is that you have so many layers. If you didn't have so much glue, your code would be simpler and easier to deal with, and it would run faster, and you wouldn't be so worried about needing to pass lots of context information up several layers between modules, because those situations don't really arise.
- barrkel 11y agoIn this age when we are realizing strong typing is a good idea, that hidden state is a bad idea, and that in general you should be very specific about what is going on Java tried to be typesafe about errors, about not obscuring the state, and it was a dreadful idea. The way code fails is a function of its implementation; communicating high quality error information is fundamentally an abstraction violation, but a strongly typed facade that forces you to rewrap all your error cases in some other type just obscures the underlying problem. Strongly typed error information also breaks composability. How do you write a 'map' function that can accept a callback, where the callback may want to communicate back one of several distinct kinds of error information? With unchecked exceptions, the way forward is clear. Without them, you either need to write a lot of boilerplate to unify your error types in some kind of container, or rely on subtyping to fit them into a single type and live with the lack of strong typing via another route. Dynamically typed programming languages improve usability over statically typed languages particularly when a precise description of the types used in the static case is difficult or tedious to express. Error information, and its intrinsic implementation-dependent content, is one of the strongest instances of this.
- dllthomas 11y agoYou could have checked exceptions with more flexibility than Java had, and it might work out well (or might break down for other reasons). "How do you write a 'map' function that can accept a callback, where the callback may want to communicate back one of several distinct kinds of error?" With Java style checked exceptions, you don't, and that's for sure a problem. However, one approach would be to say that map is polymorphic in the exceptions that may be raised from within it. But that set is not entirely unbounded - it is precisely the set that can be raised by its function argument, unioned with any that might be raised during traversal of its container argument.
- Animats 11y agoExceptions are hard to get right in language design. They're easier in a garbage-collected language like Python, because there's not so much need to release stuff. Most of the bad reputation of exceptions comes from C++, where the combination of exceptions, RAII, and having to release memory results in problems. Without exceptions, programs seem to develop too many "goto" statements. Go suffers in this way. Error handling in Rust seems to excessively complex, and hiding the complexity inside macros that do return statements is an ugly solution to the problem.