Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:126823 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by qa.php.net (Postfix) with ESMTPS id 2E7301A00BC for ; Tue, 18 Mar 2025 13:46:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1742305422; bh=JW/twCfR8m7tB8VlxhCNpmMK6NArg1jJAMZuI/qFSQA=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=OH9HowxC/Y2lFcuohi/r6HJSfTPTyhUI3NrPIbuKbq+LPRng5miH1J3VzgkS+5ME1 w8gZ6lH2SuZYbYFR3PsNfTDekjDtZMgpg6HBO3sqg/FH4KyHZSst8HY4MLXt5JDwqT o5wyeLd/3ov76NYGmmXrQKMZOWBRNJ5YFRx6yTEPPkEHlM0bQj+sjv3JRVr7HvIkRl dcUskYnSzOfO4gRrVPjF/7IQCpPKzRGOqSDNxnWJDwcxHO/bMBJvQso0llypbX6q8Q 8z83k1zVhjnE+VWQcGtbAA6XKU3I6rdDeaK/oG5f+FPa8Z0BIac5zfmmuEJOTFAo1s zfgUsLHCTfMeg== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id DE304180079 for ; Tue, 18 Mar 2025 13:43:40 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-1.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS, FREEMAIL_ENVFROM_END_DIGIT,FREEMAIL_FROM,HTML_MESSAGE, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE, SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-pj1-f43.google.com (mail-pj1-f43.google.com [209.85.216.43]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Tue, 18 Mar 2025 13:43:40 +0000 (UTC) Received: by mail-pj1-f43.google.com with SMTP id 98e67ed59e1d1-2ff4a4f901fso5332108a91.2 for ; Tue, 18 Mar 2025 06:46:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1742305571; x=1742910371; darn=lists.php.net; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=lw6ARGYZ52J9Tx1b1y7fDSzd339oOKN8nPPz7hykazE=; b=DFC/RZnoGH6udp9KhBPLpuX3DeSIBxhJ0rBlNV2FAhSfbk43AzxgZX8Br20Omdyvcb cBQcZCtg7UAidue0OCY1jAT8LWXxHg2RYKRwQTegK2swCNwUwwMJ3B4f/sJBDjJdcvKe ZxQjcGEsjFlGnVRnH/XXki/kYKfkS/ab00TK5qKujFORZyDKP5k0pNJNn6kwOhS0eGu4 EzC7RuglGZ93RvqnwBcYzLMQ9vcrYqvL0Ldv4lnk28wYjykSXDat5OSNMt0x7eIzhdOU Vhi4qRMQ7PzWVK+XM5npH0t1Nh8JSpdR62mr33qaZQU74hVBmDMvU6B0rDuoi+QIKHf3 qsbA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1742305571; x=1742910371; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=lw6ARGYZ52J9Tx1b1y7fDSzd339oOKN8nPPz7hykazE=; b=ufWOZskfvDl9K/W7Vz84TH48zl9oe1QrPUw4U9r2OUXhOhYBnp1WPFTP9iG360OgwH sIgfu49T7iOGhf/Y1qKDr/LW4JEe3dmihgks2lIjwf4mCW6E+tvrdrpTAhB7VfCl2ZDN 4NBDiAw/mKAKlFDnyxFdOhvV2efsktjhdLtlx6NqyXQjqE9/wQsU8syi3yS7np1V2oHW AKm7z7VAARHSfNTN4NdL7BFmrBFolENsb1tMY+ociq/Ul+w5QPBguP/w18V8yKb5hf4X FSFvX9bnTV1mY0VVFwGtO4B3lOFF+TROkIFIcljVLxi6B8nfxJy2Ct9Q2jt5xD75sBQ8 +Gvw== X-Gm-Message-State: AOJu0YwvSat7JtOcsFH5J90HnBYfiKnyOBKPf4jIqM4XJPcDSBsBeup/ RgZicrACpojeeXinYTjf/t/UsZoRXTTDNONYI6T3Ip7oihi7Lhr7kM5S9nzRdY2BRRlsBWLWhLB L4FY23DXmBIiAl/Klf1AcG5z8WO85eR4Z X-Gm-Gg: ASbGnct6n3C+pw7HahLqYMrmgABUGps78PiG4kK+PgDur6oHW2y2W/kkqxfgQQdp8d2 HpbUdtCaa/d18p2kHDDhNVZ7v0jvmTU8EAwgyjVAYizaW4GGn0Bx3D0ddT9UT9Ty/GqpyhtyIM8 id1H/u2ytFj8ho26xSMv81wpsZWm5fE0a04P2TKFNkongdnjzg566P6GljPVk= X-Google-Smtp-Source: AGHT+IGBIulzb2dl4ZtZTE7iCy6I12Foot8K9gnuRlLEZ6Z1v1y1pkKqCFzf0GyeizCHmrAwUm6PRB9n2tph1xuxxi8= X-Received: by 2002:a17:90a:e7c2:b0:2ff:784b:ffe with SMTP id 98e67ed59e1d1-301a5b1304cmr3546603a91.11.1742305570910; Tue, 18 Mar 2025 06:46:10 -0700 (PDT) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 References: In-Reply-To: Date: Tue, 18 Mar 2025 10:45:59 -0300 X-Gm-Features: AQ5f1JpSu3I_HHAt26lduqBjkFlDg6mzpXzMI1SlndXd8-3PzYL8Dcupa9EeAKM Message-ID: Subject: Re: [PHP-DEV] PHP True Async RFC - Stage 2 To: Larry Garfield Cc: php internals Content-Type: multipart/alternative; boundary="000000000000a4fb0f06309e20c1" From: talyssonlima5@gmail.com (Talysson Lima) --000000000000a4fb0f06309e20c1 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable If I say it's bright, you call it dark. If I choose the east, you push for the south. You=E2=80=99re not seeking a path, just a fight... Debating with you? Not worth the time! Em ter., 18 de mar. de 2025 =C3=A0s 03:00, Larry Garfield escreveu: > On Sun, Mar 16, 2025, at 4:24 AM, Edmond Dantes wrote: > > Good day, everyone. I hope you're doing well. > > > > https://wiki.php.net/rfc/true_async > > > > Here is a new version of the RFC dedicated to asynchrony. > > > > Key differences from the previous version: > > > > * The RFC is not based on Fiber; it introduces a separate class > > representation for the asynchronous context. > > I'm unclear here. It doesn't expose Fibers at all, or it's not even > touching the C code for fibers internally? Like, would this render the > existing Fiber code entirely vestigial, not just its API? > > > * All low-level elements, including the Scheduler and Reactor, have > > been removed from the RFC. > > * The RFC does not include Future, Channel, or any other primitives, > > except those directly related to the implementation of structured > > concurrency. > > > > The new RFC proposes more significant changes than the previous one; > > however, all of them are feasible for implementation. > > > > I have also added PHP code examples to illustrate how it could look > > within the API of this RFC. > > > > I would like to make a few comments right away. In the end, the Kotlin > > model lost, and the RFC includes an analysis of why this happened. The > > model that won is based on the Actor approach, although, in reality, > > there are no Actors, nor is there an assumption of implementing > > encapsulated processes. > > > > On an emotional level, the chosen model prevailed because it forces > > developers to constantly think about how long coroutines will run and > > what they should be synchronized with. This somewhat reminded me of > > Rust=E2=80=99s approach to lifetime management. > > Considering that lifetime management is one of the hardest things in Rust > to learn, that's not a ringing endorsement. > > > Another advantage I liked is that there is no need for complex syntax > > like in Kotlin, nor do we have to create separate entities like > > Supervisors and so on. Everything is achieved through a simple API that > > is quite intuitive. > > I'll be honest... intuitive is not the term I'd use. In fact, I didn't > make it all the way through the RFC before I got extremely confused about > how it all worked. > > First off, it desperately needs an "executive summary" section up at the > top. There's a *lot* going on, and having a big-picture overview would > help a ton. (For examples, see property hooks[1] and pattern > matching[2].) > > Second, please include realistic examples. Nearly all of the examples ar= e > contrived, which doesn't help me see how I would actually use async > routines or what the common patterns would be, and I therefore cannot > evaluate how well the proposal treats those common cases. The first > non-foobar example includes a comment "of course you should never do it > like this", which makes the example rather useless. And the second is > built around a code model that I would never, ever accept into a code bas= e, > so it's again unhelpful. Most of the RFC also uses examples that... have > no return values. So from reading the first half of it, I honestly > couldn't tell you how return values work, or if they're wrapped in a Futu= re > or something. > > Third, regarding syntax, I largely agree with Tim that keywords are bette= r > than functions. This is very low-level functionality, so we can and shou= ld > build dedicated syntax to make it as robust and self-evident (and IDE > friendly) as possible. > > That said, even allowing for the async or await or spawn keywords, I got > super confused when the Scope object was introduced. So would the > functions/keywords be shortcuts for some of the common functionality of a > Scope object? If not, what's the actual difference? I got lost at that > point. > > The first few sections of the RFC seem to read as "this RFC doesn't > actually work at all, until some future RFC handles this other part." > Which... no, that's not how this works. :-) > > As someone that has not built an async framework before (which is 99.9% o= f > PHP developers, including those on this list), I do not see the point of > half the functionality here. Especially the BoundedScope. I see no reas= on > for it to be separate from just any other Scope. What is the difference > between scope and context? I have no clue. > > 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 concurrenc= y, > which as noted is my preference. It's not clear why the various positive= s > 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. > > Sorry, I'm just totally lost at this point. > > As an aside: I used "spawn" as a throw-away keyword to avoid using "await= " > in a previous example. It's probably not the right word to use in most o= f > these cases. > > I know some have expressed the sentiment that tightly structured > concurrency is just us not trusting developers and babysitting them. To > which I say... YES! The overwhelming majority of PHP developers have no > experience writing async code. Their odds of getting it wrong and doing > something inadvertently stupid by accident through not understanding some > nuance are high. And I include myself in that. MY chances of > inadvertently doing something stupid by accident are high. I *want* a > design that doesn't let me shoot myself in the foot, or at least makes it > difficult to do. If that means I cannot do everything I want to... GOOD! > Humans are not to be trusted with manually coordinating parallelism. We'= re > just not very good at it, as a species. > > 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. (Whi= ch > is exactly what Rust does.) > 2. Request handlers, for persistent-process servers. Would also apply fo= r > 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 feel like those three seem to capture most reasonable use cases, give o= r > take some details. (And, of course, many apps will include all three in > various places.) So any proposal should include copious examples of how > those three cases would look, and why they're sufficiently ergonomic. > > A playpen model can handle both 1 and 2. In fan out, you want the "Wait > all" logic, but then you also need to think about a Future object or > similar. In a request handler, you're spawning an arbitrary number of > coroutines that will terminate, and you probably don't care if they have = a > return value. > > It's the "throw over the wall" cases where a playpen takes more work. As > I showed previously, it can be done. It just takes a bit more setup. Bu= t > if that is too much for folks, I offer a compromise position. Again, jus= t > spitballing the syntax specifics: > > // Creates an async scope, in which you can create coroutines. > async { > > // Creates a new coroutine that MAY last beyond the scope of this block= . > // However, it MUST be a void-return function, indicating that it's > going to > // do work that is not relevant to the rest of this block. > spawn func_call(1, 2, 3); > > // Creates a new coroutine that will block at the end of this async > block. > // The return value is a future for whatever other_function() will > return. > // $future may be used as though it were the type returned, but trying > // to read it will block until the function completes. It may also hav= e > other > // methods on it, not sure. > $future =3D start other_function(4, 5, 6); > > // Queues a coroutine to get called after all "start"ed coroutines have > completed > // and this block is about to end. Its return value is discarded. > Perhaps it should be > // restricted to void-return, not sure. In this case it doesn't hurt > anything. > defer cleanup(7, 8, 9); > > // Do nothing except allow other coroutines to switch in here if they > want. > suspend; > > // Enqueues this coroutine to run in 100 ms, or slightly thereafter > whenever the scheduler gets to it. > timeout 100ms something(4, 5, 6); > > } // There is an implicit wait-all here for anything start-ed, but not fo= r > spawn-ed. > > I honestly cannot see a use case at this point for starting coroutines in > arbitrary scopes. Only "current scope" and "global scope, let it escape.= " > That maps to "start" and "spawn" above. If internally "spawn" gets > translated to "start in the implicit async block that is the entire > application", so that those coroutines will still block the whole script > from terminating, that is not a detail most devs will care about. (Which > also means in the global async scope, spawn and start are basically > synonymous.) > > I can see the need for cancellation, which means probably we do need a > scope object to represent the current async block. However, that's just = a > cancel() method, which would propagate to any child. Scheduling it can b= e > handled by the timeout command. At this point, I do not see the use case > for anything more advanced than the above (except for channels, which as = I > argued before could make spawn unnecessary). There may be a good reason > for it, but I don't know what it is and the RFC does not make a compellin= g > argument for why anything more is needed. > > I could see an argument that async $scope { ... } lets you call all of th= e > above keywords as methods on $scope, and the keywords are essentially a > shorthand for "this method on the current scope". But you could also pas= s > the scope object around to places if you want to do dangerous things. I'= m > not sure if I like that, honestly, but it seems like an option. > > Elsewhere in the thread, Tim noted that we should unify the function call > vs closure question. I used straight function calls above for simplicity= , > but standardizing on a closure also makes sense. Related, I've been > talking with Arnaud about trying to put Partial Function Application > forward again[3], assuming pipes[4] pass. If we follow the previous mode= l, > then it would implicitly provide a way to turn any function call into a > delayed function call: > > function foo(int $a, int $b) { ... } > > foo(4, 5); // Calls foo() right now > foo(4, 5, ...); // Creates a 0-argument closure that will call foo(4, 5) > when invoked. > > Basically the latter is equivalent to: > fn() =3D> foo(4, 5); > > A 0-argument closure (because all arguments are already captured) goes by > the delightful name "thunk" (as in the past tense of think, if you don't > know English very well.) That likely wouldn't be ideal, but it would mak= e > standardizing start/spawn on "thou shalt provide a closure" fairly > straightforward, as any function could trivially be wrapped into one. > > That's not necessarily the best way, but I mention it to show that there > are options available if we allow related features to support each other > synergistically, which I always encourage. > > Like Tim, I applaud you're commitment to this topic and willingness to > work with feedback. But the RFC text is still a long way from a model th= at > I can wrap my head around, much less support. > > [1] https://wiki.php.net/rfc/property-hooks > [2] https://wiki.php.net/rfc/pattern-matching > [3] https://wiki.php.net/rfc/partial_function_application > [4] https://wiki.php.net/rfc/pipe-operator-v3 > > --Larry Garfield > --000000000000a4fb0f06309e20c1 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
If I say it's bright, you call it dark.
If I choose= the east, you push for the south.
You=E2=80=99re not seeking a path, ju= st a fight...
Debating with you? Not worth the time!

Em ter., 18 de mar. de 2025 =C3=A0s 03:00, Larry Garfield <larry@garfieldtech.com> escreveu:
=
On Sun, Mar 16, 202= 5, at 4:24 AM, Edmond Dantes wrote:
> Good day, everyone. I hope you're doing well.
>
> https://wiki.php.net/rfc/true_async
>
> Here is a new version of the RFC dedicated to asynchrony.
>
> Key differences from the previous version:
>
> * The RFC is not based on Fiber; it introduces a separate class
> representation for the asynchronous context.

I'm unclear here.=C2=A0 It doesn't expose Fibers at all, or it'= s not even touching the C code for fibers internally?=C2=A0 Like, would thi= s render the existing Fiber code entirely vestigial, not just its API?

> * All low-level elements, including the Scheduler and Reactor, have > been removed from the RFC.
> * The RFC does not include Future, Channel, or any other primitives, <= br> > except those directly related to the implementation of structured
> concurrency.
>
> The new RFC proposes more significant changes than the previous one; <= br> > however, all of them are feasible for implementation.
>
> I have also added PHP code examples to illustrate how it could look > within the API of this RFC.
>
> I would like to make a few comments right away. In the end, the Kotlin=
> model lost, and the RFC includes an analysis of why this happened. The=
> model that won is based on the Actor approach, although, in reality, <= br> > there are no Actors, nor is there an assumption of implementing
> encapsulated processes.
>
> On an emotional level, the chosen model prevailed because it forces > developers to constantly think about how long coroutines will run and =
> what they should be synchronized with. This somewhat reminded me of > Rust=E2=80=99s approach to lifetime management.

Considering that lifetime management is one of the hardest things in Rust t= o learn, that's not a ringing endorsement.

> Another advantage I liked is that there is no need for complex syntax =
> like in Kotlin, nor do we have to create separate entities like
> Supervisors and so on. Everything is achieved through a simple API tha= t
> is quite intuitive.

I'll be honest... intuitive is not the term I'd use.=C2=A0 In fact,= I didn't make it all the way through the RFC before I got extremely co= nfused about how it all worked.

First off, it desperately needs an "executive summary" section up= at the top.=C2=A0 There's a *lot* going on, and having a big-picture o= verview would help a ton.=C2=A0 (For examples, see property hooks[1] and pa= ttern matching[2].)=C2=A0

Second, please include realistic examples.=C2=A0 Nearly all of the examples= are contrived, which doesn't help me see how I would actually use asyn= c routines or what the common patterns would be, and I therefore cannot eva= luate how well the proposal treats those common cases.=C2=A0 The first non-= foobar example includes a comment "of course you should never do it li= ke this", which makes the example rather useless.=C2=A0 And the second= is built around a code model that I would never, ever accept into a code b= ase, so it's again unhelpful.=C2=A0 Most of the RFC also uses examples = that... have no return values.=C2=A0 So from reading the first half of it, = I honestly couldn't tell you how return values work, or if they're = wrapped in a Future or something.

Third, regarding syntax, I largely agree with Tim that keywords are better = than functions.=C2=A0 This is very low-level functionality, so we can and s= hould build dedicated syntax to make it as robust and self-evident (and IDE= friendly) as possible.

That said, even allowing for the async or await or spawn keywords, I got su= per confused when the Scope object was introduced.=C2=A0 So would the funct= ions/keywords be shortcuts for some of the common functionality of a Scope = object?=C2=A0 If not, what's the actual difference?=C2=A0 I got lost at= that point.

The first few sections of the RFC seem to read as "this RFC doesn'= t actually work at all, until some future RFC handles this other part."= ;=C2=A0 Which... no, that's not how this works. :-)

As someone that has not built an async framework before (which is 99.9% of = PHP developers, including those on this list), I do not see the point of ha= lf the functionality here.=C2=A0 Especially the BoundedScope.=C2=A0 I see n= o reason for it to be separate from just any other Scope.=C2=A0 What is the= difference between scope and context?=C2=A0 I have no clue.=C2=A0

My biggest issue, though, is that I honestly can't tell what the mental= model is supposed to be.=C2=A0 The RFC goes into detail about three differ= ent async models.=C2=A0 Are those standard terms you're borrowing from = elsewhere, or your own creation?=C2=A0 If the former, please include citati= ons.=C2=A0 I cannot really tell which one the "playpen" model wou= ld fit into.=C2=A0 I... think bottom up, but I'm not sure.=C2=A0 Moreov= er, I then cannot tell which of those models is in use in the RFC.=C2=A0 Th= ere's a passing reference to it being bottom up, I think, but it certai= nly looks like the No Limit model.=C2=A0 There's a section called struc= tured concurrency, but what it describes doesn't look a thing like the = playpen-definition of structured concurrency, which as noted is my preferen= ce.=C2=A0 It's not clear why the various positives and negatives are th= ere; it's just presented as though self-evident.=C2=A0 Why does bottom = up lead to high memory usage, for instance?=C2=A0 That's not clear to m= e.=C2=A0 So really... I have no idea how to think about any of it.

Sorry, I'm just totally lost at this point.

As an aside: I used "spawn" as a throw-away keyword to avoid usin= g "await" in a previous example.=C2=A0 It's probably not the = right word to use in most of these cases.

I know some have expressed the sentiment that tightly structured concurrenc= y is just us not trusting developers and babysitting them.=C2=A0 To which I= say... YES!=C2=A0 The overwhelming majority of PHP developers have no expe= rience writing async code.=C2=A0 Their odds of getting it wrong and doing s= omething inadvertently stupid by accident through not understanding some nu= ance are high.=C2=A0 And I include myself in that.=C2=A0 MY chances of inad= vertently doing something stupid by accident are high.=C2=A0 I *want* a des= ign that doesn't let me shoot myself in the foot, or at least makes it = difficult to do.=C2=A0 If that means I cannot do everything I want to... GO= OD!=C2=A0 Humans are not to be trusted with manually coordinating paralleli= sm.=C2=A0 We're just not very good at it, as a species.

Broadly speaking, I can think of three usage patterns for async in PHP (spe= aking, again, as someone who doesn't have a lot of async experience, so= I may be missing some):

1. Fan-out.=C2=A0 This is the "fetch all these URLs at once" type= use case, which in most cases could be wrapped up into a para_map() functi= on.=C2=A0 (Which is exactly what Rust does.)
2. Request handlers, for persistent-process servers.=C2=A0 Would also apply= for a queue worker.
3. Throw it over the wall.=C2=A0 This would be the logging example, or send= ing an email on some trigger, etc.=C2=A0 Importantly, these are cases where= there is no result needed from the sub-routine.

I feel like those three seem to capture most reasonable use cases, give or = take some details.=C2=A0 (And, of course, many apps will include all three = in various places.)=C2=A0 So any proposal should include copious examples o= f how those three cases would look, and why they're sufficiently ergono= mic.

A playpen model can handle both 1 and 2.=C2=A0 In fan out, you want the &qu= ot;Wait all" logic, but then you also need to think about a Future obj= ect or similar.=C2=A0 In a request handler, you're spawning an arbitrar= y number of coroutines that will terminate, and you probably don't care= if they have a return value.

It's the "throw over the wall" cases where a playpen takes mo= re work.=C2=A0 As I showed previously, it can be done.=C2=A0 It just takes = a bit more setup.=C2=A0 But if that is too much for folks, I offer a compro= mise position.=C2=A0 Again, just spitballing the syntax specifics:

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

=C2=A0 // Creates a new coroutine that MAY last beyond the scope of this bl= ock.
=C2=A0 // However, it MUST be a void-return function, indicating that it= 9;s going to
=C2=A0 // do work that is not relevant to the rest of this block.
=C2=A0 spawn func_call(1, 2, 3);

=C2=A0 // Creates a new coroutine that will block at the end of this async = block.
=C2=A0 // The return value is a future for whatever other_function() will r= eturn.
=C2=A0 // $future may be used as though it were the type returned, but tryi= ng
=C2=A0 // to read it will block until the function completes.=C2=A0 It may = also have other
=C2=A0 // methods on it, not sure.
=C2=A0 $future =3D start other_function(4, 5, 6);

=C2=A0 // Queues a coroutine to get called after all "start"ed co= routines have completed
=C2=A0 // and this block is about to end.=C2=A0 Its return value is discard= ed. Perhaps it should be
=C2=A0 // restricted to void-return, not sure.=C2=A0 In this case it doesn&= #39;t hurt anything.
=C2=A0 defer cleanup(7, 8, 9);

=C2=A0 // Do nothing except allow other coroutines to switch in here if the= y want.
=C2=A0 suspend;

=C2=A0 // Enqueues this coroutine to run in 100 ms, or slightly thereafter = whenever the scheduler gets to it.
=C2=A0 timeout 100ms something(4, 5, 6);

} // There is an implicit wait-all here for anything start-ed, but not for = spawn-ed.

I honestly cannot see a use case at this point for starting coroutines in a= rbitrary scopes.=C2=A0 Only "current scope" and "global scop= e, let it escape."=C2=A0 That maps to "start" and "spaw= n" above.=C2=A0 If internally "spawn" gets translated to &qu= ot;start in the implicit async block that is the entire application", = so that those coroutines will still block the whole script from terminating= , that is not a detail most devs will care about.=C2=A0 (Which also means i= n the global async scope, spawn and start are basically synonymous.)

I can see the need for cancellation, which means probably we do need a scop= e object to represent the current async block.=C2=A0 However, that's ju= st a cancel() method, which would propagate to any child.=C2=A0 Scheduling = it can be handled by the timeout command.=C2=A0 At this point, I do not see= the use case for anything more advanced than the above (except for channel= s, which as I argued before could make spawn unnecessary).=C2=A0 There may = be a good reason for it, but I don't know what it is and the RFC does n= ot make a compelling argument for why anything more is needed.

I could see an argument that async $scope { ... } lets you call all of the = above keywords as methods on $scope, and the keywords are essentially a sho= rthand for "this method on the current scope".=C2=A0 But you coul= d also pass the scope object around to places if you want to do dangerous t= hings.=C2=A0 I'm not sure if I like that, honestly, but it seems like a= n option.

Elsewhere in the thread, Tim noted that we should unify the function call v= s closure question.=C2=A0 I used straight function calls above for simplici= ty, but standardizing on a closure also makes sense.=C2=A0 Related, I'v= e been talking with Arnaud about trying to put Partial Function Application= forward again[3], assuming pipes[4] pass.=C2=A0 If we follow the previous = model, then it would implicitly provide a way to turn any function call int= o a delayed function call:

function foo(int $a, int $b) { ... }

foo(4, 5); // Calls foo() right now
foo(4, 5, ...); // Creates a 0-argument closure that will call foo(4, 5) wh= en invoked.

Basically the latter is equivalent to:
fn() =3D> foo(4, 5);

A 0-argument closure (because all arguments are already captured) goes by t= he delightful name "thunk" (as in the past tense of think, if you= don't know English very well.)=C2=A0 That likely wouldn't be ideal= , but it would make standardizing start/spawn on "thou shalt provide a= closure" fairly straightforward, as any function could trivially be w= rapped into one.

That's not necessarily the best way, but I mention it to show that ther= e are options available if we allow related features to support each other = synergistically, which I always encourage.

Like Tim, I applaud you're commitment to this topic and willingness to = work with feedback.=C2=A0 But the RFC text is still a long way from a model= that I can wrap my head around, much less support.

[1] https://wiki.php.net/rfc/property-hooks
[2] https://wiki.php.net/rfc/pattern-matching
[3] https://wiki.php.net/rfc/partial_function= _application
[4] https://wiki.php.net/rfc/pipe-operator-v3

--Larry Garfield
--000000000000a4fb0f06309e20c1--