On 18 March 2025 05:59:07 GMT, Larry Garfield <la...@garfieldtech.com> wrote:

>My biggest issue, though, is that I honestly can't tell what the mental model 
>is supposed to be.  The RFC goes into detail about three different async 
>models.  Are those standard terms you're borrowing from elsewhere, or your own 
>creation?  If the former, please include citations.  I cannot really tell 
>which one the "playpen" model would fit into.  I... think bottom up, but I'm 
>not sure.  Moreover, I then cannot tell which of those models is in use in the 
>RFC.  There's a passing reference to it being bottom up, I think, but it 
>certainly looks like the No Limit model.  There's a section called structured 
>concurrency, but what it describes doesn't look a thing like the 
>playpen-definition of structured concurrency, which as noted is my preference. 
> It's not clear why the various positives and negatives are there; it's just 
>presented as though self-evident.  Why does bottom up lead to high memory 
>usage, for instance?  That's not clear to me.  So really... I have no idea how 
>to think about any of it.


I had a very different reaction to that section. I do agree that some citations 
and links to prior art would be good - I mentioned in my first email that the 
"actor model" is mentioned in passing without ever being defined - but in 
general, I  thought this summary was very succinct:

> -  No limitation. Coroutines are not limited in their lifetime and run as 
> long as needed.
> - Top-down limitation: Parent coroutines limit the lifetime of their children
> - Bottom-up limitation: Child coroutines extend the execution time of their 
> parents

Since you've described playpens as having an implicit "await all", they're 
bottom-up: the parent lasts as long as its longest child. Top-down would be the 
same thing, but with an implicit "cancel all" instead.


>Broadly speaking, I can think of three usage patterns for async in PHP 
>(speaking, again, as someone who doesn't have a lot of async experience, so I 
>may be missing some):
>
>1. Fan-out.  This is the "fetch all these URLs at once" type use case, which 
>in most cases could be wrapped up into a para_map() function.  (Which is 
>exactly what Rust does.)
>2. Request handlers, for persistent-process servers.  Would also apply for a 
>queue worker.
>3. Throw it over the wall.  This would be the logging example, or sending an 
>email on some trigger, etc.  Importantly, these are cases where there is no 
>result needed from the sub-routine.


I agree that using these as key examples would be good.



>// Creates an async scope, in which you can create coroutines.
>async {


The problem with using examples like this is that it's not clear what happens 
further down the stack - are you not allowed to spawn/start/whatever anything? 
Does it get started in the "inherited" scope?

You've also done exactly what you complained the RFC did and provided a 
completely artificial example - which of the key use cases you identified is 
this version of scope trying to solve?

I actually think what you're describing is very similar to the RFC, just with 
different syntax; but your examples are different, so you're talking past each 
other a bit.



>I honestly cannot see a use case at this point for starting coroutines in 
>arbitrary scopes.


The way I picture it is mostly about choosing between creating a child within a 
narrow scope you've just opened, vs creating a sibling in the scope created 
somewhere up the stack.

The "request handler" use case could easily benefit from a "pseudo-global" 
scope for each request - i e. "tie this to the current request, but not to 
anything else that's started a scope in between".

There were also some concrete examples given in the previous thread of 
explicitly managing a context/scope/playpen in a library.


Rowan Tommins
[IMSoP]

Reply via email to