Exception as the primary error handling mechanism?

Steven D'Aprano steven at REMOVE.THIS.cybersource.com.au
Tue Jan 5 03:26:10 EST 2010


On Tue, 05 Jan 2010 02:31:34 +0000, r0g wrote:

> A pattern I have used a few times is that of returning an explicit
> success/failure code alongside whatever the function normally returns.

That doesn't work for languages that can only return a single result, 
e.g. C or Pascal. You can fake it by creating a struct that contains a 
flag and the result you want, but that means doubling the number of data 
types you deal with.


> While subsequent programmers might not intuit the need to test for
> (implicit) "magic" return values they ought to notice if they start
> getting tuples back where they expected scalars...

What if they're expecting tuples as the result?



> def foo(x)
>     if x>0:
>         return True, x*x
>     else:
> 	return False, "Bad value of x in foo:",str(x)
> 
> ok, value = foo(-1)

Oops, that gives:

ValueError: too many values to unpack


because you've returned three items instead of two. When an idiom is easy 
to get wrong, it's time to think hard about it.



> if ok:
>     print "foo of x is", value
> else:
>     print "ERROR:", value


Whenever I come across a function that returns a flag and a result, I 
never know whether the flag comes first or second. Should I write:

flag, result = foo(x)

or

result, flag = foo(x)



I've seen APIs that do both.

And I never know if the flag should be interpreted as a success or a 
failure. Should I write:

ok, result = foo(x)
if ok: process(result)
else: fail()

or


err, result = foo(x)
if err: fail()
else: process(result)


Again, I've seen APIs that do both.

And if the flag indicates failure, what should go into result? An error 
code? An error message? That's impossible for statically-typed languages, 
unless they have variant records or the function normally returns a 
string.

And even if you dismiss all those concerns, it still hurts readability by 
obfuscating the code. Consider somebody who wants to do this:

result = foo(bar(x))

but instead has to do this:


flag, result = bar(x)
if flag:  # I think this means success
    flag, result = foo(x)  # oops, I meant result

Again, it's error-prone and messy. Imagine writing:


flag, a = sin(x)
if flag:
    flag, b = sqrt(x)
    if flag:
        flag, c = cos(b)
        if flag:
            flag, d = exp(a + c)
            if flag:
                flag, e = log(x)
                if flag:
                    # Finally, the result we want!!!
                    flag, y = d/e
                    if not flag:
                        fail(y)
                else:
                    fail(e)
            else:
                fail(d)
        else:
            fail(c)
    else:
        fail(b)
else:
    fail(a)



Compare that to the way with exceptions:

y = exp(sin(x) + cos(sqrt(x)))/log(x)


Which would you prefer?




-- 
Steven



More information about the Python-list mailing list