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

Lie Lie.1296 at gmail.com
Tue Mar 11 15:30:59 EDT 2008


(If there is anything weird that I say, please ignore it since I'm
writing this half-sleeping)

On Mar 12, 12:00 am, "Diez B. Roggisch" <de... at nospam.web.de> wrote:
(snip)

> I totally fail to see where
>
> raise Equal(a, b)
>
> is less cluttered or not than
>
> callback(a, b)
>
> Actually, the latter is even less cluttered, misses a raise - if pure number
> of literals is your metric, that is.

You don't just compare by the calling code, you've got to compare also
by the surrounding codes. The calling codes in SE might be a little
bit messy, but it worths by making the code surrounding it more clean
and structured. And anyway, if you used Context Object callback, they
will be as messy as each other.

> > If there is a syntax support, you could also make "resume" able to
> > transfer values:
>
> >     def somefunc(a, b):
> >         if a == b: a, b = raise Equal(a, b)
>
> >     def toplevel():
> >         try:
> >             somefunc(10, 20)
> >         except Equal, args:
> >             a, b = args[0], args[1] + 1
> >             resume a, b
>
> Sure, you can make all kinds of things, but so far you didn't come up with a
> comprehensive feature description that just _does_ specify what SEs are and
> what not.

- Exception that aren't handled when no handler exists for it.
- It's not a way for notifying errors
- It's a way to describe status changes to higher codes
- Everything described in the first post

> > Perhaps you meant:
> >     raise_soft(SoftException)
>
> > cause SoftException() may have side effects if called greedily
>
> Nope, I didn't, and it's beside the point.

Then what happen when SoftException is called? And a side-effect
occurs?

> > That could be done, but when raise_soft() returns, it returns to the
> > code that raises it so it must be 'break'en:
>
> >     def caller(a, b):
> >         if a == b:
> >             raise_soft(SoftException)
> >             break
>
> > but this makes SoftException must break everytime, which make it lost
> > its original purpose, so you have to add return code to raise_soft
> > whether you want to break it or not:
>
> You didn't understand my example. If there is a handler registered, it will
> be invoked. If not, nothing will be raised. The exact same amount of
> state-keeping and lookups needs to be done by the SE-implementation.

I do understand your example. And even if I misunderstood you about
passing context object, the same problem still exists in context
object based solution, i.e. functions can't make the called function
break automatically, it must be 'break' manually or the program will
go astray (break ==> return, sorry I confused it with break for
loops). And if you used InterruptException to break, it doesn't play
well with multiple SoftExceptions.

The final, resulting code by function passing below is extremely
messy, see if you can make it cleaner and with the same
functionalities and all to the SE version.

    def called(a, b, cont_obj = Null_CO):
        if a == b:
            a, b = cont_obj.a_equal_b(a, b)
        cont_obj.addition(a, b)
        return a + b

    def caller():
        class cont_obj(object):
            def a_equal_b(a, b):
                if a < 0 and b < 0:
                    return a + 1, b                  # resume
                raise InterruptException(('a_e_b',)) # break

            def addition(a, b):
                if a > b:
                    return
                raise InterruptException(('addit', (a, b)))   # break

        try:
            called(10, 10, cont_obj)
        except InterruptException, args:   # if breaken
            ret, arg = args
            if ret == 'a_e_b': return -1
            a, b = arg
            if ret == 'addit': return a ** b


# by adding try clauses, and you've really equalize the biggest
overhead of SE.
# And I don't think you could create a less messy InterruptException
handler,
# the other solution to it would be to create a handler for each
unique returns
# but that would make it exceed the second SE overhead, the Exception
Declaration
# In other words, the tricks that is used to emulate the SoftException
would all
# have higher code overhead compared to using the clean, structured
SEs

# * Overheads means garbage code that's important to make something
work

# The code is separated into three parts, "try except", and cont_obj,
and called. Worse, the cont_obj can't determine what happen if they
got unresumed errors, without using some tricky part.

Compare that code above with:

    def called(a, b):
        if a == b:
            a, b = raise a_equal_b(a, b)
        raise addition(a, b)
        return a + b

    def caller():
        class a_equal_b(Exception): pass
        class addition(Exception): pass

        try:
            ret = called(10, 10)
        except a_equal_b(a, b):
            if a < 0 and b < 0:
                resume a + 1, b
            return -1
        except addition(a, b):
            if a > b: resume
            return a ** b

# The code is separated into two parts, the "trys and excepts" and the
called code.

> > That could be done, but when raise_soft() returns, it returns to the
> > code that raises it so it must be 'break'en:
> >     def caller(a, b):
> >         if a == b:
> >             if raise_soft(SoftException):
> >                 break
>
> > Compare to:
> >     def caller(a, b):
> >         if a == b:
> >             raise SoftException
>
> > And this also makes it impossible to have state-changing behavior
> > without some other weirder tricks
>
> That's not true. The
>
> with add_soft_handler(SoftException, handler):
>
> approach (I missed the handrel the first time, sorry)
> can easily throw an exception to interrupt, like this:
>
> def handler(e):
>     if some_condition_on_e(e):
>        raise InterruptException()
>
> with add_soft_handler(SoftException, handler):
>      try:
>           work(...)
>      except InterruptException:
>           pass
>
> You could also introduce a function
>
> def interruptable(fun, *args, **kwargs):
>     try:
>        return fun(*args, **kwargs)
>     except InterruptException:
>        passthe
>
> to make the code look a bit cleaner - if it fits your usecase, that is of
> course.

The code doesn't work well with multiple excepts that have multiple
fallbacks.

> I don't say that SoftExceptions can't have semantics that go beyond this. I
> just don't see a oh-so-compelling use-case that makes things so much better
> than they are reachable now, without actually much programming.




More information about the Python-list mailing list