My first observation is that there are really *three* types of
exceptions: bad arguments, programming errors, and "expected
problems". The distinction between the first and the second isn't
always clear, but in theory it is possible, and in practice often
desirable, to protect your code against bad arguments. E.g. if an
argument must be a non-empty list of strings, a few straightforward
type checks can verify this. Against programming errors, there is no
defense (if you knew one, you'd be busy becoming filthy rich by
selling it and you wouldn't be reading this :-), and one would hope
that these indeed result in a program crash. Try..finally is for
essential clean-up and state-saving operations in such cases.
Now the expected problems. Try statements are clumsy and have the
disadvantages mentioned in earlier mails. Suppose we introduce a new
built-in object type, the "letter bomb", and a new operator,
"attempt(expression, list_of_exceptions)". (There's a problem with the
parser here so it can't be called "try()".) Attempt() evaluates the
expression and returns its value, if all went well. If the expression
raise an exception that is not in the list of exceptions, it raises
this exception. If the expression raises an exception that is in the
list of exceptions, attempt() does not raise an exception but instead
returns a letter bomb. Through a simple type comparison the receiver
of the value can test whether the resulting value is a letter bomb or
not. If it is one, the only valid operations are to extract the
exception type and value from it. This must be possible in order to
print a decent error message, e.g. in the case of "file: cannot open".
filename = attempt(sys.argv, IndexError)
print 'usage: cat filename'
f = attempt(open(filename, 'r'), IOError)
print filename + ': cannot open:', filename.exc_value
Using a bomb in another way, e.g. calling f.read() when f is a bomb,
will raise an error appropriate to misuse of a general object (e.g.
AttributeError) -- not the original exception. This is logical, since
you can only get a bomb object when you ask for it (user-defined
functions are still required to raise exception, not to return
bombs!), and in that case you should handle it very carefully!
This doen't mean that I don't like the try...except...else... idea; in
fact I like that as well (and it's easier to implement :-), but the
letter bomb idea may have a different application area. Note that if
the first argument of attempt() is a bomb, it is returned unchanged,
so it is possible to make nested attempts.
Implementation note: the parser has to recognize attempt as a keyword,
since this is a "special form" (for you crypto-lispers :-) which
evaluates its first argument only after it has been called.
Regarding Jaap's idea of restarting operations: I'm afraid this is
totally unrealistic, and only rarely can exceptional situations be
mended in a useful way anyway. E.g. if list[i] fails because i is
beyond the length of the list, a mending routine might want to patch
either i or list, but neither option is realistic. In any case it
would require a complete overhaul of all the implementation aspects of
exceptions, which isn't likely to happen (well, maybe when Doug Moen
gets around to implementing Python++ :-).
PS Jack receive credit for coming up with the letter bomb idea, but
the name is mine. I guess us Python types have a somewhat anarchistic
sense of humor. :-)
--Guido van Rossum, CWI, Amsterdam <Guido.van.Rossum@cwi.nl>