Anybody who wants to judge try/catch should first go and read about Common Lisp's condition system. See for example the chapter about conditions and restarts from the excellent book "Practical Common Lisp" by Peter Seibel:
Notice I'm not saying you should go and program in Common Lisp, just that you should understand those ideas before you engage in any sort of meaningful discussion about exceptions, errors and error handling in general. Because, you know, some things have already been thought about and invented. Don't re-invent them.
And on a more practical level, in languages with dynamic binding it isn't difficult to provide error handlers and "send" them up the call chain, so that in case of an exception your handler gets called, fixes the problem, and lets the called function continue. You can do all that using try/catch as a low-level tool, I've seen it done in Clojure, using JVM's exception system.
I work in Windows file systems for a living. We very much live with two models: exception handling and canonical return codes. I cannot tell you how many times I would have killed for Lisp-like conditions. If I were to tell you that, however, I would also have to tell you that most of the killing would have been in vain.
The trouble with the never-ending "try-catch versus conditions versus return codes versus fail-fast" argument is that there is no easy way to have the conversation about big swaths of code. The log parser is a great example, yes, but it is exceedingly simple. The fact is that 'low' in the linked example is exposing its guts to 'high' whether it likes it or not, and that in any reasonably large body of code this too can become unmanageable.
Wherever you see a religious war, your Spidey sense should be tingling, telling you: people are arguing over which tool is better for all jobs when in fact you might want to learn all of the tools and choose the best whenever possible. And that you will at times show up to a job site where they're using the wrong tool and you'll have to learn to change them or live with it -- whichever makes more sense/is more possible.
That said, sometimes consistency just wins out. A module that throws to a module that returns is always baffling, but if it's hiding this fact from the rest of the module -- or many more modules -- then it's worth it.
(edited to note that these are Windows file systems, hoping to avoid 'is that really so')
Inspired by Lisp's condition system I wrote a ruby library which comes pretty close to Lisp's error handling. It also implements "restarts" ... a little bit messy though since, as you said, low-level execption are used to get it working. In one point the condition system bootstraps itself which is quite interesting to think about.
In the beginning I thought it can only be ported to Ruby because I used some Ruby-specific features. The exception solution (which I implemented later) should work in a lot of different languages though.
Another thing is, if you understood the condition system, you can use it for a lot of different things besides error handling. You can build protocols on top of that, event handling etc. Freedom is yours.
Have you considered using Ruby's throw/catch instead of exceptions? I did that for Atomy's condition system[1]. They worked great because you can just use the restart names for the throw/catch tags.
I might have considered throw and catch as I wrote the library, but right now, I cannot think of any good reason why I haven't used it. Will check that again. Thanks for the suggestion.
Spot on, but CL's error generation/capture/recovery mechanism only helps in single-threaded code composition. Erlang's model of linking up processes so that a "supervisor" process get notified if a servant dies is the counterpart in a concurrent scenario. Together, they seem to me to cover most of the ground.
Furthermore, in Haskell you can throw an exception to another thread, though I'm not sure whether that's any more valuable as a design tool than a plain message passing channel. (see also "throwTo & block statements considered harmful" http://www.haskell.org/pipermail/haskell-cafe/2006-December/...)
Good point! I never actually considered this, because almost all Common Lisp code I wrote was single-threaded (CL and threads aren't friends), and nowadays I write in Clojure, where I just stick to fairly plain catch/throw.
This is a great example of why it is always worth it to learn various languages, not just stick to what you know. You regularly get eye-opening revelation moments.
http://www.gigamonkeys.com/book/beyond-exception-handling-co...
Notice I'm not saying you should go and program in Common Lisp, just that you should understand those ideas before you engage in any sort of meaningful discussion about exceptions, errors and error handling in general. Because, you know, some things have already been thought about and invented. Don't re-invent them.
And on a more practical level, in languages with dynamic binding it isn't difficult to provide error handlers and "send" them up the call chain, so that in case of an exception your handler gets called, fixes the problem, and lets the called function continue. You can do all that using try/catch as a low-level tool, I've seen it done in Clojure, using JVM's exception system.