What c.l.py's opinions about Soft Exception?

Lie Lie.1296 at gmail.com
Sun Mar 9 18:45:33 CET 2008


On Mar 9, 7:54 pm, Steven D'Aprano <st... at REMOVE-THIS-
cybersource.com.au> wrote:
> On Sun, 09 Mar 2008 00:30:51 -0800, Lie wrote:
> > (3) Informing codes above it about what's currently happening inside,
> > the thing is just a mundane report that might be useful to codes above
>
> > Which might be a useful place to use SoftExceptions
>
> Okay, now we're getting somewhere.
>
> So, I have a function foo() which raises a HardException on error, but it
> also raises a SoftException if it wants to notify me of something
> "mundane".
>
> def foo(sequence):
>     if args == []:
>         raise SoftException("empty list")
>     return len(args)
>
> Now, how do I use that?
>
> try:
>    x = foo(something)
> except TypeError:
>    print "you should pass a sequence"
>    sys.exit()  # unrecoverable error
> except SoftException:
>    print "you passed an empty list"
> print x
>
> Am I close?
>
> But there's a problem. Once the SoftException is caught, execution will
> continue from the line "print x" -- but the function foo() never got a
> chance to actually return a result!

In that particular case above, you don't need to handle the Soft
Exception. As stated in it's original purpose, you don't need to
handle a Soft Exception, it exists there if you need to handle the
exceptional case, but ignorable on other cases. _IF_ printing the "you
passed an empty list" is an important operation, you'd make it like
this:
    try:
       x = foo(something)
    except TypeError:
       print "you should pass a sequence"
       sys.exit()  # unrecoverable error
    except SoftException:
        print "you passed an empty list"
        x = 0
    print x

or alternatively:

    try:
       x = foo(something)
    except TypeError:
       print "you should pass a sequence"
       sys.exit()  # unrecoverable error
    except SoftException:
        print "you passed an empty list"
    else:
        print x

The case you states above only mentions that you haven't fully grasped
the workflow in exception-based system.

> In order to make that work, you would need a significant change to
> Python's internals. I don't know how difficult that would be, but I'm
> guess that it would be a lot of work for not much benefit.

I too don't know how difficult that'd be, especially because the only
thing I know about Python's internal is it implements its garbage
collector by ref counting (which is barely useful here).

> But even if that happened, it would mean that the one mechanism has TWO
> different effects:
>
> try:
>     x = foo(sequence)
> except SoftException:
>     print x  # this is okay, because foo() did return
> except TypeError:
>     print x  # this is NOT okay, because foo() never returned
>
> That is a recipe for confusion.

Both are not okay since foo() never return on both cases (soft
statement doesn't return to finish the try clause except if explicitly
resumed).

(snip)
> > Perhaps relabeling it as Warning, and renaming raise SoftException as
> > give Warning might make it more acceptable?
>
> Do you realise that Python already has a warnings module?

Ah... I completely forget about it, but anyway the Warning module is
unrelated to this. Perhaps we could just think of another name, but
names doesn't seems to be important in Python modules anyway, can you
guess what pickle/zip/mutex/quopri/curses is if you're new to Python
without looking at the documentations? There are some modules in
Python that have weird names, we ought to reduce it, but we can't
reduce it... we ought to live with it.

(snip)
> > A possible solution to this problem might be to check whether distance
> > is less than P1.radius + P2.radius in the calculateforce. But, this
> > obfuscate the code since we have to separate distance calculation from
> > the main formula (see !s),
>
> I don't agree that this obfuscates the code.

For a formula as complex as this:
http://www.mapleprimes.com/blog/axel-vogt/computing-the-complex-gamma-function-using-spouges-formula
it might be useful to fragment the formula to smaller pieces

but in a simple to moderately complex that still fits in one line,
fragmenting the code confuses the eye.

> And here's a way to do it that doesn't calculate anything twice, and
> doesn't require any exceptions:
>
> def calculateforce(P1, P2, dist):
>     return (P1.mass - P2.mass)/dist
>
> And then for all pairs of particles:
>
> dist = distance(P1, P2)
> if dist <= P1.radius + P2.radius:
>     clump(P1, P2)
>     break
> F = calculateforce(P1, P2, dist)

That... is the worst solution that could ever be suggested. The
Particle objects already contain their own position, supplying dist
overrides their real distance of the particles and also it may make
bug holes by supplying bogus distance:
    def calculateforce(P1, P2, dist):
        return (P1.mass - P2.mass) / dist

    calculateforce(P1, P2, 'bogus data')

or simply by passing dist that isn't equal (P1.X - P2.X) ** 2 + (P1.Y
- P2.Y) ** 2.

If you want to do it that way, it'd be much better if you go fully
functional:
    def calculateforce(P1_mass, P2_mass, dist):
        return (P1_mass * P2_mass) / dist ** 2

but it would be a pain in the neck to manually dismantle the P1.M,
P2.M, dist every time you call the calculateforce.

btw, on an unrelated note, the original formula I supplied for gravity
calculation is incorrect, it should be (P1.M * P2.M) / dist ** 2
instead of (P1.M - P2.M) / dist, but that's just physics not python.

> > A much better solution would be to use SoftException
> >     def distance(P1, P2):
> >         D = (P1.X - P2.X) ** 2 - (P1.Y - P2.Y) ** 2
> >         if D <= P1.radius + P2.radius:
> >             raise Collision
> >         return D
>
> But look at that line, "raise Collision". You could replace that with a
> callback function, and have the same result:
>
>     DO_NOTHING = lambda : None
>
>     def distance(P1, P2, callback=DO_NOTHING):
>         D = (P1.X - P2.X) ** 2 - (P1.Y - P2.Y) ** 2
>         if D <= P1.radius + P2.radius:
>             callback()
>         return D
>
> That gives you virtually everything you want. If you want to ignore the
> signal, you simply call distance(P1, P2). If you want to do something on
> the signal, you set the callback.

Guess where it goes after the callback finishes? It goes to "return
D". And if the calling code hasn't been notified that P1 and P2 has
already been clumped by the callback, it'll refer to an inexistent
object.
Soft Exception don't do that, it terminates the distance calculation
and goes to the handler and won't go back to finish distance
calculation unless explicitly told to (by using resume).
And another weakness, it'd be a pain in the neck to handle more than
two or three separate callbacks. You can define as many Soft Exception
as you want without cluttering the function's "interface" (please find
a better diction for "interface")

> But frankly, the function distance() is NOT the place for that. Why
> should the distance() function decide what's a special result and what
> isn't? With your plan, you end up with functions like this:
(snip)
Because when a calculation of distance is lower than the sum of their
radius, it means the two objects shared the same space (!this is
impossible but not problematic for calculations if ignored!), that
would be an event of interest to codes that handles the movement,
clumping, etc but codes that just wants to print the distance to
screen wouldn't want to care about it.

> Every module and function that calls distance() will start demanding that
> it raises the SoftExceptions that *it* wants, and before you know it,
> your distance() function has a hundred SoftExceptions covering all sorts
> of things that most people don't care about.
>
> No. This is a terrible idea. If the caller wants to treat a particular
> result as special, the caller should be responsible for detecting that,
> not the callee.

No the callee isn't responsible for detecting the exceptional result,
the caller is responsible for handling the exceptional result, while
the callee is responsible to notify the caller. In some cases it might
be impossible for the caller code to determine the exceptional cases
or probably the cost of doing so is nearly equal to the cost of doing
the whole calculations (but completing the calculation would result in
garbage out).

(snip)

Steven says:
> Just out of curiosity, are there any existing languages that do something
> like this, or did you invent it yourself?
On Mar 9, 8:21 pm, "Diez B. Roggisch" <de... at nospam.web.de> wrote:
(snip)
> Is this soft-exception implemented anywhere, so that one can see what
> experiences and best practices have evolved around using it?

Actually I "invent" it myself (or probably I'm just ignorant enough
not to know any), that's why I expect there'll be a lot of weaknesses
I haven't foreseen (two heads are better than one, and a bunch of
heads are better than two). And it is also the reason why I'm asking
here for others' opinions, to bake the ideas, and possibly getting it
implemented when it's baked enough or to have other's opinion on why
it's bad and why it shouldn't exist.

<rant>If there is a reason why I chose here particularly, it'll be
because I think Python (AFAICS) have a lot of interesting features:
list comprehension, generator functions, for-loop semantic, just to
name a few of them (well, maybe it's not *that* unique to you, but
coming from a VB background[1], such features are extremely unique to
me) and I love them and use them heavily.</rant>

Another reason might be that Python is a dynamic language that have
adopted preference for try block compared to if block. And I think the
idea fits well enough with Python's ideologies.

<rant>Still another reason is because Python is not controlled by a
Impotent Company For Life but only by a Benevolent Dictator For Life.</
rant>

<rant>Yet another reason is simply because Python is currently my
active language.</rant>

[1] <rant mode="inherited">Although since I picked Python, I've yet to
touch VB again, I'm sure I'll still be Thinking In Python if I go back
to do some VB.</rant>



More information about the Python-list mailing list