Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:128903 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 lists.php.net (Postfix) with ESMTPS id DC2731A00BC for ; Wed, 22 Oct 2025 14:04:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1761141887; bh=LY4Jz2Uw6PkSCuLSzYWWxiUPoD6YzULwRcjuOvpyt8M=; h=Date:From:To:Cc:In-Reply-To:References:Subject:From; b=Tz/rG3RKPI6iKAj+6u+pttxNwjoBOGrbnNBdMuDMLD4mM3NqdSX/b+CtqFeT+T2a0 lDC723J4kbfT8WvPQ4st0i7U70OZamMvDWdqlzCG3GWxUWIDcwfz3cnwMAtQujrN1g vQyOelhy/2G44Xxpamge55Z1KyMrIGKb0ECofxvCgQL06qO8xgD15gQWGeWHHmlgll p1TqgT/BL4QeC+6NYRS+KHYB6TEEf2FMG9t1YWfeUe902OHxLifSgtTeMqYPrILLGE z4Gu0KqxtTryUkjAY+onuJoZu1YthC/IehksZThsWuaCc4Wyylfd9Zeb/1haLbQcqq xE44sGqNUTxaw== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 2EDE6180083 for ; Wed, 22 Oct 2025 14:04:46 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-0.1 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,HTML_MESSAGE, RCVD_IN_DNSWL_LOW,RCVD_IN_MSPIKE_H2,SPF_HELO_PASS,SPF_PASS autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: No X-Envelope-From: Received: from fout-a4-smtp.messagingengine.com (fout-a4-smtp.messagingengine.com [103.168.172.147]) (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 ; Wed, 22 Oct 2025 14:04:45 +0000 (UTC) Received: from phl-compute-05.internal (phl-compute-05.internal [10.202.2.45]) by mailfout.phl.internal (Postfix) with ESMTP id 40900EC0208; Wed, 22 Oct 2025 10:04:40 -0400 (EDT) Received: from phl-imap-05 ([10.202.2.95]) by phl-compute-05.internal (MEProxy); Wed, 22 Oct 2025 10:04:40 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bottled.codes; h=cc:cc:content-type:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=fm3; t=1761141880; x= 1761228280; bh=iz02kXIlc5UpHueqdi68SVdvTDXTb4nDu3d5v7JbhVs=; b=N MpzZqPWk+OWc11dTdTQ1bpGIaisIHnhz4XCEfhLBwTUk99lN9ouxWVEsL8TW+Ld1 6GuuIyfbbX+b60ETz5/UCWYc3pU7B4C0+EG4+AiipbIy/Dsibsv6OB3/5WeamC0k GXhzvn0AVrSlUa8nx8IPMR/KO8k/31egJzb/YavUWXJcz3TmsLtXYOHRz8RIUGmf 89FZwA4djcEZhH7TkwjvzYQt8Tg56iQk4Lmi47Gy7NOujDAYUz1YdiRNKhypgeV+ a7ClRmzTUk/UWNk5LdfT1Ic9WRJYSUEu3s0E55R0/K6W92gjjGRRmPQB8dzZDmXO DtvVsEZpzzH5KiE6aiVkQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm2; t= 1761141880; x=1761228280; bh=iz02kXIlc5UpHueqdi68SVdvTDXTb4nDu3d 5v7JbhVs=; b=FtVdmgcnKnMqo0Rb06ofEEEazVaNHAmGq36i9jUflNgzhwZL7yJ 9ikAydmZY3Zy+unNU4sDKGdaGNvAUwIA2EkOxMbtxgWmf+hT4E0KZUhiLYvSZYNm db8/qKdm+Dt4qQhqKO4b2QWaYcjdO44W3tAbzqMqnyGSM31qU1YZIGviaY0aaL4K NiflTQOqC50Rf1LAFjd9LREXg6vJvIh5CDwVXrph6sk+BjigVGoJNvPm8VQnPD4w xTIozQRKlf+LNhOSALXwBQnzjWbjLZ6GducqBpQhNycd+94rxnRdIEYX/qd25hq1 paXUbbNEQBmxxQq/xmIy0o86bHg+zuOoncA== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggddugeefjeejucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhepofggfffhvfevkfgjfhfutgesrgdtreerredtjeenucfhrhhomhepfdftohgsucfn rghnuggvrhhsfdcuoehrohgssegsohhtthhlvggurdgtohguvghsqeenucggtffrrghtth gvrhhnpeeiueethedvvdefjefhgfeiheelheehtdfhfeekjefflefgvedvkeduteejjedt tdenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehroh gssegsohhtthhlvggurdgtohguvghspdhnsggprhgtphhtthhopeefpdhmohguvgepshhm thhpohhuthdprhgtphhtthhopegvughmohhnugdrhhhtsehgmhgrihhlrdgtohhmpdhrtg hpthhtohepihhnthgvrhhnrghlsheslhhishhtshdrphhhphdrnhgvthdprhgtphhtthho pegrrghrohhnsehtrhhofihskhhirdgtohhm X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id 8C15B182007A; Wed, 22 Oct 2025 10:04:39 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface Precedence: list list-help: list-unsubscribe: list-post: List-Id: x-ms-reactions: disallow MIME-Version: 1.0 X-ThreadId: AG5VjklPAjNR Date: Wed, 22 Oct 2025 16:03:38 +0200 To: "Edmond Dantes" Cc: "Aaron Piotrowski" , "PHP Internals" Message-ID: <772a457f-69b6-4a76-8224-081917d719f6@app.fastmail.com> In-Reply-To: References: <0e4e39d6-9cc9-4970-92e0-2463143b4011@app.fastmail.com> <37180d8d-85b4-49a3-a672-334bf4329470@app.fastmail.com> <2f8524a7-dea2-4fbf-933a-c538d3706253@app.fastmail.com> <151800a7-1094-49bc-8e43-c593a74741af@app.fastmail.com> Subject: Re: [PHP-DEV] PHP True Async RFC Stage 4 Content-Type: multipart/alternative; boundary=16b1a45c7e3c425baf6bb3f36341034a From: rob@bottled.codes ("Rob Landers") --16b1a45c7e3c425baf6bb3f36341034a Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On Wed, Oct 22, 2025, at 14:57, Edmond Dantes wrote: > > > > If you await a value, everything works, but then someone somewhere e= lse awaits the same Awaitable that wasn't actually a "one-shot" Awaitabl= e, > > so now everything breaks sometimes, and other times not -- depending= on which one awaits first. > > >=20 > There is a function: >=20 > ```php > function my(mixed $i) > { > return $x + 5; > } > ``` >=20 > Someone called it like this somewhere: my("string"), and the code brok= e. > The question is: what should be done? >=20 > Do we really have to get rid of the mixed type just to prevent someone > from breaking something? > If a programmer explicitly violates the contracts in the code and then > blames the programming language for it. So the programmer is the one > at fault. > If someone uses a contract that promises non-idempotent behavior, it=E2= =80=99s > logical to rely on that. This isn't even the same example. We're not talking about type juggling,= but an interface. Mixed is not an object nor an interface. > > 2. memoization becomes an issue >=20 > The same question: why should the code work correctly if the > agreements are violated at the contract level? > There=E2=80=99s no element of randomness here. > 1. A person deliberately found the Awaitable interface and inserted it > into the code. > 2. They deliberately wrote that function. >=20 > So what exactly is the problem with the interface itself? >=20 > > With a "multi-shot" Awaitable, this is not practical or even a good = idea. You can't write general-purpose helpers, at all. > In what way are generalized types a problem? > Why not just use the contracts correctly? >=20 > ```php > function getOnce(Awaitable $some) { >=20 > if($some instanceof FutureLike =3D=3D=3D false) { > return await($some); > } >=20 > static $cache =3D []; > $id =3D spl_object_id($some); > return $cache[$id] ??=3D await($some); > } > ``` The "FutureLike" type is exactly what I'm arguing for! > > 3. static analysis > I really don=E2=80=99t see how static analysis could help here. What e= xactly > would it be checking? I can imagine getting something like the following foreach($next =3D await($awaitable)) { } *error*: usage of single-shot Awaitable in loop, this is an infinite loop or: $val =3D await($awaitable); //in another file/function/5 lines later/etc $val =3D await($awaitable); *error*: multiple usages of multi-shot Awaitable; you may get a differen= t result on each invocation of await() or something like that. >=20 > > 4. violation of algebraic laws with awaitAll/awaitAny >=20 > And the fact that awaitAll/awaitAny are designed to correctly handle > Awaitable objects? > I=E2=80=99m not aware of any such violations. I had to go take a look at the implementation. It appears NOT to handle = this case, at all. Meaning it treats all Awaitables passed to it as a si= ngle-shot Awaitable. (the code was easy to follow, but maybe I missed so= mething). The RFC doesn't spell this out and needs some work here. When I say "violations" I mean that, assuming $a and $b resolve instantl= y: awaitAll([$a, $b]) !=3D=3D [await($a), await($b)] awaitAny([delay(10, fn() =3D> await($a), await($b)]) !=3D=3D await($b) >=20 > > 5. violation of own invariants in the RFC > Can you show which statement of the RFC is being violated here? > What invariants? sidenote: It would really help to include the full context of the emails= you're replying to so I don't have to open our email conversation in an= other window to see exactly what I wrote. This is my mistake though. The discussion about await() is all mixed up = with coroutines so its hard to tell what the actual behaviour for await(= ) is outside of the context of coroutines. The violation I thought of on= ly applies to coroutines, the actual behaviour is "undefined" in the RFC. >=20 > > 6. common patterns aren't guaranteed anymore >=20 > So JavaScript yes? >=20 > ```js > class NonIdempotentThenable { > constructor() { > this.count =3D 0; > } > then(resolve, reject) { > this.count++; > resolve(this.count); > } > } >=20 > async function demo() { > const obj =3D new NonIdempotentThenable(); > console.log(await obj); // 1 > console.log(await obj); // 2 > console.log(await obj); // 3 >=20 > if (await obj) { > console.log(await obj); // 5 > } > } >=20 > demo(); > ```` >=20 > Other cases generally have the same problem =E2=80=94 a violation of t= he agreement. But you're talking about this being *the default*. There's a reason this= is an anti-pattern in Javascript and violation of the 'agreement' there. >> how will the scheduler handle backpressure? >=20 > I don=E2=80=99t really understand the problem with the Scheduler, and = even > less its relation to backpressure. > Dealing with backpressure is a matter of queue implementation. > The Awaitable contract has nothing to do with this situation. It has everything to do with it! If a multi-shot Awaitable keeps emittin= g, this causes repeated wakeups to the same continuation. It has to sche= dule this *every time* and there isn't a way to say "don't produce until= consumed". You can basically starve other tasks from executing. However= , if you use iterables of single-shot Awaitables, you get backpressure "= for free" and don't request the next future until the previous one is co= mplete, otherwise, the buffering pressure lands in the awaitable (unboun= ded memory) or in the schedulers ready queue (which affects fairness). F= urther, when cancelling a multi-shot awaitable, should the scheduler dro= p pending emissions and what happens if it keeps re-enqueing it anyway? = It makes the scheduler far more complicated than it needs to be! >> I'm not sure what your thought process is here, because in the last f= ew emails you've gone from "maybe" >> to doubling-down on this (from my perspective), but I feel like this = will be a footgun to both developers and the future of the language. >=20 > Confident about what exactly? > And what exactly would be the footgun? > These are very general statements. >=20 > It seems to me that people in this conference use the word =E2=80=9Cfo= otgun=E2=80=9D > far too often =E2=80=94 and not always in the right context. > There=E2=80=99s a lack of rational boundaries here. > Not every programmer=E2=80=99s mistake is a footgun. > Especially that: >=20 > ```php > if (await($response)) { > return await($response); > } > ``` A footgun is any reasonable looking code that subtly breaks because the = contract is weak, not because the programmer didn't RTFM. =E2=80=94 Rob --16b1a45c7e3c425baf6bb3f36341034a Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable
On Wed, Oct = 22, 2025, at 14:57, Edmond Dantes wrote:
>
> If you await a value, ever= ything works, but then someone somewhere else awaits the same Awaitable = that wasn't actually a "one-shot" Awaitable,
> so now every= thing breaks sometimes, and other times not -- depending on which one aw= aits first.
>

There is a function:=

```php
function my(mixed $i)
{
    return $x + 5;
}
`= ``

Someone called it like this somewhere: my("s= tring"), and the code broke.
The question is: what should be d= one?

Do we really have to get rid of the mixed = type just to prevent someone
from breaking something?
If a programmer explicitly violates the contracts in the code and then=
blames the programming language for it. So the programmer is = the one
at fault.
If someone uses a contract that pr= omises non-idempotent behavior, it=E2=80=99s
logical to rely o= n that.

This isn't even the same e= xample. We're not talking about type juggling, but an interface. Mixed i= s not an object nor an interface.

> 2. memoization becomes an issue

The same question: why should the code work corre= ctly if the
agreements are violated at the contract level?
There=E2=80=99s no element of randomness here.
1. A per= son deliberately found the Awaitable interface and inserted it
into the code.
2. They deliberately wrote that function.

So what exactly is the problem with the interface i= tself?

> With a "multi-shot" Awaitable, this= is not practical or even a good idea. You can't write general-purpose h= elpers, at all.
In what way are generalized types a problem?
Why not just use the contracts correctly?

<= div>```php
function getOnce(Awaitable $some) {

<= /div>
  if($some instanceof FutureLike =3D=3D=3D false) {
=
     return await($some);
  }

  static $cache =3D [];
  $id= =3D spl_object_id($some);
  return $cache[$id] ??=3D awa= it($some);
}
```

The "FutureLike" type is exactly what I'm arguing for!

=
> 3. static= analysis
I really don=E2=80=99t see how static analysis could= help here. What exactly
would it be checking?

I can imagine getting something like the followin= g

foreach($next =3D await($awaitable)) { }
error: usage of single-shot Awaitable in loop, this is an i= nfinite loop

or:

$val = =3D await($awaitable);
//in another file/function/5 lines late= r/etc
$val =3D await($awaitable);
error: mult= iple usages of multi-shot Awaitable; you may get a different result on e= ach invocation of await()

or something like tha= t.


> 4. violation of algebraic laws with awaitAll/awai= tAny

And the fact that awaitAll/awaitAny are de= signed to correctly handle
Awaitable objects?
I=E2=80= =99m not aware of any such violations.

=
I had to go take a look at the implementation. It appears NOT to ha= ndle this case, at all. Meaning it treats all Awaitables passed to it as= a single-shot Awaitable. (the code was easy to follow, but maybe I miss= ed something). The RFC doesn't spell this out and needs some work here.<= /div>

When I say "violations" I mean that, assuming $= a and $b resolve instantly:

awaitAll([$a, $b])= !=3D=3D [await($a), await($b)]
awaitAny([delay(10, fn() =3D&g= t; await($a), await($b)]) !=3D=3D await($b)


> 5. v= iolation of own invariants in the RFC
Can you show which state= ment of the RFC is being violated here?
What invariants?
=

sidenote: It would really help to inclu= de the full context of the emails you're replying to so I don't have to = open our email conversation in another window to see exactly what I wrot= e.

This is my mistake though. The discussion ab= out await() is all mixed up with coroutines so its hard to tell what the= actual behaviour for await() is outside of the context of coroutines. T= he violation I thought of only applies to coroutines, the actual behavio= ur is "undefined" in the RFC.


> 6. common patterns are= n't guaranteed anymore

So JavaScript yes?
=

```js
class NonIdempotentThenable {
<= div>  constructor() {
    this.count =3D 0;
  }
&n= bsp; then(resolve, reject) {
    this.count++;
    resol= ve(this.count);
  }
}

async function demo() {
&nbs= p; const obj =3D new NonIdempotentThenable();
  console.log(await obj); // 1
&n= bsp; console.log(await obj); // = 2
  console.log(a= wait obj); // 3

  if (await obj) {
    console.log(await obj); // 5

demo();
````

Other cases generally= have the same problem =E2=80=94 a violation of the agreement.

But you're talking about this being the default. There's a reason this is an anti-pattern in Javascript= and violation of the 'agreement' there.

how will the scheduler handle backpressure?

I don=E2=80=99t really understand th= e problem with the Scheduler, and even
less its relation to ba= ckpressure.
Dealing with backpressure is a matter of queue imp= lementation.
The Awaitable contract has nothing to do with thi= s situation.

It has everythi= ng to do with it! If a multi-shot Awaitable keeps emitting, this causes = repeated wakeups to the same continuation. It has to schedule this = every time and there isn't a way to say "don't produce until cons= umed". You can basically starve other tasks from executing. However, if = you use iterables of single-shot Awaitables, you get backpressure "for f= ree" and don't request the next future until the previous one is complet= e, otherwise, the buffering pressure lands in the awaitable (unbounded m= emory) or in the schedulers ready queue (which affects fairness). Furthe= r, when cancelling a multi-shot awaitable, should the scheduler drop pen= ding emissions and what happens if it keeps re-enqueing it anyway? It ma= kes the scheduler far more complicated than it needs to be!
I'm not sure what you= r thought process is here, because in the last few emails you've gone fr= om "maybe"
to doubling-down on this (from my perspective), but= I feel like this will be a footgun to both developers and the future of= the language.

Confident abou= t what exactly?
And what exactly would be the footgun?
These are very general statements.

It seems = to me that people in this conference use the word =E2=80=9Cfootgun=E2=80= =9D
far too often =E2=80=94 and not always in the right contex= t.
There=E2=80=99s a lack of rational boundaries here.
Not every programmer=E2=80=99s mistake is a footgun.
Especi= ally that:

```php
if (await($response= )) {
  return await($response);
}
```=

A footgun is any reasonable= looking code that subtly breaks because the contract is weak, not becau= se the programmer didn't RTFM.

=E2=80=94 Rob
--16b1a45c7e3c425baf6bb3f36341034a--