4 ms·
Well, what you're saying is exactly the reason why java called them Exceptions and not Errors (well, Error also exists, but it is generally reserved for very na
by simiones 6y ago
Well, what you're saying is exactly the reason why java called them Exceptions and not Errors (well, Error also exists, but it is generally reserved for very nasty problems).
Anyway, you can't just define the problem away, or else you end up with Go style error handling - that is exactly what a language with no built-in support for errors looks like.
Languages need to offer control flow mechanisms that allow you to separate common cases in the code from the uncommon cases. Especially since for most code, the uncommon cases appear deep in the bowels of an application, and the only way to handle them is to ask an actual human to handle them, usually on the opposite side of the application stack.
For a more reified implementation of this pattern, the Common Lisp Condition system is very interesting. It is similar to most Exception systems (code can raise a Condition, at which point a handler for that Condition is searched for up the call stack), but with one crucial difference: when raising a Condition, you can also specify Restarts - alternatives for how to proceed. Condition handlers can choose whether to cancel the execution flow and handle the condition with their own logic, OR they can choose to invoke one of the Restarts offered along the condition, based on their own logic. An example would be something like: Raise ConfigFileNotFound; Restarts: ContinueWithDefaults, Retry, ContinueWithOtherFile(newFileName). Then, a Condition handler (possibly a User) can choose to stop the application, or it can choose to continue with the defaults, or it can create the file with its own defaults and Retry, or it can try a different config file path.
This offers a lot of flexibility and has uses outside the idea of handling errors.
- enriquto 6y ago> Languages need to offer control flow mechanisms that allow you to separate common cases in the code from the uncommon cases. Strong disagree with this sentence. It represents the exact opposite of what I deem a good programming language. The difference between "common" and "uncommon" execution paths must be of no bearing. Moreover, this "likeliness" depends on the (unknown to the programmer) usage that the program will be put through. For all you know, the uncommon path may be the only one that will be ever traversed in all instances of your program. A correctly specified program must deal with all possible input conditions, and that includes missing files, inconsistent parameters, and lack of resources. A filename existing or not is the same boolean value as an integer being even or odd.
- creata 6y ago> The difference between "common" and "uncommon" execution paths must be of no bearing. Why? I think that a good programming language should help readers focus on the "essence" of an algorithm. Usually, handling stuff like memory allocation errors is just noise in that effort. Besides, most errors will usually be propagated to the caller, so I think the programming language should make that convenient, like Rust does with its ? syntax, and most languages do (perhaps too implicitly) with some concept of exceptions.
- simiones 6y agoCode is primarily meant to be read by humans. Humans can't focus on 30 things at once when reading code. A function that should take a list of strings and return a list of all the strings in the first list starting with 'A' will be harder to read if it must also handle allocation errors for the new list, because they are a completely different kind of concern. Even outside of programming, human thought often works exactly in terms of general cases and exceptions. It's just what comes naturally, and we shouldn't be fighting it in code. > The difference between "common" and "uncommon" execution paths must be of no bearing. Moreover, this "likeliness" depends on the (unknown to the programmer) usage that the program will be put through. For all you know, the uncommon path may be the only one that will be ever traversed in all instances of your program. As the designer of that program, I obviously know up to a very good degree of confidence what the usage of the program will be: I am designing my program for a particular use, by definition. Sure, I may not know exactly how flaky your network may be, but I know that this program only really works on networks that deliver more packets than they drop, that can read all of the bytes I wrote to disk back, at least most of the time, and so on. Or, perhaps I'm writing a program that is meant to run on very flaky networks (say, a device for wireless thunderstorm sensors): I expect that program to look very different from the Netflix app's networking code.
- enriquto 6y ago> I am designing my program for a particular use, by definition. No; this is bad engineering. You write a program to conform to a specification. In the specification, it says what must happen when a file does not exist, what must happen when there's not enough memory, etc. Then you write the specified behavior into code.
- chrismorgan 6y agoRust actually used to use conditions for its I/O errors, but they were removed as part of the “new runtime” project in 2013, for reasons that are dimmed in my memory by time, but I think they included: quite a lot of complexity, including in ways that had negative performance implications; lack of clarity about where errors could occur; lack of ability to distinguish between errors by source in the handler; more limited potential than hoped in ways that were probably connected to Rust’s ownership orientation; unfamiliarity to users (and Rust had already spent its weirdness budget); and that the actual power of conditions was almost entirely unused (I honestly don’t think I saw any production-like code use handlers for anything interesting, ever). You can still readily express the concept of conditions in Rust, but it’s no longer baked into the standard library.