News for hackers. Stuff that matters.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by kstenerud on Friday February 19, 2021 @04:49AM (#26191291)
Error handling has been wrong since the beginning, and has continued to be wrong ever since.

First, we had error codes. Except these were wrong because people forget all the time to check them.

Then we had exceptions, which solved the problem of people forgetting to check by crashing the app.

Then the Java team got the bright idea to have checked exceptions, which at first helped to mitigate crashes from uncaught exception, but caused an explosion in thrown exception signatures, culminating in "catch Throwable". Back to square one.

Then we got multi-return error objects, maybes, panics, and all sorts of bright ideas that fail to understand the basic premises of errors:

Any error system that relies upon developer discipline will fail because errors will be missed.

Any error system that handles all errors the same way will fail because there are some errors we can ignore, and some errors we must not ignore. And what's ignorable/retriable to one project is not ignorable/retriable to another.

Attempting to get the complete set of error types that any given call may raise is a fool's errand because of the halting problem it eventually invokes. Forcing people to provide such a list results in the Java problem for the same reason.

It's a hard problem, which is why no one has solved it yet.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by brundolf on Friday February 19, 2021 @10:49AM (#26194364)
It's not perfect, but I think Rust's approach is the best one yet.

> Any error system that handles all errors the same way will fail because there are some errors we can ignore, and some errors we must not ignore.

Rust has separate categories for these two things. Panics cannot be handled, which pushes the author to use them sparingly. Results must be handled (or explicitly elevated to panics, in a way that's easy to track down later).

The main weakness of this system, imo, is that the thrower, not the caller, decides whether or not a given error must be handled, and sometimes the answer is "it depends on what the consumer is doing".

That said, the powerful (mainly, no-implicit-null...) type systems that are starting to become prevalent prevent a lot of cases that would have been errors 20 years ago from existing in the first place. In modern typed languages an error is almost always a genuine IO/environment anomaly, which means there are a lot fewer of them to handle in the first place.

Edit: The other big weakness of Rust's system, as mentioned in the article, is the jumble of all the different disjoint Error types. I think the core issue here is that Rust doesn't have ad-hoc union types. I.e. if you want to say "this value is of type X or Y or Z", you have to declare a new enum somewhere, and embed your possible types in each branch, and that's a lot of ceremony at each point where you bubble up a different possible type of error. I understand why Rust's enums are static, though, I wonder if there's a place for some sort of heap-allocated ("dyn?") solution that allows any combination of possible types.

Edit 2: Actually this probably wouldn't be possible because there would be no way to distinguish the union, because Rust carries no type info at runtime. Maybe there could be syntax sugar for anonymous enum types declared inline?

Re:Where Everything Went Wrong: Error Handling and Error Mes... by brazzy on Friday February 19, 2021 @11:32AM (#26194823)
>The main weakness of this system, imo, is that the thrower, not the caller, decides whether or not a given error must be handled, and sometimes the answer is "it depends on what the consumer is doing".

So... exactly the same problem that Java's checked exceptions have?

Re:Where Everything Went Wrong: Error Handling and Error Mes... by masklinn on Friday February 19, 2021 @01:49PM (#26196464)
> So... exactly the same problem that Java's checked exceptions have?

Well yes, but also no because Java's checked exceptions have issues which go way beyond that. Hell I'd say this is not an issue because of the other issues.

In Rust the thrower decides whether the error must be handled, but the default is "yes", and it's the overwhelmingly common decision. Panic is the exception (or multiple APIs are provided). Rust also provides easy way to convert errors to panics, and syntactic sugar to convert between error types.

In Java, the classifications were much less stark, and thus more arbitrary, some exceptions were checked, others were not, but there was little rhythm or reason about it.

Furthermore, there was little to no ability to abstract over checked exception, and the statement-oriented nature of the language made both converting checked to unchecked or converting between different checked exception types a chore.

The verbosity and horrible ergonomics of java's checked exceptions is where the problem always lied, really, with the seemingly arbitrary nature of the classification coming in at third.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by zaphar on Friday February 19, 2021 @02:43PM (#26197173)
Errors are a part of the API. I'm not sure why we keep thinking we need to treat them differently. We don't complain when we have make decisions based on or transform data that is passed to us from a function or method. We just handle the data. Errors are just more data.

The correct thing here is to correctly model your Error domain and map errors you don't control to Errors that you do. In the Java Checked Exception case the Throws clause only grows because everyone is trying to ignore the errors and make somebody else handle it. I would argue that in Rust the explosion of different generic error handling mechanisms is also laziness and a desire to not handle the other half of the API's you are handling.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by danhau on Saturday February 20, 2021 @12:37PM (#26206048)
Thank you. Couldn't agree more.

It seems programmers want errors to handle themselves. It's like they want errors to automagically hook themselves into the entire code base and do "the right thing". Or maybe in other words: programmers mistake errors-as-additional-data, with errors-as-automatic-runtime-behaviour.

Am I crazy?

Re:Where Everything Went Wrong: Error Handling and Error Mes... by dralley on Friday February 19, 2021 @01:47PM (#26196446)
Except more ergonomic and less verbose, usually.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by caffeine on Friday February 19, 2021 @11:40AM (#26194895)
Also, panics can be caught, and now that ppl write a lot of async code, many panics do get caught by default. So it's just a Java RuntimeException now, buried deep inside the dependency tree layers down..
Re:Where Everything Went Wrong: Error Handling and Error Mes... by mlindner on Friday February 19, 2021 @11:01AM (#26194488)
One of the biggest issue with Rust’s panics is there’s many times when you must never panic. For example in an OS, when trying to save your crucial data to disk, in real-time code where panicking would maybe kill someone in the real world, etc
Re:Where Everything Went Wrong: Error Handling and Error Mes... by kibwen on Friday February 19, 2021 @11:45AM (#26194951)
I don't really understand this criticism, because there's no good alternative. Every language is capable of producing invalid states that the programmer did not intend; consider `x / user_input()`. (Unless literally every possible invariant of the program is expressed in the type system, which is not something that we have figured out how to do at scale and not something that even the most type-heavy of the popular languages come anywhere close to.) And once you're in an invalid/unanticipated state, you either crash or you don't. Of the two options, the latter is far worse (On Error Resume Next, anyone?), and is just as likely to lose data/get someone killed.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by masklinn on Friday February 19, 2021 @01:53PM (#26196521)
> I don't really understand this criticism, because there's no good alternative.

There's the option of surfacing the panic-ability of a function in the same way the constness is surfaced, which would allow some subsets of the code to ensure they won't call a possibly-panicing thing, even at the cost of convenience.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by a1369209993 on Friday February 19, 2021 @08:40PM (#26200570)
In the cases where the problem is actually panicing (ie, a non-total function) rather than your choice of core-dumping code, lack of panic-ability is strictly insufficient - `while(1){/*busy-loop*/}` is exactly as bad as `panic();`, and what you actually need is not "must never panic", it's "must make forward progress within some (not necessarily rigorously defined but) small amount of time". That doesn't have anything to do with panic besides that `panic` would be declared as `fn(string)->noreturn[takes_upto: (infinity * seconds)];`, rather than just `fn(string)->noreturn`. (And really, noreturn ought to be enough to infer that on its own.)
Re:Where Everything Went Wrong: Error Handling and Error Mes... by masklinn on Saturday February 20, 2021 @02:50AM (#26202413)
> lack of panic-ability is strictly insufficient

Sure. It is also by far the largest risk you face, so would solve a good 4 or 5 nines if the issue.

> And really, noreturn ought to be enough to infer that on its own.

noreturn only works for functions which never return. Most panicking functions do return, just not all the time.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by a1369209993 on Saturday February 20, 2021 @04:19PM (#26208086)
> noreturn only works for functions which never return. Most panicking functions do return, just not all the time.

Sorry, I meant that any function that was noreturn could be inferred to take up to forever to return (since it always takes forever to return, since it never returns). Then, any function that calls such a function (in a non-DCEed position) can be inferred to also take up to forever to return (even though it might take less than that), because some possible code path through it (it's up to X amount of time to return) takes forever due calling a function that takes forever.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by masklinn on Sunday February 21, 2021 @09:58AM (#26213884)
That is true, although I don't know how helpful that is aside from checking that the annotation is not broken? Much like `const`, the annotation would have to be opt-in either way:

* Rust doesn't infer function types (on purpose)

* like const (or Copy), the removal of the flag is a breaking change, so it should not happen implicitly

So functions would necessarily have to be explicitly marked as e.g. `convergent` or whatever. And while callers of `!`-functions would be easy to check for, I don't think rustc is currently smart enough to infer the totality loops in general.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by a1369209993 on Sunday February 21, 2021 @01:46PM (#26215968)
> Rust doesn't infer function types (on purpose)

> I don't think rustc is currently smart enough to infer the totality loops in general.

Well, yes; we're necessarily talking about changes to the language (or, equivalently, possible different languages).

> while callers of `!`-functions would be easy to check for, [other problems wouldn't]

That was my point, thanks: "when trying to save your crucial data to disk, in real-time code where panicking would maybe kill someone in the real world, etc", checking for the easy-to-check-for problems doesn't actually suffice to prevent disasters.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by mlindner on Friday February 19, 2021 @08:07PM (#26200312)
So you want your kernel to panic whenever it runs out of memory?
Re:Where Everything Went Wrong: Error Handling and Error Mes... by xxpor on Friday February 19, 2021 @09:46PM (#26201017)
Yes, for servers! Hard failures are much easier to deal with than soft-not-dead-but-still-useless states.

echo 1 > proc/sys/vm/panic_on_oom

Re:Where Everything Went Wrong: Error Handling and Error Mes... by mlindner on Sunday February 21, 2021 @08:57PM (#26219820)
Interesting. The company I just left sells a load balancer/application server/do-everything-box and one of the rules of the coding is that it must never crash. Even out of memory errors need to be recovered from and we shut off the kernel feature that makes malloc not actually malloc memory.
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.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by samatman on Friday February 19, 2021 @02:22PM (#26196906)
> It's a hard problem, which is why no one has solved it yet.

On the contrary, the Common Lisp condition/restart system solves it, and it's maddening that this hasn't been adopted anywhere else.

An exceptional state signals a condition, and without unwinding the stack, looks for a handler for that condition, which can, among other things, restart the computation from a lower stack frame. In development mode, it defaults to dropping into the debugger/repl; in release mode, an unhandled condition panics.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by Ygg2 on Friday February 19, 2021 @04:53AM (#26191317)
> It's a hard problem, which is why no one has solved it yet.

The way you described it seems not so much as hard problem. More like an impossible one.

That said. Perhaps error handling isn't one problem at all, but several problems masquerading as one. In which case Rust approach makes much more sense.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by brazzy on Friday February 19, 2021 @11:30AM (#26194795)
The article isn't really about any of that though. It's about a much simpler problem: how to produce error output that is useful to developers looking to fix a bug.

And I'm no Rust developer, but it looks to me like it basically demonstrates how Rust is an abject failure in that regard. The developer has to jump through lots of nonobvious hoops and choose between competing libraries to get anything useful.

Contrast that with Java, where any uncaught or logged exception prints a detailed stack trace and (usually) one or more useful error messages, by default, since day one.

This is a problem with very good known solutions, yet Rust seems to fail hard.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by kibwen on Friday February 19, 2021 @12:04PM (#26195182)
> Contrast that with Java, where any uncaught or logged exception prints a detailed stack trace and (usually) one or more useful error messages, by default, since day one.

I think you're misunderstanding what the OP is trying to show here. If all you want is a backtrace, then Rust supports that out of the box. Here's an approximation of the full error message that you would see from the first example in the post:

    thread 'main' panicked at 'dumping failed', src/main.rs:2:5
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
The OP has omitted the next line, which tells you how to receive a backtrace. Backtraces are off by default in Rust because, unlike Java, Rust has a lightweight, C-style runtime that tries not to impose any runtime cost that the programmer did not ask for.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by ymbeld on Friday February 19, 2021 @12:38PM (#26195606)
I don’t get why this is off by default for debug builds though. Maybe that’s a separate concern.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by alpaca128 on Friday February 19, 2021 @01:08PM (#26195980)
In my experience enabling the backtrace is necessary in very few cases. I've defined a bash alias to save me a little typing in those situations. As it can be turned on persistently by setting a single environment variable I'd say it's a good middle ground.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by barrkel on Friday February 19, 2021 @01:09PM (#26195992)
In other compiled languages, stack traces can be supported by looking up code references on the stack in a map of executable addresses to source code locations.

Even in the absence of stack frames, the mere contents of the stack with lookups where possible is really useful, and usually more than enough.

The cost is a little code to do lookups at runtime, and making the mapping data available (typically compressed and embedded in the executable).

(Note that this isn't stack unwinding or exceptions. This is backtraces. Rust, like most languages which embed errors in return values, makes the developer do the stack unwinding manually.)

Re:Where Everything Went Wrong: Error Handling and Error Mes... by _ek94 on Friday February 19, 2021 @02:36PM (#26197083)
That’s exactly how rust generates backtraces though. They just are not typically attached to errors.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by h0l0gr4ph1c on Friday February 19, 2021 @12:13PM (#26195272)
> And I'm no Rust developer, but it looks to me like it basically demonstrates how Rust is an abject failure in that regard. The developer has to jump through lots of nonobvious hoops and choose between competing libraries to get anything useful.

As a rust developer, there are other ways to skin a cat, or an Error return as it were. For the std::net library (I've been using it when dealing with tcp/udp sockets), when a socket returns an Error, it uses std::io::Error [1]. This is basically a struct that implements the display and Error traits, it stores an enum inside of the types of errors that you may want / need to ignore/ handle. You don't technically need to use libs to do anything error related.

This code has been round since Rust 1.0. Imho, this is kinda how Rust should tell people to make/handle errors. I like it. But I also have found rust enums to be extremely powerful in a lot of use cases.

[1] https://doc.rust-lang.org/std/io/struct.Error.html

Re:Where Everything Went Wrong: Error Handling and Error Mes... by jdmichal on Friday February 19, 2021 @12:26PM (#26195445)
I'd like to just point out that Java didn't have exception chaining until 1.4. And suppressed exceptions were added in 1.7 along with the try-with-resources construct. I know 1.4 was a long time ago, but it was 4 years after 1.2, which is what I typically consider the start of Java becoming a dominant language.

I'm only posting this because I find a lot of people forgetting that Java has had a very long history at this point. And that many of the things we take for granted in the language today did not always exist. I still remember when generics were released. Get off my lawn.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by brazzy on Friday February 19, 2021 @12:42PM (#26195660)
Oh, I've been around since the 1.2 days as well. Somehow, exception chaining didn't register as a big change when it happened. But yeah, incremental improvements have been pretty important as well.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by jdmichal on Friday February 19, 2021 @02:10PM (#26196736)
I agree regarding the chaining. I don't even remember thinking twice about it when it was introduced. But the ability to transform exception types without losing the original context and stack trace is actually a pretty big deal!
Re:Where Everything Went Wrong: Error Handling and Error Mes... by ymbeld on Friday February 19, 2021 @12:30PM (#26195494)
It took Java until Java SE 14 (last year) to produce NPE stacktraces which actually tell you which variable was null. At least in Rust third parties could have created a library to remedy a similar situation.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by jdmichal on Friday February 19, 2021 @02:14PM (#26196780)
For whatever reason, it look a long time for parameters and locals to have a name in addition to a slot and a type in the JVM bytecode. Before that data was introduced, it was an impossible task. Also why things like de-/serialization frameworks required annotations on parameters duplicating the parameter name.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by mhh__ on Friday February 19, 2021 @04:58AM (#26191347)
The only system I think should completely go is Exceptions except in the case of termination or absolutely catastrophic failure - this isn't really about programming but rather that the implementation is a total pain, the compiler struggles to optimize them, and even better they make quite a few safety analyses like borrow checking very difficult because the control flow graph basically explodes when you start considering exceptional control flow - even on a basic block
Re:Where Everything Went Wrong: Error Handling and Error Mes... by simiones on Friday February 19, 2021 @05:27AM (#26191515)
In terms of control flow, this:

  try {
    someCall();
  } catch (ErrorICanHandle err) {
    //handle
  }
perfectly equivalent to this:

  err = someCall();
  if canHandle(err) {
    //do something
  } else {
    return err
  }
I don't see why so many people think exceptions make code harder to analyze. In my opinion, it is errors themselves that make code hard to analyze, regardless of implementation strategy.

The only difference between exceptions and error returns/error codes is in human readability: one makes the bubbling behavior explicit, which clutters the code but makes it obvious; the other makes the bubbling behavior implicit, which keeps the code cleaner, but also less obvious.

After writing professionally in both languages with Exceptions (Java/C#/Python) and in Go, I much prefer Exceptions to error values/codes, but I can appreciate that it may be different in other domains or for other people.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by xpressvideoz on Friday February 19, 2021 @05:41AM (#26191622)
I mean, Go's error handling is one of the worst implementations of the "error code" idea, so it is a bit unfair to disregard the idea just because one particular implementation is bad. You should try Rust or Swift, at least the error handling part, to fully appreciate what a good error code implementation could be.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by simiones on Friday February 19, 2021 @10:12AM (#26193992)
I'd love to when I get a chance. I did see that Rust seems to favor a ? macro that seems to make error codes behave essentially like exceptions, so I am curious to try it out at some point and see if that ends up being any different from exceptions in practice.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by sensitive-ears on Friday February 19, 2021 @07:11PM (#26199818)
The "?" operator doesn't turn them into exceptions, it's just a "return-early-if-error" shortcut. The main difference being that the caller still has to handle the error of a function using "?". (even if it's by punting further up the call stack with more "?", which you could argue is exactly how exceptions work, but it is at least explicit in what functions can fail and which don't)
Re:Where Everything Went Wrong: Error Handling and Error Mes... by simiones on Monday February 22, 2021 @02:52PM (#26229648)
> even if it's by punting further up the call stack with more "?"

Yes, this is exactly what I meant.

> it is at least explicit in what functions can fail and which don't

So is Java with exceptions (at least as long as developers are even slightly disciplined).

Re:Where Everything Went Wrong: Error Handling and Error Mes... by sensitive-ears on Monday March 01, 2021 @06:12PM (#26309797)
Yeah, I think java had the right idea, but the ergonomics didn't turn out great, so they didn't get the buy in they'd hoped for. Having both checked and unchecked exceptions (ie, NPEs) meant it doesn't offer the same safety guarantees as something like rust does. The implementation of exceptions can also have some implications for the runtime, whereas the enum method of error handling allows you to run in very minimal environments and easily see what's going on. (Exceptions could be implemented this way under the hood, but they often aren't)
Re:Where Everything Went Wrong: Error Handling and Error Mes... by BlackFly on Friday February 19, 2021 @12:48PM (#26195720)
Exceptions are harder to analyze because they create scopes from which you are already "handling" errors, but if you add code to the scope that throws a new error of that type your handler may not actually be able to handle it.

  try {
    someCall();
    someOtherCall();
  } catch (ErrorType err) {
    //handle
  }
Which method throws? If they both throw ErrorType but one of them cannot be handled, then you have to put additional logic into the catch and rethrow.

This is a common source of faulty error handling.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by simiones on Friday February 19, 2021 @01:04PM (#26195925)
Yes, this is an easier mistake to make with exceptions than with error values. Still, the fix is to do exactly the same thing as in the error values case:

  try {
    someCall()
  } catch (ErrorType err) {
    //handle one way
  }
  try {
    someOtherCall()
  } catch (ErrorType err) {
    //handle another way
  }
It's no less verbose than the error values way, though again, it is easier to make the mistake in the first place.

I'm not trying to claim that exceptions are ultimately better than error values, just that the difference isn't so much "non-local control flow" as it is explicitness vs implicitness. Exceptions are implicit, error codes are explicit. Both have benefits and drawbacks.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by gmfawcett on Friday February 19, 2021 @10:44AM (#26194313)
Exceptions make code harder to analyze because they implement non-local control flow. Your two example statements aren't really equivalent. `ErrorICanHandle` may be a kind of exception that the `someCall` function has no idea about whatsoever -- it could be an exception propagated from much farther down the call stack -- while your if statement assumes the error was explicitly returned from the function.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by simiones on Friday February 19, 2021 @11:23AM (#26194729)
In an exception-based language, assuming all functions potentially throw, code like this:

  someCall()
is equivalent to code in a language without exceptions that looks like this:

  err = someCall()
  if err {
    return err
  }
(ignoring for a moment non-error returns, which don't change the point significantly)

Having functions that are guaranteed not to throw / return errors doesn't significantly change this shape.

So basically it's no more non-local than the code you'll end up writing with error codes, in most cases, any way. The only difference is implicit vs explicit.

> `ErrorICanHandle` may be a kind of exception that the `someCall` function has no idea about whatsoever -- it could be an exception propagated from much farther down the call stack -- while your if statement assumes the error was explicitly returned from the function.

someCall doesn't need to know about the meaning of the error in either case. It only needs to propagate any error it receives from any functions it calls (that it can't handle itself). Think about the function I wrote myself: did it need to know the precise type of err? Nope - it just needed to know that it implements some kind of Error interface (which may be as simple as being a negative number, like a POSIX error code).

Edit to give a more complete example:

  extern void bar();
  void foo() {
    bar()
    doOtherStuff()
  }
  void someCall() {
    foo()
    doMoreStuff()
  }
  void main() {
    try {
      someCall()
      doFinalStuff()
    } catch (BarSpecificException e) {
      doErrorStuff()
    }
  }
Is equivalent to this code:

  extern err bar()
  err foo() {
    err = bar()
    if err {
      return err
    }
    err = doOtherStuff()
    if err {
      return err
    }
  }
  void someCall() {
    err = foo()
    if err {
      return err
    }
    err = doMoreStuff()
    if err {
      return err
    }
  }
  err main() {
    err = someCall()
    if err is BarSpecificException  {      
      err = doErrorStuff()
      if err {
         return err
      }
    } else if err {
      return err
    }
    err = doFinalStuff()
    
    if err is BarSpecificException  {      
      err = doErrorStuff()
      if err {
         return err
      }
    } else if err {
      return err
    }
  }
If I ask you in the second piece of code what will be executed after the call to foo(), is it any easier to reply than in the first version? Personally, I don't believe so.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by _ek94 on Friday February 19, 2021 @02:43PM (#26197159)
For those of us who don’t like exceptions I think the concern is more that the exception might not be caught right there, so an exception may potentially transfer control to an arbitrary point above the current function in the stack.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by metreo on Friday February 19, 2021 @06:49AM (#26192112)
this issue is knowing whether you are catching the right errors
Re:Where Everything Went Wrong: Error Handling and Error Mes... by simiones on Friday February 19, 2021 @01:07PM (#26195957)
You have the same problem with error codes, don't you?

The bigger difference is knowing whether you should expect errors at all - with exceptions, you can forget to handle an error and you may screw up an important assumption, like not unlocking a Mutex if an Exception is raised. With error codes, you have the opposite problem: you may forget to check for an error, and continue to execute in a bad state.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by skinkestek on Friday February 19, 2021 @05:31AM (#26191544)
> the compiler struggles to optimize them,

Nitpick: I'd say if you need the compiler to optimize exception handling you are using them wrong.

Exceptions are for exceptional circumstances.

(if you mean that exceptions mess up optimization of surrounding code that could be a bigger deal but I won't accept that without pointers to benchmarks$

Re:Where Everything Went Wrong: Error Handling and Error Mes... by mhh__ on Saturday February 20, 2021 @01:11AM (#26202055)
Exceptions force a less efficient ABI, for example - take a look at unique_ptr, it should be in a register but it's not.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by dnautics on Friday February 19, 2021 @01:01PM (#26195883)
zig gets this pretty close to "right" as per your definition.

> Any error system that relies upon developer discipline will fail because errors will be missed.

You must handle all errors on egress to either C abi or a function that does not have an error signature.

> Any error system that handles all errors the same way will fail because there are some errors we can ignore, and some errors we must not ignore. And what's ignorable/retriable to one project is not ignorable/retriable to another.

trys, which are the "lazy" way of error handling (not counting "catch unreachable" - which promotes errors to panics and shouldn't be used except in dev) automatically append the error return code to the trying function's error return call.

> Attempting to get the complete set of error types that any given call may raise is a fool's errand because of the halting problem it eventually invokes. Forcing people to provide such a list results in the Java problem for the same reason.

If every function has a well-defined tree of possible internal "call dependencies" that has finite set of type signatures, you don't have this problem.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by infensus on Friday February 19, 2021 @08:28AM (#26192902)
> Then we got multi-return error objects, maybes, panics [...] that fail to understand the basic premises of errors:

> Any error system that relies upon developer discipline will fail because errors will be missed.

> Any error system that handles all errors the same way will fail because there are some errors we can ignore, and some errors we must not ignore.

Not sure how that's true for Rust. Functions that can fail, return a Result, that contains either a value or an error. If you want to use the value, you have to either explicitly check which one is it (you can handle or ignore the error at this point), or quickly access it by unwrapping the Result, which will crash (panic) the program on error.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by chmod775 on Friday February 19, 2021 @05:14AM (#26191425)
> Error handling has been wrong since the beginning, and has continued to be wrong ever since.

Maybe it's not error-handling that is the problem then, but undisciplined software developers writing bad code?

Out of all the engineering disciplines, us software engineers have to be the worst bunch by far.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by nx7487 on Friday February 19, 2021 @05:41AM (#26191620)
Yeah, my question is, what's the best direction to go in:

1. Continue trying to create error-handling systems for devs 2. Try to solve this with more generic static (or dynamic) analysis tools.

I mean, maybe what we need to do is make simulator-esque testing more popular, or something.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by MaxBarraclough on Friday February 19, 2021 @02:56PM (#26197319)
> Then we had exceptions, which solved the problem of people forgetting to check by crashing the app.

They can also harm readability, as just about every statement can now cause an early return from a block. They also greatly complicate static analysis.

No discussion of the shortcomings of conventional exceptions would be complete without these two excellent blog posts by the great Raymond Chen:

Cleaner, more elegant, and harder to recognize https://devblogs.microsoft.com/oldnewthing/20050114-00/?p=36...

Cleaner, more elegant, and wrong https://devblogs.microsoft.com/oldnewthing/20040422-00/?p=39...

> Attempting to get the complete set of error types that any given call may raise is a fool's errand because of the halting problem it eventually invokes. Forcing people to provide such a list results in the Java problem for the same reason.

The Java problem is one of ergonomics -- unwieldy lists of exception types -- not a computability problem. What do you mean here?

Re:Where Everything Went Wrong: Error Handling and Error Mes... by pjmlp on Saturday February 20, 2021 @03:33AM (#26202617)
The only programming language where exceptions are such a problem is C++.

First of all, because a religious subculture will run to pitchforks and torches at the slight mention of the word exceptions in the room.

Then the overall machinery in place to make them work alongside code that was most likely written as C originally.

And naturally every ms and byte lost to exceptions infrastructure is a capital felony, so even more fine grained semantics to make it happen.

C++ is still the language I reach for when not doing any JVM/.NET language, but things like this is what takes the fun out of using it with another ones on a team.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by MaxBarraclough on Saturday February 20, 2021 @07:35AM (#26203669)
> The only programming language where exceptions are such a problem is C++.

I'm not sure what you mean here, neither article is about the low-level issues that arise in C++. The second article only discusses C#. Chen's point is more about the way every call can surprise you by causing your code to return early.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by pjmlp on Saturday February 20, 2021 @09:40AM (#26204392)
EINTR and longjmp can also surprise you, even in middle of calls, yet C doesn't have any exceptions.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by MaxBarraclough on Saturday February 20, 2021 @11:18AM (#26205241)
You're right that the C language has many weird and wonderful capabilities and footguns. How does that address Chen's point about exceptions being unmanageable?
Re:Where Everything Went Wrong: Error Handling and Error Mes... by pjmlp on Saturday February 20, 2021 @12:12PM (#26205802)
His point doesn't stand in any programming language that doesn't enforce error checking, exceptions or not, you will be surprised at any possible execution point if you don't check error values from previous call.

At least with exceptions you get a "I told you so" if something does happen, and a defined way to force application termination if the error isn't properly handled.

Given the remarks on the C++ post, I assume he belongs to the /EH- side of pitch forks and torches, which actually goes against the C++ best practices at Microsoft, https://docs.microsoft.com/en-us/cpp/cpp/errors-and-exceptio..., which leaves a feeling of a kind of biased article in some way.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by jiehong on Friday February 19, 2021 @10:02AM (#26193875)
Would you put the Erlang/Elixir error handling in the "catch Throwable" camp?

Because in this case, the error handling is done in a different process altogether (supervisor).

Re:Where Everything Went Wrong: Error Handling and Error Mes... by Floegipoky on Friday February 19, 2021 @01:24PM (#26196181)
I think of it as "no error handling", the program just crashes. There just happen to be many other programs running, which may restart the program that crashed.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by heavenlyblue on Friday February 19, 2021 @05:16AM (#26191436)
> Attempting to get the complete set of error types that any given call may raise is a fool's errand because of the halting problem it eventually invokes.

That’s not true.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by im3w1l on Friday February 19, 2021 @05:43AM (#26191631)
Yeah, it only becomes an issue if you want the compiler to infer that ImpossibleError cannot be raised in

    while True: pass
    raise ImpossibleError
But normally we are perfectly content to put ImpossibleError in the signature for code like this.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by heavenlyblue on Friday February 19, 2021 @01:25PM (#26196188)
Actually in this specific case you can infer that, and you can have an exhaustive list of language constructs beyond which this becomes undecidable.

There is a practical example of “panic!” in Rust to go around that as well.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by why_Mr_Anderson on Friday February 19, 2021 @05:24AM (#26191492)
Because all systems try to handle several kinds of unexpected behavior in the exactly the same way - invalid arguments (e.g. 'x == null') - code logic (e.g. 'if (x.salary < 0) ...') - external errors (e.g. 'out of memory')

I'd argue that only the 3rd kind is actually 'exception', it's completely out of program's control.

Code contracts are wonderful way of dealing with 1st and 2nd kind, sadly they didn't catch up and remains mostly unknown. They are vastly superior to tests and also serve as much better way to document intent of the code, how the author expected the code to work.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by nerdponx on Friday February 19, 2021 @06:06AM (#26191797)
Unless you have dependent types, isn't a contract just syntactic sugar for an if/else that either raises and exception or returns an error code?
Re:Where Everything Went Wrong: Error Handling and Error Mes... by higerordermap on Friday February 19, 2021 @10:37AM (#26194243)
Using exceptions for control flow vs using control flow for exceptions. Who will win?
Re:Where Everything Went Wrong: Error Handling and Error Mes... by mrmr1993 on Friday February 19, 2021 @07:49AM (#26192560)
I think that exceptions are a problem and cause this developer burden only because they are invisible. If they appeared in the type signature, for example as

() -[DatabaseReadError]-> ()

then they would be part of a function's 'contract'.

With this, consumers of your function are making an active decision about whether to handle or bubble an exception without examining your implementation, and the type of the main function tells you which errors will end up being fatal.

Disclaimer: this is not a new idea. There are proposals for OCaml to add 'algebraic effects', which have similar annotations on arrows, and I believe there have been discussions around using the syntax to track exceptions.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by simiones on Friday February 19, 2021 @07:55AM (#26192608)
The biggest problem with that is that it is very unwieldly if you're using any kind of higher-order functions.

To fix that, you need to start supporting error polymorphism. For example, `map` should have a signature like

  map :: List a -> (a -> b -[err]) -> List b -[err]
So that

  map [1 2 3] +1 //no errors
  map [1 2 3] sendOnNetwork //returns NetworkError
At least, this is one of the major limitation of Java's Checked Exceptions idea, one which often forces you to use Unchecked Exceptions.

Another common problem is handling exceptions which occur while handling another exception. D has the best default I've seen in that area (it will automatically collect all of the errors, instead of panic()-ing like C++ or discarding the old exception like Java and C#).

Re:Where Everything Went Wrong: Error Handling and Error Mes... by dasyatidprime on Friday February 19, 2021 @11:56AM (#26195075)
There've been a few times I've used checked exceptions very locally in Java where I did use a type parameter successfully for this—though it's not really threaded through the type signatures in the standard libraries, so interop can be a problem. Almost like what you wrote, of course more verbosely, something like:

    interface SomeProcessor<E> {
        void process(Thing thing) throws E;
    }

    <E> void processThingsSomehow(
        SomeProcessor<E> processor,
        Container<Thing> things,
        Parameter how) throws E {
        // ... {
            processor.process(extractedThing);
        // } ...
    }
and then later:

    void processOrFail(Thing thing)
        throws SomeCheckedException {
        // ...
    }

    void processOurThings() {
        try {
            someObject.processThingsSomehow(
                this::processOrFail,
                getThings(), HOW);
        } catch (SomeCheckedException e) {
            // ...
        }
    }
and it definitely worked the way I expected—if the correct exceptions weren't caught in processOurThings, it wouldn't compile, and processThingsSomehow did not have to catch them. It even worked in at least some cases with multiple throws on the concrete SomeProcessor, though I think the different exceptions involved had an upper type bound within the package; I don't know how well that's handled in the general case.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by simiones on Friday February 19, 2021 @12:58PM (#26195846)
Nice, I never actually tried to do this (my problem was more that I needed to use built-in functions that don't throw, for example sorting a list with a Comparator which can throw).
Re:Where Everything Went Wrong: Error Handling and Error Mes... by a1369209993 on Friday February 19, 2021 @09:39PM (#26200978)
> To fix that, you need to start supporting error polymorphism.

  > map :: List a -> (a -> b -[err]) -> List b -[err]
FWIW, Haskell has this already; it's called[0] Traversable:

  ghci> :t mapA
  mapA :: (Traversable t,Applicative f) => (a -> f b) -> t a -> f (t b)
  ghci> :t mapA @(Error ||) @[]
  mapA :: (a -> Error || b) -> [a] -> Error || [b]
0: The default prelude calls it `traverse` instead of `mapA` (which is terrible for reasons that should be obvious[1]) and `Either Error a` instead of `Error || a` (which is a matter of taste).

1: Especially if you rename the method of Functor from `fmap` to:

  map :: Functor t => (a ->   b) -> t a ->    t b
  -- for comparison:
  mapA :: ...      => (a -> f b) -> t a -> f (t b)
Re:Where Everything Went Wrong: Error Handling and Error Mes... by junke on Friday February 19, 2021 @10:35AM (#26194224)
And if the function can throw different kinds of exceptions, you need a way to express the union of unrelated exceptions (without defining a new variant type), a bit like Polymorphic Variants I guess.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by dllthomas on Friday February 19, 2021 @04:39PM (#26198360)
Ideally also a way of saying "... except for X Y Z" while remaining appropriately polymorphic. I've remarked before that while checked exceptions help make sure you consider every possible situation, imprecision forces you to consider many (too many?) impossible situations as well.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by brazzy on Friday February 19, 2021 @11:46AM (#26194964)
This is exactly Java's checked exceptions, which I'd call a failed experiment in language design.

The problem is that it makes the most common case (letting the exception bubble up) very inconvenient and clutter-y. Signatures with 5 different declared exceptions are worse than useless.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by pjmlp on Saturday February 20, 2021 @03:26AM (#26202578)
> Then the Java team got the bright idea to have checked exceptions

The Java team adopted the ideas of CLU, Modula-3 and C++, yet it keeps getting the blame while being the fourth language implementing the idea.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by dilap on Friday February 19, 2021 @09:56AM (#26193802)
Are you familiar with Zig's error handling? Except for the fact that errors cannot contain payloads, it is, imo, perfect.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by malcolmstill on Friday February 19, 2021 @02:36PM (#26197079)
Of all the absolutely fantastic stuff that Zig gets right, I think my favourite is the error handling.

While it would be cool to get an error payload, I would hope that if that was added to the language, that it doesn't affect the current ergonomics of error handling.

Where I've really needed to get some data back out, I've passed in a pointer to a struct that gets populated.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by jiehong on Friday February 19, 2021 @10:05AM (#26193916)
it has the same issues. You can always discard errors with `catch unreachable` for example.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by defen on Friday February 19, 2021 @11:29AM (#26194785)
If you haven't overridden the default panic handler, and you're in a Debug or ReleaseSafe build you'll still automatically get an error return trace (which is nicer than a stack trace) even if you do `catch unreachable`.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by dilap on Friday February 19, 2021 @10:18AM (#26194059)
True, but it's very explicit, and easy to audit for.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by tus89 on Friday February 19, 2021 @01:08PM (#26195985)
> Any error system that relies upon developer discipline will fail because errors will be missed.

Haven't there been some languages that force functions to return some kind of tuple like:

result,error

And forces the programmer to at least do:

if(error) { }

It does not force any kind of correct handling, but simply oversights should be caught. I might be imagining things though.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by masklinn on Friday February 19, 2021 @02:02PM (#26196643)
What you're describing is Go, except its requirements are much weaker than that.

Rust, meanwhile, is way stricter than that, and has gone way further on the "not relying on developer discipline" path: a failing Rust function will return a `Result<Value, Error>`. You can't even access the value without explicitly checking whether it's a value or an error one way or the other, which you can very much do so in e.g. Go. Rust also has an attribute called `must_use`, which can be set on types and functions. That attribute causes the compiler to emit a warning if the marked object is not used at all (either the type or the function's result), so while go will not say anything if you write

    Foo()
and that returns an error (or an error and a result you happened not to care for), Rust will absolutely complain by default in the same case, you will need to write at the very least

    let _ = Foo();
Go has a second issue, which is that in

    result, error := Foo()
that you "have to" handle the error is a consequence of the unused variable check (can't define a variable and never read from it). However because it's that instead of something dedicated, this:

    id, error := Foo()
    result, error := Bar(id)
    if error != nil {}
will work fine, with no complaint. Despite possibly passing complete nonsense to Bar if Foo is in error. Also works with

    id, error := Foo()
    if error != nil {}
    result, error := Bar(id)
Funnily enough Rust will also warn in both those cases, because it tries to track individual writes.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by simias on Friday February 19, 2021 @01:12PM (#26196028)
That's Go. IMO Rust's approach is vastly saner, since a Result<> type has to be explicitly handled one way or an other. You simply can't access the returned value without unwrapping it.

Of course that leaves function that can fail but don't return any value, but since Result is tagged "must_use" you get a compiler warning if you don't explicitly discard the result with something like `let _ = foo()`.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by kelnos on Friday February 19, 2021 @05:00PM (#26198588)
There's no "forcing" there. You can simply ignore the error part of the tuple, or even just forget to check it.

If the function returns a (non-error) value that is directly accessible from the function call, the programmer is not forced to do anything with the error.

This is exactly the same problem with null: forget a null check, and you're hosed. Forget an error check, and you're hosed.

If you make errors a part of the actual single return type (as Rust does), then you have to explicitly deal with the possibility of an error before you are even allowed to touch the successful case.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by TacticalCoder on Friday February 19, 2021 @01:48PM (#26196460)
GP mentionned "maybes". In Haskell (and others) you have for example both maybe and either. When you've got an either you can have either (ah!) the left to indicate an error (and which error) or the right to hold the correct value. And the type system forces you to at deal with both cases (like a maybe forces you to deal with the case where it's "maybe not").
Re:Where Everything Went Wrong: Error Handling and Error Mes... by dataflow on Friday February 19, 2021 @04:18PM (#26198157)
I feel like error handling is fine in like C#. No checked exceptions, and stack traces come with the exceptions, and you can nest them. What's not to like?
Re:Where Everything Went Wrong: Error Handling and Error Mes... by kelnos on Friday February 19, 2021 @04:56PM (#26198538)
IMO exceptions are a terrible way to signal errors. They obscure control flow and make it much harder to reason about a program's behavior. They make it easy to forget to check for errors, and encourage sloppy catch-all error handling. It is a lot harder to determine if exception-based code is correct or not, and languages that have exceptions require you to examine literally every line of code to determine if it can throw.

Even the name is wrong: an "exception" should signal truly exceptional behavior, things that you would not expect to happen unless something is really wrong. And yet exceptions are thrown for mundane things like "file not found", something you could very easily expect to happen routinely.

Exceptions should be like Rust's `panic!()`: only for things that the programmer can't reasonably do anything about, which will cause the program to terminate.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by dataflow on Friday February 19, 2021 @05:02PM (#26198607)
I don't agree with your first or third paragraphs but I do kind of agree with the second, yet I think the solution to that is to provide return-value-based solutions where it makes sense, not to avoid exceptions in general.

Also, things like "user canceled this operation" are great for exceptions IMO... exactly how would you stop (say) a sort() function otherwise? You'd need to write a custom cancelable sort, which isn't a great idea? You can't reinvent the wheel for everything.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by tasubotadas on Friday February 19, 2021 @11:18AM (#26194665)
Nice summary. Ideally, I would like languages just to do simple exceptions (no checked stuff) and create a way to catch them.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by randyrand on Friday February 19, 2021 @12:38PM (#26195596)
i’ve use Java C++ and Objective-C and by far the best approach to errors has been Objective-C’s NSError* out parameters.

They're strings. They’re chain-able. They’re visible. They’re hard to ignore. They don’t propagate or crash unless you want them to.