My kingdom for someone who can figure out how to solve error handling. My code consists of some reasonably straightforward sequence of actions with a random smattering of error handling significantly distracting from that. That error handling code is tedious to write, very time consuming to test (and often virtually impossible) and usually not run very often. Exceptions at least let you put the handling code somewher…
You and I sound like we may be cut from similar cloth. I find Go's approach to error handling -- which is to not to do much about the problem at all -- is better than pervasive use of exceptions but worse than some other possible mechanisms (and on this we may disagree). Although I'm not saying the language Rust has got it right, perhaps some of the thoughts on the mailing list may be useful. However scarily, my mail…
Another go at the Next Big Language
11–20 of 125 posts
Re: Another go at the Next Big Language
#12My kingdom for someone who can figure out how to solve error handling. My code consists of some reasonably straightforward sequence of actions with a random smattering of error handling significantly distracting from that. That error handling code is tedious to write, very time consuming to test (and often virtually impossible) and usually not run very often. Exceptions at least let you put the handling code somewher…
You and I sound like we may be cut from similar cloth. I find Go's approach to error handling -- which is to not to do much about the problem at all -- is better than pervasive use of exceptions but worse than some other possible mechanisms (and on this we may disagree). Although I'm not saying the language Rust has got it right, perhaps some of the thoughts on the mailing list may be useful. However scarily, my mail…
BTW your posting left out that close() can also fail (eg deferred writes run out of disk space).
And note that none of these really help with testing. In your 'cat' example, I'd want to test open failing, read failing, write failing and close failing. At a certain level the combination of language and libraries know these can fail. Ideally I would like to write zero lines of code to exercise those cases. In practise I usually find out that my test code is larger than the code being tested, and I usually have to add all sorts of instrumentation to force various failure modes which again makes my original code even longer and more obfuscated.
Re: Another go at the Next Big Language
#13My kingdom for someone who can figure out how to solve error handling. My code consists of some reasonably straightforward sequence of actions with a random smattering of error handling significantly distracting from that. That error handling code is tedious to write, very time consuming to test (and often virtually impossible) and usually not run very often. Exceptions at least let you put the handling code somewher…
My kingdom for someone who can figure out how to solve error handling. You don’t really seem to want someone who can solve error handling. You seem to want someone who can make it disappear , or at least get as close as possible to that ideal. I feel your pain, but I fear you are asking the impossible. :-( Some errors are recoverable, and maybe it would be helpful to have default policies like “if the file is read on…
I think part of the problem is error reporting is still somewhat shaky. To a certain extent you only actually need to handle errors that occur in the real world. Some languages like Java and Python can get you a reasonable amount of information when an exception occurs, but you still have to find mechanisms to report it back, and chances are you won't have detailed program state or operations leading up to the error. But if you did you could in theory release programs with no error handling as alpha releases (in the original sense, not current marketing usage) and see what shakes out.
> I don’t know what kind of testing you have in mind
Using fdr's example of a cat program, it consists of an open, a sequence of read followed by write and a close. I would like to point the language/environment at that and say "test it" with no further effort. Currently I have to write far more code to test, and then add in instrumentation to force various failures (eg make close return an error). The test code in this example would take considerably longer to develop and could itself have bugs!
Re: Another go at the Next Big Language
#14My kingdom for someone who can figure out how to solve error handling. My code consists of some reasonably straightforward sequence of actions with a random smattering of error handling significantly distracting from that. That error handling code is tedious to write, very time consuming to test (and often virtually impossible) and usually not run very often. Exceptions at least let you put the handling code somewher…
This is the problem that comes up in many languages and which needs to be dealt with:
"Because each function is a black box, function boundaries are an excellent place to deal with errors. Each function--low, for example--has a job to do. Its direct caller--medium in this case--is counting on it to do its job. However, an error that prevents it from doing its job puts all its callers at risk: medium called low because it needs the work done that low does; if that work doesn't get done, medium is in trouble. But this means that medium's caller, high, is also in trouble--and so on up the call stack to the very top of the program. On the other hand, because each function is a black box, if any of the functions in the call stack can somehow do their job despite underlying errors, then none of the functions above it needs to know there was a problem--all those functions care about is that the function they called somehow did the work expected of it.
In most languages, errors are handled by returning from a failing function and giving the caller the choice of either recovering or failing itself. Some languages use the normal function return mechanism, while languages with exceptions return control by throwing or raising an exception. Exceptions are a vast improvement over using normal function returns, but both schemes suffer from a common flaw: while searching for a function that can recover, the stack unwinds, which means code that might recover has to do so without the context of what the lower-level code was trying to do when the error actually occurred.
Consider the hypothetical call chain of high, medium, low. If low fails and medium can't recover, the ball is in high's court. For high to handle the error, it must either do its job without any help from medium or somehow change things so calling medium will work and call it again. The first option is theoretically clean but implies a lot of extra code--a whole extra implementation of whatever it was medium was supposed to do. And the further the stack unwinds, the more work that needs to be redone. The second option--patching things up and retrying--is tricky; for high to be able to change the state of the world so a second call into medium won't end up causing an error in low, it'd need an unseemly knowledge of the inner workings of both medium and low, contrary to the notion that each function is a black box.
Common Lisp's error handling system gives you a way out of this conundrum by letting you separate the code that actually recovers from an error from the code that decides how to recover. Thus, you can put recovery code in low-level functions without committing to actually using any particular recovery strategy, leaving that decision to code in high-level functions.
To get a sense of how this works, let's suppose you're writing an application that reads some sort of textual log file, such as a Web server's log. Somewhere in your application you'll have a function to parse the individual log entries. Let's assume you'll write a function, parse-log-entry, that will be passed a string containing the text of a single log entry and that is supposed to return a log-entry object representing the entry. This function will be called from a function, parse-log-file, that reads a complete log file and returns a list of objects representing all the entries in the file.
To keep things simple, the parse-log-entry function will not be required to parse incorrectly formatted entries. It will, however, be able to detect when its input is malformed. But what should it do when it detects bad input? In C you'd return a special value to indicate there was a problem. In Java or Python you'd throw or raise an exception. In Common Lisp, you signal a condition.
A condition is an object whose class indicates the general nature of the condition and whose instance data carries information about the details of the particular circumstances that lead to the condition being signaled.3 In this hypothetical log analysis program, you might define a condition class, malformed-log-entry-error, that parse-log-entry will signal if it's given data it can't parse."
http://www.gigamonkeys.com/book/beyond-exception-handling-co...
Read the whole thing. It was a very clever system and wish something like it was available in other languages. (The lack of it in Clojure was one of the few things I agreed with Loper about in his anti-Clojure rant: http://www.loper-os.org/?p=42)
Re: Another go at the Next Big Language
#15Re: Another go at the Next Big Language
#16Re: Another go at the Next Big Language
#17Earlier quoted context omitted.
You and I sound like we may be cut from similar cloth. I find Go's approach to error handling -- which is to not to do much about the problem at all -- is better than pervasive use of exceptions but worse than some other possible mechanisms (and on this we may disagree). Although I'm not saying the language Rust has got it right, perhaps some of the thoughts on the mailing list may be useful. However scarily, my mail…
I actually prefer exceptions to Go's approach, mainly because most of my code is structured with the best place to handle errors being higher up the call chain. Exceptions let me do that for free, whereas Go requires boilerplate code all over the call chain. BTW your posting left out that close() can also fail (eg deferred writes run out of disk space). And note that none of these really help with testing. In your 'c…
However, there are a few libraries that offer 2 versions of the same functions, one that returns the value and error combo, and one that panics in case of error and returns only the value (regexp' Compile and MustCompile for example). When it makes sense, it's nice to have this option.
Anyway, error handling is hard, I'm not a fan of exceptions, though alternatives such as Node's error as first argument of a callback and Go's second return value require polluting the normal code path. I like go's "defer" idea as a "finally", though.
Re: Another go at the Next Big Language
#18Earlier quoted context omitted.
My kingdom for someone who can figure out how to solve error handling. You don’t really seem to want someone who can solve error handling. You seem to want someone who can make it disappear , or at least get as close as possible to that ideal. I feel your pain, but I fear you are asking the impossible. :-( Some errors are recoverable, and maybe it would be helpful to have default policies like “if the file is read on…
I agree that zero lines of error handling is the perfect solution. Even getting error handling and testing to be a trivial percent of the actual useful code would be a major improvement. At the moment my testing code is around the same size as the useful code plus error handling, and error handling is around 10% to 50% of the code. I think part of the problem is error reporting is still somewhat shaky. To a certain e…
OK, so I suppose the next question becomes what “test it” means.
Perhaps you want your test system to make some kinds of automatic deductions about the intended behaviour of your code and then to verify that the actual behaviour matches? If that is the case, what kinds of deductions would you want to be handled automatically for you?
Or maybe you’re thinking of some sort of automatic simulation of possible failure cases when you get to the open/read/write/close operations, serving a similar roles to things like mocks and stubs in unit testing?
Some of these are the kinds of problems I’m hoping a good effect system will help with. If you know that the operations of opening, reading, writing and closing files must occur only in certain orders, you can automatically detect violations of those rules. And if you know all possible types of cause and effect that can be relevant within each part of your code — that is, you can identify all stateful/impure/externally observable results — then you can identify any of them that aren’t handled according to some set of rules. As long as you can figure out what you want those rules to be, that is...
Re: Another go at the Next Big Language
#19It performs fairly well too: http://shootout.alioth.debian.org/u32/javascript.php . I'm not sure why one test is 100x slower, but the rest are < 10x slower than Java.
Re: Another go at the Next Big Language
#20My kingdom for someone who can figure out how to solve error handling. My code consists of some reasonably straightforward sequence of actions with a random smattering of error handling significantly distracting from that. That error handling code is tedious to write, very time consuming to test (and often virtually impossible) and usually not run very often. Exceptions at least let you put the handling code somewher…
Lisp had a condition system that was very clever. You still had to write the code, but you were able to separate the problem, the handling, and the restart. This is the problem that comes up in many languages and which needs to be dealt with: "Because each function is a black box, function boundaries are an excellent place to deal with errors. Each function--low, for example--has a job to do. Its direct caller--mediu…
But if there was some way for medium to recover by itself, it had the option to handle the exception on the way back up the call stack and then to restart the computation that called low, hopefully under improved conditions.
Control will therefore return to high only if medium doesn’t itself know how to handle the error. At that point, the entire computation for which medium was responsible has failed.
Assuming the system as a whole uses reasonable functional decomposition and modular design, since exceptions have limited value under other circumstances anyway, shouldn’t any context that was known only within medium be irrelevant to any recovery action taken at high’s level?