Christian Dowski posts some uses for python’s enhanced generators. I tried to type a comment on that post, but I couldn’t figure out how to submit it successfully. Either comments are not allowed or I failed the CAPTCHA.
Anyhow, ever since I read about how common lisp handles exceptions, I’ve been daydreaming about how to do the same trick in python. In lisp, an exception jumps to some other place to get handled, just like in python. However, what is different is that the exception handler can repair the problem and then hand control back into the original block. For example, in the lisp toplevel, if you forget to define a variable before you try to copy its value to somebody else, the exception will propagate to the debugger. And in the debugger, you can then assign a value to that variable, and then resume your original program.
So that’s the background for my idea for generators. The generator that is trying the exception-raising code could yield the traceback to another generator when it hits an uncaught exception. Then the other generator, the exception-handling generator, could repair/log/do whatever, and then yield a value back to the original code.
For example, if the original code is iterating through a list of two-tuples, and for each two-tuple, it divides the first element by the second element, when it raises a ZeroDivisionError, it could catch that and yield it over to the exception handler. Then the exception handler could do whatever, like maybe prompt the programmer to choose a new denominator. After the programmer chose a new denominator, the exception handler could yield that back to the original generator and then the original generator could resume.
Lua calls generators that can receive values “coroutines” or “non-preemtible threads”. I think those are better labels because they hint that generators are way more than just iterators in drag.
Without the ability to yield across call stacks, generators as co-routines are useless. I'd suggest using co-routines as co-routines, in this case, which are implemented by stackless, pypy and the greenlet module.
Florian — I don't think I follow you. What do you mean by yielding across call stacks? How is that different than regular yield and send?
I mean to say generators with yield/send are not full co-routines. Full co-routines can switch to another thread of execution from anywhere within the call stack and return there later. If you want to do the same with generators, you need to start treating every “call” as an iteration.
Hence generators are very pointless to use as co-routines, and greenlets which are actually co-routines are much better at that capacity.
Florian, thanks for the extra detail. I think I need to study co-routines in stackless, pypy, and greenlets before I will really understand your point.
I've thought about this but came to the conclusion that this begs a distinction between error handling and error repair and repair we can simply handle with proper callbacks.
Hi Calvin — thanks for the comment! After the callback fires, how can I go back into the code that originally raised the exception? As far as I understand, that frame is inacessible.
Hi Calvin — thanks for the comment! After the callback fires, how can I go back into the code that originally raised the exception? As far as I understand, that frame is inacessible.