
Isaacs: try/catch is an anti-pattern - jacobr
https://groups.google.com/d/topic/nodejs/1ESsssIxrUU
======
jwr
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:

[http://www.gigamonkeys.com/book/beyond-exception-handling-
co...](http://www.gigamonkeys.com/book/beyond-exception-handling-conditions-
and-restarts.html)

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.

~~~
andregawron
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.

<https://github.com/melkon/conditions>

An example ("parse_log_file" and "log_analyzer show restarts"):
[https://github.com/melkon/conditions/blob/master/example/exa...](https://github.com/melkon/conditions/blob/master/example/example.rb)

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.

~~~
vito
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.

[1]:
[https://github.com/vito/atomy/blob/master/kernel/condition.a...](https://github.com/vito/atomy/blob/master/kernel/condition.ay)

~~~
andregawron
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.

------
dchest
This seems to be exactly the attitude of Go:

"We believe that coupling exceptions to a control structure, as in the try-
catch-finally idiom, results in convoluted code. It also tends to encourage
programmers to label too many ordinary errors, such as failing to open a file,
as exceptional.

Go takes a different approach. For plain error handling, Go's multi-value
returns make it easy to report an error without overloading the return value.
A canonical error type, coupled with Go's other features, makes error handling
pleasant but quite different from that in other languages.

Go also has a couple of built-in functions to signal and recover from truly
exceptional conditions. The recovery mechanism is executed only as part of a
function's state being torn down after an error, which is sufficient to handle
catastrophe but requires no extra control structures and, when used well, can
result in clean error-handling code."

<http://golang.org/doc/go_faq.html#exceptions>

In Go, if there's a programmer error, call panic(); if there's a non-
programmer error, return it as a second return value.

[Added] Plus, it's obvious to see where people ignore errors:

    
    
       f, _ := os.Open("filename")
    

"_" is a throw-away variable to indicate that this value won't be used in the
code. It's obvious that the programmer decided to ignore the error.

    
    
       f, err := os.Open("filename")
    

If you don't use "err" later, this won't compile.

    
    
       f, err := os.Open("filename")
       if err != nil {
          // handle error
       }
    

This code handles error.

~~~
PaulHoule
This is a bad idea that keeps coming back again and again.

I see nothing wrong with exceptions, I do have a problem with (1) checked
exceptions, and (2) catching exceptions prematurely and (3) people not
learning how to use "finally" so they do (2) and rethrow.

Languages like Go and Scala roll out various mechanisms that bring us back to
the bad old days of C, when we had to check the return/value and or the error
code after every function call... if we wanted error handling to work.

The trouble with this approach is that it increases code bulk. For CS class
projects, this isn't so bad, but when you're building real systems, the
complexity of the error handling can approach or exceed the complexity of the
"normal" path and when that happens you're in deep trouble.

Exceptions drastically reduce code bulk by introducing default "abort"
behavior, which can itself be aborted at any level of the program and which
can invoke cleanup anywhere in between.

Many programmers in many situations would be perfectly happy to catch "failure
to open a file" and "failure to open a database connection" and "failure to
connect to a network host" with one simple handler that logs the failure and
either aborts, retries or ignores.

~~~
gizmo
> when we had to check the return/value and or the error code after every
> function call... if we wanted error handling to work.

Errors as return values force you to think about every possible error, which
is _a good thing_ for code quality. Look at how much rock stable C software we
have out there. Software that can be compiled on many different architectures,
run in many different environments, and it all just works, even 20 years
later.

Writing code with try/catch is much less work. Not because you have to do less
typing, but because you simply think less about how errors should be handled.

~~~
TylerE
> Look at how much rock stable C software we have out there.

Which would be what exactly. The work of Knuth and djb I'll grant you, but the
rest?

~~~
vog
*BSD kernels, GCC, tar, gzip, ...

~~~
TylerE
BSD kernels have had numerous exploits over the years. GCC is a massive
hairball. gzip has had exploits (<http://www.kb.cert.org/vuls/id/381508> for
one).

Care to try again?

~~~
andrewflnr
Without defending any given piece of software, the job of the exception
advocate is not to prove that return values suck, but that using exceptions
produces _more stable software_ as fast or faster. This question cannot be
resolved by examples/anecdotes alone.

------
azov
_> You can't reasonably argue that this:

    
    
      try {
        foo = JSON.parse(input)
      } catch (er) {
        return "invalid data"
      }
    

is more lightweight than:

    
    
      foo = JSON.parse(input)
      if (!foo) return "invalid data

_

This is like saying that walking is faster then driving because I can walk 5
meters faster then it takes me to get into a car. Yes, error codes are more
compact in a tiny "Hello world" example because it is only showing one
function call. Exception handling becomes more compact when you're writing
something less trivial and you don't have to repeat the same error handling
code after every call.

 _> Try/catch is goto wrapped in pretty braces. There's no way to continue
where you left off, once the error is handled. _

Don't throw exceptions if you can handle the error and continue where you left
off. Exceptions is for when you can't continue. Think of throwing as a way to
roll back transaction, stop whatever you were trying to do, and go back to the
last consistent state.

Obviously, exceptions are not perfect. As author correctly notes, they require
careful consideration of what's exception and what's part of normal flow. But
they were invented for a reason, and I don't see the article offering any
alternative solutions to the problems that exceptions are solving now.

~~~
dmethvin
> Don't throw exceptions if you can handle the error and continue where you
> left off. Exceptions is for when you can't continue.

Yes, although quoting that example doesn't support the assertion. JSON.parse
is a library function. How can it judge whether the caller can continue or not
just because the JSON cannot be parsed?

> Think of throwing as a way to roll back transaction, stop whatever you were
> trying to do, and go back to the last consistent state.

Any try block that is larger than one atomic operation can become a nightmare
to roll back, since the catch gives you no idea how far it was into the block,
what resources were allocated, etc. So although try/catch avoids the hassle of
checking state after each operation, you pay for it on errors.

~~~
azov
> JSON.parse is a library function. How can it judge whether the caller can
> continue or not just because the JSON cannot be parsed?

Don't make assumptions about the caller, throw if your library can't continue.

> So although try/catch avoids the hassle of checking state after each
> operation, you pay for it on errors.

If your language supports RAII
([http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initial...](http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization))
you get a free ride. Otherwise you have to write non-trivial cleanup code
regardless of how you return errors. _Try/catch_ (and especially _finally_ )
helps a lot because you get to group all your cleanup in one place. With error
codes the code looks something like:

    
    
      a = acquire(A)
      if (!a) return
    
      b = acquire(B)
      if (!b) {
         release(a)
         return
      }
    
      c = acquire(C)
      if (!c) {
         release(a)
         release(b)
         return
      }
    
      ...
    

This is a maintenance nightmare. It's hard to see what the code is doing
because the real logic is hidden between piles of error handling stuff. If you
add a new resource acquisition in the middle you have to go over every
statement that follows and modify error handlers. And don't you dare changing
the order of statements, because that basically forces you to rewrite the
whole function. Believe me, you don't want this in your code, exceptions are
your friend :)

~~~
regularfry
No, the way to handle that pattern is with nested gotos:

    
    
        a = acquire(A);
        if (!a) goto err_a;
    
        b = acquire(B);
        if (!b) goto err_b;
    
        c = acquire(C);
        if (!c) goto err_c;
    
        do_stuff(a,b,c);
    
      err_c:
        release(b);
      err_b:
        release(a);
      err_a:
        return;
    

This is precisely why goto is not universally evil.

~~~
gue5t
Consider:

    
    
      a = acquire(A);
      if (a)
      {
      	b = acquire(B);
      	if (b)
      	{
      		c = acquire(C);
      		if (c)
      		{
      			do_stuff(a,b,c);
      		}
      		release(b);
      	}
      	release(a);
      }
      return;

------
pilif
_> I much prefer php's json_decode function, since it just returns `null` on
invalid input._

A function which has the same result in case of an error as when given valid
input (hint: 'null' is a valid json string) is neither good design, nor
something I would actually 'prefer'.

Aside of that (and more to the point of the original article), I do believe
that exceptions can be very useful the deeper the abstraction of your
libraries gets.

If you are 'far down' the stack and you want to ensure that you are in a
position where it's safe to proceed and making it very, very clear for the
caller that something went wrong, trow() is the perfect tool in order not to
be called with a garbage argument on a successive call to a different function
(hint: error results tend to get ignored).

And if you are a user of a library, getting nice exceptions can be very handy
too - sometimes even wrapping and re- throwing them.

A very good example of real code: In a command line script which processes a
lot of text-data to import into a database delegates to various importer
classes depending on import line type.

All these importer classes do their thing and whenever they come across an
issue, they just throw an ImporterException _with_ all the context that they
know about.

The parent script only has to deal with one single case of Exception to
produce nice error messages and show everything about the context where they
happened.

I can make the importers as complicated as they need to be and I never have to
check a single return value (or forget to check it). There is one central
place to handle whatever kind of Error that can creep up.

This is _very_ handy.

Granted, in JS/node where each callback cleans out the stack so that a thrown
exception can't be handled by whatever caused the callback to be executed
later), exceptions are useless and actually harmful because they can mess up
the program flow (which assumes callback to be triggered eventually).

But exceptions being useless in one environment doesn't make the useless
everywhere.

~~~
Periodic
I thought a JSON document had to have either a top level object or array,
which would make a bare null an invalid JSON text. Granted, we might not be
that strict all the time.

See section 2, paragraph 2: <http://www.ietf.org/rfc/rfc4627.txt?number=4627>

~~~
davisp
The issue with the JSON spec is that it's deceptively simple. For instance,
this particular issue comes up relatively often. As you point out it's quite
specific that a valid "JSON text" has a top level object or array. This
assumption is even required for the sections on "detecting character
encodings" (which makes me wince to this day).

The particular issue is that the spec refers to "JSON text" (which no one ever
uses in practice) as well as "JSON value" which is what everyone expects. The
difference being that a "JSON value" is any of the 'bare' types (null,
boolean, number, string, object, or array). A quick survey of JSON parsers
will show you that most will accept any of these types at the top level. The
one notable exception I'm aware of is Ruby's JSON library (the default one?
I'm not so hip to the Ruby).

Notably, though, is that the major JavaScript interpreters _don't_ enforce
this constraint (Nor do Python or YAJL (kinda) or Erlang (even the ones I
didn't write)).

Don't bother asking me about invalid combining characters as \u escapes. You
wouldn't like me when I get the rage eyes.

------
viraptor
It looks great on simple examples, but now consider your stacktrace is 5+
levels deep with some generators doing their generalised work on some
collection. If the last function fails, then you better be sure that every
single level:

\- returns the error message and context instead of just error code

\- wraps the internal problem properly inside its own error description and
context

\- cleans up all internal state

Guess what... that's exactly what exceptions do. If don't do the first point,
you'll end up with multiple causes producing the same error number. If you
don't do the second, you'll get "invalid data" when parsing json (where? what
data? which line? which element? why is it invalid?). If you don't do the
third, you're going to crash anyway.

So if you don't use exceptions to make your code nicer, you're going to end up
implementing the same flow over and over again in places that could just allow
the exception to pass through. You might even write some helper/macros
functions to... add a file and line number when wrapping previous exception.
It feels like reinventing the wheel.

Edit: Forgot to mentioned the collection processing in the end. What is tricky
about it is that for a common "map()" to stop on error, you'll need to have a
common way of handling errors. Exceptions do this just fine, custom methods -
not necessarily.

------
gbog
> At least JavaScript doesn't have typed catches. Holy black mother of
> darkness, that shit is intolerable.

I would like the author to expand a bit instead. In python there are typed
catches, it seems to make a lot of sense to me: you catch only the exceptions
you want, and let the other ones bubble up. It is well explained in Martelli's
Python in a Nutshell.

I have seen try/catch construct in Java and Javascript, and it is probably
less readable, and certainly can be over-used, but in Python, returning None
in all exceptional cases is annoying, different issues got merged into a
single "Muted" case.

------
kstenerud
Rule #1 of programming articles: Be wary of anyone who states "always" or
"never" or "harmful" or "evil" with regards to a code construct/idiom/pattern.

What they're telling you, in effect, is that they know everything there is to
know about X, and so you should trust their judgment and not think for
yourself.

And in every case I've seen so far (including the famous goto debacle), they
have merely focused on their pet code, ignoring the actual cases where the
construct is useful (most often, they'll include trivial examples when the
construct is meant to be used in complex situations), and this article is no
exception.

When your argument becomes "we should throw this out because bad programmers
use it badly", you have no argument.

------
AndrewDucker
I think that having alternatives to try/catch is generally worthwhile.
Exceptions should only be thrown in exceptional circumstances.

So (to take some C# code as an example), I'd only call int.parse(myString) if
it really, really should only get called when myString contains something
that's parseable to an integer.

If it just probably contains an integer, then I'd call int.TryParse(myString,
out returnedInt), and check the boolean return to see whether it was valid.

------
josscrowcroft
This part really sunk in for me in Bruno Jouhier's reply:

> _"So the big mistake I've always seen people make is being too "nervous"
> about exceptions and feeling that they have to do something about them as
> close as possible to the point where the exceptions were raised. They need
> to the exact opposite: feel relaxed about exceptions and let them bubble
> up."_

Hadn't really thought about it in that way, but I find myself employing this
pattern where possible - having said that, with async callbacks it can be hard
to bubble up when you essentially have multiple logical processes occurring.

Haven't found a solution to this in my own codez yet but I feel a little
twitch in my eye whenever I have to do:

    
    
        try {
            something = JSON.parse ...
    

because there's no native `JSON.validate` method.

Not complaining though, definitely not worth losing sleep over .. yet.

~~~
ecaradec
> "So the big mistake I've always seen people make is being too "nervous"
> about exceptions and feeling that they have to do something about them as
> close as possible to the point where the exceptions were raised. They need
> to the exact opposite: feel relaxed about exceptions and let them bubble
> up."

While the principle seems to make sense, I find it unusable with GUI like
apps. GUI have hundreds of entry point from various events. If I want to catch
exceptions far from their raise I need to do it in each one of those event
handler, so that they don't bubble through librairies, literally hundreds of
times.

Every beautifully written code I've seen has always been CLI programs.
Exceptions probably works for server code where you can crash your single
process and/or redo it from your original request. GUI and frameworks apps
can't really use crash as an acceptable behavior.

~~~
slowpoke
_> Every beautifully written code I've seen has always been CLI programs._

I might be a fairly novice coder, but I think a good solution for this would
be a client/server model. I'm a full-time Linux user and for various reasons,
I really like application which have a daemon mode, with the UI built as a
client accessing said daemon. Good examples are mpd or deluge.

This way, the actual code can handle handle most, if not all exceptions and
errors at the "CLI level" and expose meaningful error codes to the "API" that
the UI uses. At least that's how I'm trying to build my first bigger software
project (an image viewer).

Again, I'm a fairly inexperienced programmer, so this might be a foolish
suggestion, but I try to give much thought to the way I'm building software.
Maybe even too much, I'm kinda prone to over-engineering.

------
jronkone
> At least JavaScript doesn't have typed catches. Holy black mother of
> darkness, that shit is intolerable.

Well, if his complaint is about untyped catches, well, I guess they must suck.
But if he thinks that typed catches are somehow worse (hint: they're better),
he is wrong.

> But still, nothing is as bad as the common "On Error Resume Next" that so
> many terrible VB programs start with.

Ok, everyone who always checks the return value of printf() in C, raise your
hands. That's what I thought. The semantics of C is that whenever something
fails, you just continue from the next line (statement, whatever...) even if
the world is burning. Try-catch may suck, but checking for return values sucks
more (in languages without pattern-matching), deal with it.

------
Strilanc
Note to everyone: don't confuse 'try-catch sucks' with 'exceptions suck'.

There's no inherent reason an exception-throwing method can't be invoked in a
style that gives back a value/exception pair. That limitation is a property of
the specific language, not all languages. For example: exceptions could be
caught succinctly if method invocations prefixed with 'catch' returned a
value|exception tuple (`v, ex = catch ParseInt("test")`).

The primary difference between exceptions and error codes is the default
behavior. Error codes default to 'ignore' and exceptions default to
'propagate'. The rest of the differences (requiring acknowledgement,
succinctness of handling, availability of stack traces) are more of a
coincidence based on the common decisions in languages like Java, Go, C, C++,
python, etc.

I think the main downside of existing exception implementations is they
discourage programmers from defining useful error cases for functions. It's so
easy to propagate or suppress that anything else feels like massive busy work.
Simple improvements would be to make wrapping exceptions easy and defining new
types easy (throws FileNotFound as forge MissingNetConfigFile).

On the other hand, the main downside of error code implementations is the ease
of ignoring. Go has a very good idea in the "x, _ = funcWithIgnoredError"
style, but it extends poorly to otherwise-void methods like 'flush stream'.

------
exDM69
An interesting read. It's always nice to see people stating their unpopular
opinions with proper reasoning.

The author's point of view seems to be pretty Javascript/Node.js -centric. I
can relate. try/catch and node.js -style asynchronous coding do not work
together. However, I'd say that it's the async programming model that is
broken, not try/catch.

Writing code in node.js async style is very difficult for humans to do (at
least for me), but compilers are excellent in this type of program
transformations (continuation passing style). The Haskell and Erlang compilers
and runtimes do this automatically. You write regular imperative code but it's
executed in an asynchronous manner using co-operative fibers (maybe together
with native os threads).

A similar solution is possible with an interpreted language that has some kind
of continuations. Every blocking system call could be translated to a co-
operative fiber context switch using kqueue/epoll.

What is really baffling to me is that none of the popular interpreted
languages (javascript, python, ruby) seem to have decent continuations. I
don't know of a reason for not implementing continuations in an interpreter,
is there any? Using continuations, one could implement pretty nice async
operations and exception handling.

Using return values for error handling is really painful. I've been writing a
system call heavy C program, and about 40% to 50% (in LOC) of the code is
error handling. In test code coverage, I can reach just over 50% branch
coverage as most error conditions don't happen while testing. I don't know of
a nice way to make e.g. the network fail at the right time to test my error
handling in that case.

C++ is pretty nice when it comes to error handling and exceptions as resources
are cleaned up on error. Python's with..as statement or Haskell's
Control.Exception.bracket can do the same thing (the latter is just a regular
function, not a language builtin). In C, most error handling is required to
free some memory you allocated earlier, but even in a garbage collected
language you still have system resources you need to free (file handles,
sockets, db connections etc).

~~~
nogden
>> However, I'd say that it's the async programming model that is broken, not
try/catch.

This is an interesting point of view, however I'm inclined to disagree on the
basis that the async model is representative of how things actually happen;
the imperative model is not.

In an asynchronous architecture, the developer is concerned with only the
current state and the set of all events that may cause a transition from that
state. Whether or not an event is the result of an error condition is
irrelevant, one simply invokes a different handler.

I used to consider the asynchronous model much harder to program in, that is
until I changed my thought process to work in terms of state machines. Then it
actually became much simpler to program in this model since, at the handler
level, one has no preconception about what should happen next, only what can
happen next and how to handle each of those situations.

>> C++ is pretty nice when it comes to error handling and exceptions as
resources are cleaned up on error.

There has been many a discussion about the benefits vs. pitfalls of C++
exceptions. I personally am not a fan since the lack of garbage collection
means that there are situations that require lots of tedious and error prone
boilerplate code just to ensure that all resources are cleaned up.

I have to admit that perhaps the nicest way I have seen of handling failure is
in the concept of Monads in Haskell (and other functional languages). The
ability to add the context of failure as a possibility in any computation,
without requiring that computation to explicitly consider it is extremely
elegant. Add to that the safety of always returning a well defined value that
is fully type checked by the compiler and I believe you have a recipe for very
effective error handling.

~~~
jerf
"This is an interesting point of view, however I'm inclined to disagree on the
basis that the async model is representative of how things actually happen;
the imperative model is not."

Have you _used_ Erlang or Haskell? I have heard this line of defense a number
of times, but always from people who have to answer "no".

The claim is that it's better to let the compiler handle the issues. You're
response is basically that since you learned how to more properly run what the
compiler does in your head, you've done better. I would submit that's
_support_ for the idea that a compiler should be doing the work, not evidence
against. What if you could skip the part where you didn't know how, then skip
the part where you had to learn better, and then skip the part where you're
running this algorithm in your head, and just let the compiler do it for you
from day one, and probably better than you can do it even with experience?

(Humans _suck_ at maintaining invariants. It takes years of experience to even
be sort of good at it, and you'll still be terrible compared to a compiler.)

While you should know what the compiler is doing to your code, this is hardly
any different than manually implementing the C stack on every function call.
The fact that it may occasionally let you do something clever doesn't get
around the fact that you really shouldn't be thinking about this on every
single function call _at all_.

------
ericflo
Bruno Jouhier's response nails it. It's funny, but for whatever reason I
almost always disagree with Isaac's (perfectly reasonable) points of view.

------
srgseg
The best part about exceptions (unchecked), is that they allow an exception to
automatically bubble upward to the level of code that actually should be
responsible for taking action, without having to write a ton of boilerplate
code all the way down the call chain.

Even better, when stack traces in exception logs are examined, it's easy to
see where in the call chain things are going wrong.

During a database transaction for example, something might go wrong 5 method
calls deep, and I should roll back the transaction.

If I'm forced to either use checked exceptions or use if statements to check
whether there was success at each level, then that's a lot of repeated
boilerplate try/catch/rethrow or if(success) code which unchecked exceptions
free me up from having to write.

If all I receive at the transaction level is a failure code, then what do I
log, other than "something went wrong?!". With exceptions, all the work is
done for me. I just log the exception stack trace and I can easily see what
needs to be fixed.

Much of the time, exceptions are useful because they are thrown becuase of
something you _didn't anticipate_ happening, as opposed to something you had
planned for when writing the code. And when that unanticipated thing does
happen, there is nothing more beautiful than an automatically generated stack
trace telling you exactly what happened.

------
dasil003
Exceptions vs error codes seems closely related to dynamic vs strong-typing
ala Haskell.

If you use error codes exclusively, then it's possible to do much stronger
reasoning about all code paths, and you can create a more robust system. It
doesn't have the magic of the static analysis that Haskell's type system
enables, but there is a certain "purity" in that no function can have an error
pass through it in the call stack without being aware of it. This is exactly
what you need in systems programming like an OS kernel where every conceivable
thing will go wrong and your goal is to handle _everything_ gracefully.

On the other hand, when writing business apps, you not only have the technical
concerns of the system, but your program is primarily a big hairy rat's nest
of business logic which may never achieve well-definition. In such
circumstances, you never get to a point of stability where you can be so
pedantic about the technical details because you have your hands full just
trying to meet the ever-changing business requirements. Exceptions are a great
help here because they allow you to handle new errors with minimal ceremony
and function redefinitions. Just like Ruby duck-typing, this means you can
refactor faster, but obviously in a dramatically less robust fashion than
Haskell. But if you're operating in an environment where a logical failure
here or there is acceptable and code has a short half-life, then you'll get
better ROI from duck-typed, unit-tested, exception-notifying code than you
will from nearly-impossible-to-fail statically-typed code.

A lot of code probably falls into a gray area where it becomes largely a
matter of programmer taste.

------
vog
This reminds me of the Qt framework which is C++ but doesn't make any use of
exceptions.

Before I worked with Qt, I never thought this was possible without sacrificing
the API, but the Qt API is very clean and doesn't seem to suffer from that
design decision. So I looked around in the Qt API for some time, trying to
learn how they managed to get along without exceptions. And I think I finally
got it.

The whole error handling in Qt mostly boils down to providing _sensible null
objects_. That is, instead of using NULL pointers (as in C) or generic null
objects (as in JavaScript), for all kinds of objects there are specialized
null objects which behave sensible to the most possible extent.

------
perfunctory
The core of the post:

    
    
      try/catch, which blurs the line between errors
      that are *mistakes* (accessing a property of null,
      calling .write() on a stream after .end(), etc.), 
      and those which are expected application-level problems
      (invalid data, file missing, and so on).
    

Totally agree.

~~~
morsch
In a typed exception system, you just assign different kinds of exceptions to
all of those. Java also distinguishes between checked exceptions ("known
unknowns") which need to be declared, and runtime exceptions ("unknown
unknowns"). Sure, you can still catch all of them in one place, but that's
very rarely a good idea.

A non-typed exception system (that doesn't solve the problem with another
mechanism) just sounds like a very bad idea. Is Javascript like that?

------
zbowling
This is the standard in many languages and one that I believe that java got
wrong. It was a little rushed with the whole concept of forcing everyone to
handle all exceptions concept and building so many exceptions in for silly
things like connection failures and parsing errors that reasonably can be
expected to happen constantly in the normal runtime of an application.

I follow the rule in almost every language that my app should still be able to
run normally if all try catches were remarked out, otherwise I should be
handling something better. Exceptions are expensive and don't always back out
nicely when they unwind in most languages.

In Objective-C, we play C rules more often then not and almost never use
exceptions (except for assertion exceptions). Rather most errors have a ( out
NSError __) pointer if they need to pass up errors.

In Python, it's more pythonic to ask for forgiveness than to check before hand
so exceptions happen all the time. I'm not sure how I feel about that though
but it seems to work well enough.

~~~
dolbz
> It was a little rushed with the whole concept of forcing everyone to handle
> all exceptions

Just to clarify, Java doesn't force you to handle _all_ exceptions (maybe it
did at one point in time?). Exceptions which inherit from RuntimeException are
unchecked and you can choose whether to handle them or not. Exceptions which
don't inherit from RuntimeException are checked

~~~
Sandman
That being said, I'd like to quote these passages from the official Java
tutorial:

 _"Generally speaking, do not throw a RuntimeException or create a subclass of
RuntimeException simply because you don't want to be bothered with specifying
the exceptions your methods can throw.

Here's the bottom line guideline: If a client can reasonably be expected to
recover from an exception, make it a checked exception. If a client cannot do
anything to recover from the exception, make it an unchecked exception."_

Anyway... you don't need to neccessarily handle exceptions even if they are
checked (in cases where it doesn't make sense that your code handles them).
Add a 'throws' clause and let the calling code handle them.

So the poster of the grandparent comment really got it wrong when it comes to
Java and exceptions.

~~~
dolbz
> you don't need to neccessarily handle exceptions even if they are checked
> (in cases where it doesn't make sense that your code handles them). Add a
> 'throws' clause and let the calling code handle them.

The problem with this is it affects the signature of _all_ methods in your
call hierarchy up to the point where it's handled as you're forced to declare
that you throw exceptions. A simple change in one class could quickly become a
breaking change involving several classes just because an exception can now
occur.

------
tef____
''Try/catch is goto wrapped in pretty braces.''

From this I can infer that lambda is also an anti pattern.

But as good as this straw man post is, yes you can mis-use exceptions, No that
doesn't make it an anti pattern.

~~~
Jach
Glad I wasn't the only one thinking of Lambda: The Ultimate GOTO

------
phzbOx
I think the best is a little of both ways. For instance in python:

    
    
      x = some_dict['meh']
    

Will raise if 'meh' doesn't exist. If you believe that 'meh' should be there
in your program, it's fine to 'let it raise an exception'.

However, if 'meh' _could_ be there, it's better to use an error style such as:

    
    
      x = some_dict.getDefault('meh', 'some-neutral-value')
    

And continue without raising because there's _no need_ to raise as there's
nothing exceptional here.

Ideally, raising an exception should have the meaning "Hey, something is wrong
here and I don't know what to do next". And _someone_ in the call hierarchy
would handle this and say "Oh, the connection stopped.. that's why it's not
working. I'll reconnect and call you again".

And note that this "parent handling" might be way higher than where the
problem occurred..

~~~
gaius
You mean collections.defaultdict
[http://docs.python.org/library/collections.html#collections....](http://docs.python.org/library/collections.html#collections.defaultdict)

~~~
TylerE
No he doesn't. defaultdict provides a default value for ALL missing values.
His approach allows him to target one key in particular, and is a very common
python idiom.

~~~
gaius
I'm confused - where does that come from?

    
    
      >>> x.getDefault(1, 'Que?')
    
      Traceback (most recent call last):
        File "<pyshell#2>", line 1, in <module>
          x.getDefault(1, 'Que?')
      AttributeError: 'dict' object has no attribute 'getDefault'

~~~
TylerE
Because it's actually (somewhat confusingly, called setdefault).

    
    
        >>> a = {1:'foo',2:'blah'}
        >>> a.setdefault(1,'default')
        'foo'
        >>> a.setdefault(4,'default')
        'default'
        >>> a
        {1: 'foo', 2: 'blah', 4: 'default'}
    

This actually updates the underlying datastructure. Very handy when you want
to deal with nested structures, since you can do something like:

    
    
        matches.setdefault(keyword,[]).append(s)
    

This will append s to the list in matches[keyword] if it exists, or set
matches[keyword] to [] and then append.

------
albertzeyer
OT: Was it always like this that the top 170px are just fixed on
groups.google.com and thus are somehow wasted from the horizontal space?

[http://www.az2000.de/pics/screenshots/Screen%20Shot%202011-1...](http://www.az2000.de/pics/screenshots/Screen%20Shot%202011-11-14%20at%2012.29.56.png)

~~~
ww520
OMG. That is a horrible UI. It makes Groups totally unusable. Half of my
laptop vertical screen is the unscrollable fixed panel with a few big buttons
and the search bar. Then 1/4 of the horizontal screen is fixed with the left
navigation links. The actual content are crammed on the lower right half of
the screen. The bottom arrow of the scrollbar is missing. Please Google, not
everyone has 24-inch vertical monitor.

It used to be that Microsoft's website took up lots of fixed upper screen real
estate for "branding." Now they have fixed it and is much more usable. Have
the Microsoft designers gone to work for Google now?

------
adamjernst
Isaacs is spot-on, and this is one reason why I love Objective-C. NSException
is for _programming_ errors, which should be rare. You can use try/catch, but
you almost never do. NSError is for expected application-level errors.

~~~
reidmain
This is exactly what I came here to say. NSException and NSError combined with
the fact that you can send messages to nil objects and just get nil objects
back lead to much "cleaner" code in my opinion.

------
ianso
I think he has a really interesting POV but the conversation as a whole is
definitely a very informative dialog on the value of try/catch in an async
environment. As they say, read the whole thing...

------
colanderman
I think a lot of programmers use try/catch when what they really want is
Prolog-style failure:

    
    
      my_parser(String, Output) :-
    
        parseJSON(String, JSON),
    
        extract_the_values_I_want(JSON, Output).
    
     if my_parser(String, Output) then
    
      ...do stuff with Output...
    
     else
    
      ...show user "couldn't parse" message...
    

I've been using Mercury (a strongly typed Prolog) for years now and have never
once found the need for exceptions (which Mercury supports) to indicate
anything other than catastrophic failure.

~~~
sgt
What are your motivations for using Mercury? Are you using it for hobby
projects, research/academic purposes or actual production code?

~~~
colanderman
Hobby projects and hobby research. It has some pretty intense features if
you're into type theory.

IMO the library support isn't yet there for production code (e.g. no or only
rudimental GUI/networking code). But if you're just using it for computation
(e.g. AI or compiling or some such) it's pretty solid, and its FFI is the most
straightforward I've ever seen.

FWIW I hear that these people: <http://www.missioncriticalit.com/> and these
people: <http://www.princexml.com/> use it for production code, but I don't
know much about them, beyond that Håkom Wium Lie (of CSS and Opera fame) is
the Director of the latter.

I maintain a blog about Mercury's features for interested users:
<http://adventuresinmercury.blogspot.com/> if you're interested.

~~~
sgt
I'm definitely interested. Thanks. I've now bookmarked your blog.

------
FuzzyDunlop
The way I see it exceptions are helpful in creating self documenting code _if_
used thoughtfully and effectively.*

Some might argue subclassing the base exception to create more specifically
named ones is a bit silly, because you may be doing little more than renaming
a class several, maybe many, times. But it may be countered that this is
simpler than determining a list of error codes and then leaving it to other
people to find out what a random string or integer even means. I personally
haven't found this helpful for debugging.

Both ways are better than something just returning false and leaving you to
figure out why it did that in the first place.

The main benefit to me, however, is to be able to throw the exceptions deeper
in the code (where appropriate) and to be able to then catch them at the very
last moment. While not perfect (I can't account for everything), it allows me
to keep my error handling code cleanly separated from the rest of it at the
most abstracted point. Anything lower level will by necessity be a little
messier.

 _eg. not wrapping an_ entire* script in one try/catch block.

------
je42
I dont understand this. According to some believe exceptions should be used in
an exceptional context. Can anybody define what exceptional is?

I use exception to get the context in which the error happend. Call stack,
variables, stuff like that. I can log it, i can give good error message to the
user. I never think about if an error is exceptional.

------
qntm
> Try/catch is goto wrapped in pretty braces. There's no way to continue where
> you left off, once the error is handled.

...

> But still, nothing is as bad as the common "On Error Resume Next" that so
> many terrible VB programs start with.

Assuming "On Error Resume Next" does what I think it does, you can't complain
about both of these at once.

~~~
michael_dorfman
"On Error Resume Next" does _not_ do what you think it does. It ignores the
error, and marches blithely on. That is very different than "continuing where
you left off, once the error is handled."

~~~
mattdeboard
One of my personal pet peeves is seeing: "try: foo() \ except: pass" in code.

------
jakejake
I think a good rule is that if you're not planning (or able) to confidently
deal with an exception and put the app into a known state, then don't catch
it.

Some developers seem to have a fear of allowing any errors to be seen by the
user and so they have a habit of swallowing exceptions. I guess it makes them
feel that the code is more stable, but it actually masks bugs and makes them
impossible to troubleshoot. I have a name for these - I call them "insidious
bugs" especially when they result in data loss, which is common with those
types of bugs.

I was just in some code the other day that was littered with a bunch of these:

try { ... code here ... } catch(e) {}

not even a console statement or anything! It took two of us over 6 hours
chasing down what should have been a 15 minute bug due to that code.

------
Daishiman
The problem with try/catch, really, is that it makes a tremendous amount of
visual noise, which makes sense for critical operations that might crash
everything, but just don't make sense when you can tolerate certain errors or
checking for proper output with with a conditional looks better and is more
appropiate.

Obviously then you have an issue with language conventions. How do you check
for the returns of a method that does an in-place modification without
returning anything? What happens when Null is a proper return value? What
about having to access specific members or registers to check?

Perhaps, much like logging frameworks, we may need to categorize throwable
actions by their importance, or think of a new convention that can handle such
things more easily.

~~~
wladimir
But what is your alternative? Doesn't checking the return code of every
statement also make a tremendous amount of visual noise?

I've seen C code where about 80% of the code is dedicated to error handling
and corner cases. Before every statement it has to check the current error
status, and after it the return value... it's almost unreadable.

With try/except/finally a lot of that can be cleaned up, by handling the
errors higher-up in the call hierarchy where they make sense to handle.

I see a lot of critique of exceptions, but haven't seen one better alternative
yet, at least one that doesn't require switching to an obscure programming
language.

Edit: this does assume that exceptions are used properly: for errors that
should bubble up, not as extra return value.

------
epenn
_try/catch, which blurs the line between errors that are mistakes (accessing a
property of null, calling .write() on a stream after .end(), etc.), and those
which are expected application-level problems (invalid data, file missing, and
so on)._

While I tend not to be a big fan of try/catch myself, one thing I like about
Objective C and Cocoa is that they at least provide classes to separate
programmer mistakes (NSException, used with try/catch) from application issues
(NSError, used however it best fits the app). If the try/catch pattern must be
used, I think its sensible to have this kind of separation of
responsibilities.

------
ghc
How on earth can types catches be worse than untyped catches? As a Pythonista,
this whole argument makes my head hurt. He's railing against everything
considered beautiful about exception handling in the Python community.

------
jorangreef
"The really nice thing about the cb(error, result) approach is that it
constantly reminds the programmer that you _must_ acknowledge that failure may
come from any request you make."

------
shin_lao
As we say in C++ "exceptions should be exceptional".

------
romaniv
Exceptions can make your code much shorter and readable by the virtue of
centralized error handling. However, try-catching can be pretty ugly in cases
when you need to handle exceptions right away. D language has scope guards to
help this. I think we need some kind of better syntax to handle cases like
this, but getting rid of exceptions entirely is a bad idea.

------
efsavage
Try/catch may be an anti-pattern in a dynamically-typed language, but not in a
static-typed language. In static languages they're important and valid because
they help avoid the "returned a null what?" question.

~~~
apl
Exceptions aren't the one and only solution to 'null' issues in Java et al.

<http://blog.orbeon.com/2011/04/scalas-optionsomenone.html>

------
th0ma5
Anyone have an opinion with regards to Ada is this context? From what I've
read from Ada it tries to do soft errors as a rule.

------
enduser
It's only an anti-pattern in languages that don't have resumable conditions
(i.e. other than Common Lisp).

------
klodolph
I'm not sure how I'm supposed to read this... I guess I need an account?

~~~
dchest
Sometimes Google Groups ask for an account, sometimes don't -- not sure why.
Anyway, here's the post: <http://pastie.org/2861111>

