(If there is anything weird that I say, please ignore it since I'm
writing this half-sleeping)
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
Nope, I didn't, and it's beside the point.
Then what happen when SoftException is called? And a side-effect
occurs?
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'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.