On Nov 5, 2019, at 21:48, Random832 <random...@fastmail.com> wrote:
> 
> As a side note, I have, occasionally, wanted to be able to resume a function 
> after handling an exception (the use case was to turn a synchronous outer 
> function calling an asynchronous callback into an asynchronous function), 
> which - needless to say - is impossible in CPython. In a hypothetical 
> implementation that would allow such a thing, having the raise return a value 
> in such a scenario might not be unreasonable.

That’s not just a CPython limitation, it’s a limitation of the defined Python 
semantics. Python unwinds the stack before calling exception handlers. For 
function exits that’s not a big deal (you’re not allowed to rely on garbage 
being cleaned up deterministically, and you can’t detect that something is 
garbage until it’s cleaned up), but it would change the semantics—and break 
most non-trivial uses—of finally clauses and with statements. You could maybe 
redesign the context manager protocol so they understand and participate in 
resume logic somehow (separate __initial_exit__ and __final_exit__?). But what 
do you do with finally clauses?

Another option is to do what Dylan does. I don’t actually remember the details, 
but it’s something like this: the way you resume an exception is to 
resume-raise a new exception back at the point it was raised. Then, the 
interpreter can stack up exception handlers and unwind chunks in a way that 
makes sense (it needs to rearrange that stack on the fly, but only in a simple 
way that Python already requires even for simple generators). Code that doesn’t 
ever resume works the same as always. Code that does has to be written 
differently, and has to be explicit about the order of unwinds and handlers, 
but can do so just by using the normal Python indentation (the try statement 
that handles the resume-raise is either inside or outside the with or 
try/finally or function). This also makes it very easy to turn a resume into a 
retry (by just putting a while outside  the inner try). And anything retryable 
also has the advantage that pdb can optionally keep the whole stack around 
until you decide not to retry, which can be helpful for grubbing around in the 
debugger, but then clean it all up properly as soon as you do decide.

IIRC, Common Lisp does something pretty similar to Dylan, except that instead 
of resumes being exceptions they’re a separate thing that just works nearly 
identical to exceptions. That might be more readable, or maybe even easier to 
implement, even if it adds more concepts to the language. I believe there’s 
also a rejected C++ proposal for resumable exceptions, and a g++ fork that 
implements it, from way back before C++11. Since C++ is heavily designed around 
RAII (everything is a context manager), they must have come up with a solution. 
On the other hand, there’s probably a reason it was rejected—although that 
reason might just be “We had an extensive discussion on signal-like vs. 
termination semantics when first doing ANSI C++, and this is close enough to 
signal-like that without any new use case we’re not going to reopen that 
discussion.” I don’t know of any other post-70s languages that do resumable 
exception handling that aren’t continuation-based, but they probably do exist.

But for a new Python-like language with resumable exceptions, I think you’d 
want to do it with continuations, because that’s the easy way. Adding a callcc 
and first-class continuation objects would probably be easier if you first 
removed try and with entirely, then translate everything to cc semantics, then 
redesign them on top of cc. Everything should be pretty simple. And you can 
even experiment with writing variations of the logic in-language as functions 
before writing the Python syntactic sugar. 

At that point, you could even remove the first-class continuations if you want, 
and translate the language back to non-continuation-based semantics. It might 
be easier to optimize things that way, and it would probably make it a lot 
easier to write Python implementations similar to Jython and Iron that rely as 
much as possible on the semantics of an underlying high-level language.

Or maybe don’t bother with any new syntax. Once you’ve got existing Python 
semantics plus callcc, just leave syntactic exceptions as-is with no resume, 
and make resumable (and retryable) exceptions a library thing that you can use 
in the cases where they‘re needed; those cases are probably rare enough that it 
doesn’t matter if they’re not pretty.

Also, I think you’d find that almost everywhere you think you want a resumable 
exception, you can actually do it more simply by either using continuations 
directly, or using a different abstraction on top of them. That certainly seems 
true in your use case, where your goal (making a sync function async) has 
nothing to do with exceptions, and the only reason you thought of resumable 
exceptions was, effectively, as a way to clumsily simulate callcc.
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/LXEWM4EBMJYNSTXIIBRSHJQL2NK57X3D/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to