News for hackers. Stuff that matters.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by enriquto on Friday February 19, 2021 @05:42AM (#26191630)
> Error handling has been wrong since the beginning

100% this. The very concept of "error" is philosophically unsound. There are no errors; only conditions that you dislike. It is unfortunate that programming languages allow to express your emotional detachment to one of both cases of a branch. Nothing good can come from that.

I yearn for a language with no error handling nor exceptions. Just plain language constructs to deal with the state of the world around the program, without unnecessary emotional attachment to certain flow paths.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by simiones on Friday February 19, 2021 @08:12AM (#26192745)
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.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by chrismorgan on Friday February 19, 2021 @11:57AM (#26195097)
Rust 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.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by enriquto on Friday February 19, 2021 @08:35AM (#26192965)
> 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.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by simiones on Friday February 19, 2021 @09:20AM (#26193400)
Code 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.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by enriquto on Friday February 19, 2021 @09:26AM (#26193467)
> 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.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by adwn on Friday February 19, 2021 @11:18AM (#26194668)
> 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.

I've never, ever seen a specification which mentions file-not-found or out-of-memory errors. The "specifications" I get from my clients are more like (paraphrased):

> Listen, adwn, I have all those files in a shitty, undocumented file format designed by another group in another building, could you please add an import button for them? Oh, and can I have it by Friday? Thanks, you're the best!

I'm exaggerating only slightly. And then I go and try to make sense of what they need, and I implement error handling on top of it.

Your specifications tell you exactly how to deal with file-not-found situations? You lucky bastard ;-)

Re:Where Everything Went Wrong: Error Handling and Error Mes... by junke on Friday February 19, 2021 @09:43AM (#26193640)
The specification and boundary conditions directly comes from an analysis of the expected usage. Except for some all-purpose libraries you don't develop in a vacuum (assuming you don't work for Roomba).
Re:Where Everything Went Wrong: Error Handling and Error Mes... by enriquto on Friday February 19, 2021 @10:01AM (#26193868)
But design and coding are different steps, best kept separate. When you are designing, I agree with you, the expected usage is very important. But in the design step the particular language mechanism for dealing with conditions does not matter. Once you get a specification to program to, all input conditions can be treated as equal. That is, unless you need to optimize heavily by biasing your execution path for a certain percentage of input cases (which should be clearly described in the specification).
Re:Where Everything Went Wrong: Error Handling and Error Mes... by junke on Friday February 19, 2021 @10:10AM (#26193974)
> Once you get a specification to program to, all input conditions can be treated as equal.

And if the spec says "try downloading the file 3 times at 5 seconds interval; if that fails, give up the update", I am free to implement it however I want (?) unless I missing your point.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by enriquto on Friday February 19, 2021 @12:14PM (#26195289)
sure! for example, in your case you only need a for loop and an if/else statement
Re:Where Everything Went Wrong: Error Handling and Error Mes... by coderdd on Friday February 19, 2021 @05:07PM (#26198652)
Ideally yes, but most of the time I find I have no idea about what I want to do. Idea leads to code, code reveals constraints, those lead to other ideas. If we could specify things, code writing AI would be easy. But most of the time, we just have no idea.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by kazinator on Friday February 19, 2021 @04:44PM (#26198416)
You're forgetting about errors that happen due to programming mistakes.

I've never seen an end-user application accompanied by a written a specification about what happens if there is a run-time error due to a mistake in the program.

Your original thesis is something like that "there are not errors, only conditions we care about"; but then this subsequent argument is disappointingly about environmental conditions only.

If I have a program that takes two run-time inputs and divides them, I may or may not specify what happens if the denominator is zero. I could explicitly specify that the behavior is not defined; my program can do anything. (In practice, any decent processor will catch it via some numeric exception.)

If my program generates a division by zero due to a programming bug, where no such division is implied by the specification of what the program does on the inputs which it is given, that's something we don't bother specifying. Not usually.

Anything can happen if the program has a mistake. A document listing all possible mistakes and how the program will react will not only be intractably long, but in the course of writing it, you would just inspect that the program doesn't have those mistakes. In the end, you'd be left with a document describing mistakes that the program doesn't actually contain, while it remains vulnerable to unknown mistakes.

We might be required to have a strategy for the software to deal with its own faults in a general way, if we are working on something safety-critical. Such as that no matter how the program might fail, the system as a whole will revert to a safe state.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by jdmichal on Friday February 19, 2021 @12:39PM (#26195609)
You're picking nits that don't mean anything. If a specification exists outside of the definition of the program -- aka the code -- then it is only useful as a point of comparison between intent and implementation.

Most software in business is written to implement a process. And business processes are exactly defined as a common workflow with edge cases and exception handling. Not all software is like this, but a hell of a lot of it is. And those business processes are not always complete, and they are typically changing over time as well. Any idea of a global specification outside of the process that the code is actually implementing becomes useless pretty fast.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by simiones on Friday February 19, 2021 @10:58AM (#26194450)
What makes the specification superior in principle to the program?

For some domains, you can write a specification that has value in itself, it will be shorter and easier to review.

For other domains, the program itself is the best specification for the desired behavior. There is nothing special about a specification that makes it inherently more correct than a program. The specification may be large enough and detailed enough that it is essentially just as hard to review as the program output. The specification can have subtle bugs, just like a program. Even worse, the specification itself is harder to test, especially for corner cases.

It can be very easy to produce an excellent specification that solves a different problem than expected, especially for complex problems with many moving parts and many possible use-cases.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by junke on Friday February 19, 2021 @09:28AM (#26193485)
Strongly disagree with your disagreement. Programming features help you organize your code. The fact that you actually have a robust software or not with what the language offers is another problem.

When people say "errors are values", I say: yes, but they are a special kind of values. The same way floats are special (signaling NaNs), or that bools are special (short-circuit operators). It is great to have special support for values which need to be used in a certain way (here: don't lose any error, bubble up by default).

It is great to have the opportunity (not the obligation) to use features for separation of concerns. This makes it a bit like an aspect-oriented approach where normal code emits errors and error handling code elsewhere knows how to handle/restart them. If you language allows it, it makes also sense also to decouple logging (tracing functions) or data-access permissions (postgresql row level security).

In the C code I maintain at work, everything is here in plain view, logging, error handling, etc., and I am not complaining, but more recently we tend to use other languages or code generation a lot (think of something like protobuf) because it makes code a lot easier to maintain.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by enriquto on Friday February 19, 2021 @10:04AM (#26193898)
Thanks for explaining so clearly your view. I'm totally biased towards programming practices that make execution paths 100% explicit and local. Yet it is interesting to understand the other point of view (and somewhat liberating, to be honest).
Re:Where Everything Went Wrong: Error Handling and Error Mes... by junke on Friday February 19, 2021 @10:12AM (#26193990)
Thanks for the kind remarks (we are all biased :))
Re:Where Everything Went Wrong: Error Handling and Error Mes... by creata on Friday February 19, 2021 @08:58AM (#26193173)
> 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.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by blacktriangle on Friday February 19, 2021 @09:46AM (#26193675)
Maybe CL got this right with the condition/restart system? In addition to their utility for experimental programming, conditions work really well for handling errors programatically.

The general problem seems to be something weird happening far down in the system, for which the correct way forward is dependent on how we got there. This situation can't be handled where the issue happened since that would break encapsulation, but unrolling the stack all the way up blows away the context that is necessary to understand how to move forward. I don't see how a system that either unwinds the stacks (throwing errors) or tries to encapsulate the state through the system (Either) would truly solve the issue.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by enriquto on Friday February 19, 2021 @12:17PM (#26195320)
computers don't do "weird" things. If you think this is the case it means that the interface you are using is incomplete or not well-specified.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by finder83 on Friday February 19, 2021 @01:44PM (#26196417)
I agree, for the actual handling part, I think CL's error system is the best I've seen, and I'm surprised that more languages don't implement a similar system.

It doesn't solve the issue of forcing programmers to handle errors..but then, CL doesn't care a lot about hand holding.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by masklinn on Friday February 19, 2021 @01:55PM (#26196540)
One advantage of condition systems is that the caller gets to decide whether the condition is even an error. Though the restarts are still under the control of the callee.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by kazinator on Friday February 19, 2021 @04:36PM (#26198326)
Well, no, there are errors, and then there are environmental situations (if we ignore, for a minute, hardware malfunctions).

like "file not found", "disk full". Something in between like "out of memory".

Misusing an object as the wrong type, or division by zero, or accessing missing memory are errors. These usually point to defects in the program, or in some cases defects in the program's defense against bad inputs.

There are related errors are the hardware level, like numeric exceptions and bus errors. Without these, machine-language programs just lock up or produce garbage results.

Situations like "file not found" or "host not reachable" are usually environmental conditions, and not internal problems. There are ways in which they can be internal problems; two modules in a program might be related in such a way that one prepares a file that the other expects to exist, and there can be some bug in that.

Then there are situations like "out of memory" or "disk full" are somewhere in between. All the operands to the calculation exist and are well-defined, the operation is correct, but just there is a resource issue. In pure computing theory, these situations are the heart of the distinction between simulating a Turing machine by means of a finite tape, and the Turing machine abstraction, as such, which has unlimited tape.

In summary, there are basically three categories: errors (programming mistakes), environmental situations (issues in presentation of the inputs to the program, like asking it to operate on a file that doesn't exist, or connect to a host that is down) and resource exhaustions (out of some type of storage).

There is no emotional attachment in this classification whatsoever; and there is value in their separation.

Then there is another category of errors: faults in the hardware. The foregoing assumes that there are no malfunctions in the hardware; no cosmic rays that collide with silicon, flipping the values of bits and such. In some situations, you need a demonstrated strategy for these. Like what if an error happens in a DRAM chip that is not detected and corrected.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by Kaze404 on Friday February 19, 2021 @06:26AM (#26191944)
Isn't that what the Result type on Rust is? Sure, one of the branches is still called Error but it's just a plain language construct (a sum type you can write yourself).
Re:Where Everything Went Wrong: Error Handling and Error Mes... by creata on Friday February 19, 2021 @06:36AM (#26192027)
Yep, although I guess the Try trait[0] and the corresponding ? operator count as a special error handling language construct.

[0]: https://doc.rust-lang.org/std/ops/trait.Try.html

Re:Where Everything Went Wrong: Error Handling and Error Mes... by ymbeld on Friday February 19, 2021 @12:35PM (#26195563)
The Result type is specifically designed to store value-or-error. One may use it diffently but that’s what it’s made for.

The library designers had a choice between making a generic this-or-that type or a value-or-error type and they chose the latter because they thought that that would be the common this-or-that use-case.

Even Haskell’s more generic-sounding “Either” type is made for the same purpose: the “right” (as in correct) variant is the value while the left side is the error, by convention.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by zanellato19 on Sunday February 21, 2021 @09:17PM (#26220000)
While Result is designed for that, it was built with an Enum (see here: https://doc.rust-lang.org/src/core/result.rs.html#241-251)

So you can also build an enum that doesn't have any explicit mention of which is the right path

Re:Where Everything Went Wrong: Error Handling and Error Mes... by Kaze404 on Friday February 19, 2021 @01:06PM (#26195948)
I don't understand what the problem is, in that case. Is it just the fact that it's called an Error instead of something more generic? Not trying to sound dismissive, just trying to understand if there's something I'm missing.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by ymbeld on Friday February 19, 2021 @01:20PM (#26196129)
The problem? You would have to ask the poster that you initially replied to.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by Kaze404 on Friday February 19, 2021 @01:38PM (#26196344)
Fair enough :)
Re:Where Everything Went Wrong: Error Handling and Error Mes... by hacker_9 on Friday February 19, 2021 @05:49AM (#26191678)
The issue is we have to pre bake decisions into the application, and until the application is literally an AI this is as good as it gets. When building a program 'errors' occur all the time, but we make design decisions to handle them because we can identify the right path forward in the given context.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by randyrand on Friday February 19, 2021 @12:52PM (#26195757)
A better word might have been “failure”.

Something is a failure when it fails to do what it says it does.

openfile();

If openfile() does not open a file, then it failed.

This terminology makes it clear we are not just bubbling up errors. It’s the function itself which failed.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by xxpor on Friday February 19, 2021 @09:52PM (#26201059)
I'm confused. Isn't that just C? There's no errors in C, or exceptions. The only exceptions are interrupts/signals, but those aren't part of the language, but the machine or OS.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by enriquto on Saturday February 20, 2021 @08:21AM (#26203906)
there's nothing to be confused about, C is one of the few languages that gets it right (the base language... the stdlib is a different story)
Re:Where Everything Went Wrong: Error Handling and Error Mes... by golergka on Friday February 19, 2021 @10:51AM (#26194384)
> There are no errors; only conditions that you dislike.

This is true in mathematical sense, but unhelpful in UX sense.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by Ace17 on Friday February 19, 2021 @01:30PM (#26196256)
Quite the opposite IMHO : when your program interacts with a user, you cannot panic the program each time something unexpected happens. Here are some examples of unexpected conditions:

- "Null pointer dereference"

- "Out of memory"

- "Disk is full"

- "File does not exist"

- "File does not exist in cache"

- "File exists but is corrupt"

- "Access denied"

- "Connection reset by peer"

It's pretty obvious that all of the above is generally unwanted most of the time.

However, putting them all in the same bag labeled "error", and forcing them to be treated the same way might be counterproductive. Sometimes you might want to panic. Sometimes you might want to retry. Sometimes you might want to ignore!

Now, if your program isn't interactive (such as a compiler), halting on any error might be a choice. But you still have to provide contextualized and accurate error messages, which is easy for the case "File does not exist", and a lot less easy for the case "Out of range index".

Re:Where Everything Went Wrong: Error Handling and Error Mes... by carapace on Friday February 19, 2021 @06:57AM (#26192166)
Aye. throw/raise is GOTO.

Forth?

Re:Where Everything Went Wrong: Error Handling and Error Mes... by enriquto on Friday February 19, 2021 @07:04AM (#26192212)
> throw/raise is GOTO.

Worse, it's COME FROM!

https://en.wikipedia.org/wiki/COMEFROM

Re:Where Everything Went Wrong: Error Handling and Error Mes... by simiones on Friday February 19, 2021 @07:56AM (#26192621)
To be fair, throw is GOTO. Catch() is COMEFROM :).
Re:Where Everything Went Wrong: Error Handling and Error Mes... by carapace on Friday February 19, 2021 @01:32PM (#26196278)
Ah, my peeps! I love you guys.

enriquto when you talk about "a language with no error handling nor exceptions" it reminds me of a crazy idea i was toying with: what if you made all "exceptions" require handlers, e.g. what if every divide had to be accompanied by code to deal with divide-by-zero? In other words, DIV(X, Y, Foo) would be (X/Y if Y != 0 else Foo()) And so on...

Re:Where Everything Went Wrong: Error Handling and Error Mes... by heja2009 on Friday February 19, 2021 @04:16PM (#26198128)
that's the way some embedded control systems are designed. with some (many) errors (can't read/write) being fatal and shutting down the system after an alarm is sent.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by Ace17 on Friday February 19, 2021 @01:22PM (#26196157)
Technically, `throw` is `goto somewhere`.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by marcosdumay on Friday February 19, 2021 @02:34PM (#26197058)
I guess gettout wasn't well accepted by the committees of the time.