Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:126618 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 308341A00BE for ; Fri, 7 Mar 2025 09:24:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1741339334; bh=MifFiXnMG7+3tRiEd3NIDy67l0I7cyYygi9nsZB03eI=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=ki4sISv2UQg9odmoI64NKpJ+p2kdLVuhC9eeqiUM5IAaFIQ7Nf9md0/18dEY3rKPW Xk1e3DsDIuZlstfH4mTi0bps8siHucoooY7MWtK2d49K9Plb2vuDL5CR/AbZgYcaH/ Xt4akZF98X3BtIlv3AgdRj8ozxo9Sfvv2lFD/MtUlOf32MSDrxlu7YWWQSJu0L1UdT MDCB3wnCDbMNxB+tQF0CNZtEE0LvPMQbzhVy37IYhB1B3XXD7A/7GnnNiSbUTEiA0S W3cakEVFt5POYSuP+bbE58j5Zf+8tfS95fFd+ixO3WTxbvqMSVm9+0zNAbttMY5qQn BH0ksvLrUuPpQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 32D9118004A for ; Fri, 7 Mar 2025 09:22:11 +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=-3.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE, SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-yb1-f172.google.com (mail-yb1-f172.google.com [209.85.219.172]) (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 ; Fri, 7 Mar 2025 09:22:07 +0000 (UTC) Received: by mail-yb1-f172.google.com with SMTP id 3f1490d57ef6-e60b75f87aaso1260671276.2 for ; Fri, 07 Mar 2025 01:24:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1741339482; x=1741944282; 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=SpGKWet3MGAmhPJwOLwANh3jkwhcHin0U6POUeELwqk=; b=iLgBEQnILyUFi9L7NpkAR5E4zZIkFPIXRfI8OmkAu3Nmc0wZM/KcIsMueofoR28H5H CgJO/jCrqPATUvPsA9bggDGzhEAM3fv0jxmpLyZzauLD09Qse34SUYFYisHNZJsOVmmd cpZ2Z+FNxGb+INkhBjtmN/Df0EWIX6R+13jw6B/mWMQ4qlv8NTVlIF+3UUO9buhHb+Am 7Jh0/7bfyliQkVkT8S1HyQyvyR8w72XU2zR7AtypAFrXUhUofZ2NhazGPCIEsN/eB4GZ wE4aLoA7XxZQW2jwWpuuNl+GKNZ93Ynz1u2dU10EUYay7oJ8vtGuYr0OuF3DZB3GolZK VkUA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741339482; x=1741944282; 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=SpGKWet3MGAmhPJwOLwANh3jkwhcHin0U6POUeELwqk=; b=ipngV/xak1jccQlKaYBLgfTOrLXz52PqLT0MoXmLUOJDN+Pm92BzIBj7uXPmAyYV2j PYoNUpLORE8LVU1OD/Qbe8Hyyg0+nra/ZXaTdmcDj4POGZW7itxD94D/UR4feNBudoic E57I04+Byopsld5CR5UZoz0WrkhroVugaBqhmrUG/ywayu671AoqN8eJNm/HzoDADcJb oObs2Pkfz9PK1v8UnqRkqjRZ8vtVxX/5F5xGenY6gLMO3yuykliiVtgXO/xr1D+4Mc+Y +IVZ1/DbLIv64/tEgGtyKRn3KNox+v5snBW0w1e8+BV9Hg75+PXxrtg6dCY3yHKiwJFZ inbw== X-Gm-Message-State: AOJu0YzWuCLh0vPlhZT1q2irjBdZRS/wmipC8Y6R1zAoPnQBRTOtrBqA guayQZEu9FB+FFT4Uo1m0MP7/aBj5rwXty5GvvOlxBlVIS2erhiGFwowwQeYzTto+e0O2uD+hJx Php3TlSC1AhdwnsMieMjRqrJF7YD5BZJ+Gz0= X-Gm-Gg: ASbGncuxRVIlUuImHQH1a5f8Awe+Y5hxmjPSQrpxfO4N2ReYmQtWMooGXZrJAy0OBS5 XvIaHxGflozfPEo+81KvvSojTut3XszpPP5NnMRLeIFUiYeD/yRvjPBe818eoU8f0IMxviDUIkG g1HHC0wgMh+7eTQ+JG74BL2CZhVg== X-Google-Smtp-Source: AGHT+IHo2X9u25EGUEs0N4IUXgQmNtxoAKzr7eFTpWTGMtx3QX6SR5VSk89g29PQwdElzyr5/q0MFBU7KWFFAhdRn9Y= X-Received: by 2002:a05:6902:3302:b0:e60:abb8:df10 with SMTP id 3f1490d57ef6-e635c1758edmr3009359276.23.1741339482523; Fri, 07 Mar 2025 01:24:42 -0800 (PST) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 References: <9964db8c-0ffe-43d5-8246-47fc76b07180@app.fastmail.com> <78a03dd0-fd4a-4f4a-ad8a-37e5704f06fc@app.fastmail.com> <08c8ad0b-e8f4-46e3-99f0-b80748d40b89@app.fastmail.com> In-Reply-To: <08c8ad0b-e8f4-46e3-99f0-b80748d40b89@app.fastmail.com> Date: Fri, 7 Mar 2025 11:24:31 +0200 X-Gm-Features: AQ5f1JriM1KsgNvjBhJug9hoO8TMu8hp5D6SXDEP7XHpVLwWaMNusqUABD_9_yI Message-ID: Subject: Re: [PHP-DEV] PHP True Async RFC To: Larry Garfield Cc: php internals Content-Type: multipart/alternative; boundary="0000000000004a13cc062fbd31bc" From: edmond.ht@gmail.com (Edmond Dantes) --0000000000004a13cc062fbd31bc Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable > > See, what you call "paternalistic" I say is "basic good usability." > Affordances are part of the design of everything. Good design means making doing the > If we worry about "intuitive usability", we should ban caching, finite state machines, and of course, concurrency. Parallelism? Not just ban it, but burn those who use it at the stake of the inquisition! :) In this context, the child-parent model has a flaw that directly contradicts intuitive usage. Let me remind you of the main rule: Default behavior: All child coroutines are canceled if the parent is canceled. Now, imagine a case where we need to create a coroutine not tied to the parent. To do this, we have to define a separate function or syntax. Such a coroutine is created to perform an action that must be completed, even if the parent coroutines are not fully executed. Typically, this is a critical action, like logging or sending a notification. This leads to an issue: * Ordinary actions use a function that the programmer always remembers. * Important actions require a separate function, which the programmer might forget. This is the dark side of any strict design when exceptions exist (and they almost always do). And the problem is bigger than it seems because: 1. The parent coroutine is created in Function A. 2. The child coroutine is created in Function B. 3. These functions are in different modules, written by different developers. Developer A implements a unique algorithm that cancels coroutine execution. This algorithm is logical and correct in the context of A. Developer B simply forgets that execution might be interrupted. And boom! We've just introduced a bug that will send the entire dev team on a wild goose chase. This is why the Go model (without parent-child links) is different: It makes chaining coroutines harder. But if you don=E2=80=99t need chains, it=E2=80=99s simpler. And whether you need chains or not is a separate question. Possible scenarios in PHP *Scenario 1* We need to generate a *report*, where data must be collected from multiple services. - We create *one coroutine per service*. - Wait for all of them to finish. - Generate the report. Parent-child model is ideal: If the *parent coroutine* is canceled, the *child coroutines* are meaningless as well. ------------------------------ *Scenario 2* *Web server.* The API receives a request to create a *certificate*. The algorithm: 1. *Check* if we can do it, then create a *DB record* stating that the user has a certificate. 2. *Send a Job* =E2=80=93 notify other users who need to know about this= event. 3. *Return the certificate URL* (a link with an ID). *Key requirement:* - *Heavy operations* (longer than *2-3 seconds*) should be performed *in a Job-Worker pool* to keep the server *responsive*. - Notifications are sent *as a separate Job* in *a separate coroutine*, which: - Can retry sending *twice if needed*. - Implements a *fallback mechanism*. - *Is NOT linked* to the request coroutine. ------------------------------ Which scenario is more likely for PHP? > > To quote someone on FP: "The benefit of functional programming is it makes data flow explicit. The downside is it sometimes painfully explicit." > If there is a nesting of 10 functions where parameters are passed explicitly, then the number of parameters in the top function will be equal to the sum of the parameters of all other functions, and the overall code coupling will be 100%. Parameters can be grouped into objects (structures), thus reducing this problem. However, creating additional objects leads to the temptation to shove a parameter into the first available object because thinking about composition is a difficult task. This means that such an approach either violates SOLID or increases design complexity. But usually, the worst-case scenario happens: developers happily violate both SOLID and design. :) I think these principles are more suitable for areas where design planning takes up 30-50% of the total development time and where such a time distribution is rational in relation to the project's success. At the same time, the initial requirements change extremely rarely. PHP operates under completely different conditions: "it was needed yesterday" :) > > As above, in simpler cases you can just make the context a boring old function parameter, > What if a service wants to store specific data in the context? As for directly passing the context into a function, the coroutine already owns the context, and it can be retrieved from it. This is a consequence of PHP having an abstraction that C/Rust lacks, allowing it to handle part of the dirty work on behalf of the programmer. It's the same as when you use $this when calling a method. > > Do you have a concrete example of where the inconvenience of explicit context is sufficiently high to warrant an implicit global and all the impacts that has? > The refactoring issue. There are five levels of nesting. At the fifth level, someone called an asynchronous function and created a context. Thirty days later, someone wanted to call an asynchronous function at the first level of nesting. And suddenly, it turns out that the context needs to be explicitly passed. And that's where the fun begins. :) --- Ed. --0000000000004a13cc062fbd31bc Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
>
> See, what you call "paternalistic" = I say is "basic good usability."
> Affordances are part of= the design of everything.=C2=A0 Good design means making doing the
>=

If we worry about "intuitive usability", we should ban ca= ching, finite state machines, and of course, concurrency.
Parallelism? = Not just ban it, but burn those who use it at the stake of the inquisition!= :)

In this context, the child-parent model has a flaw that directly= contradicts intuitive usage.
Let me remind you of the main rule:
Default behavior: All child coroutines are canceled if the parent is canc= eled.

Now, imagine a case where we need to create a coroutine not t= ied to the parent.
To do this, we have to define a separate function or = syntax.

Such a coroutine is created to perform an action that must b= e completed,
even if the parent coroutines are not fully executed.
= Typically, this is a critical action, like logging or sending a notificatio= n.

This leads to an issue:

* Ordinary actions use a function= that the programmer always remembers.
* Important actions require a se= parate function, which the programmer might forget.

This is the dark= side of any strict design when exceptions exist (and they almost always do= ).

And the problem is bigger than it seems because:

1. The pa= rent coroutine is created in Function A.
2. The child coroutine is creat= ed in Function B.
3. These functions are in different modules, written b= y different developers.

Developer A implements a unique algorithm th= at cancels coroutine execution.
This algorithm is logical and correct in= the context of A.
Developer B simply forgets that execution might be in= terrupted.
And boom! We've just introduced a bug that will send the = entire dev team on a wild goose chase.

This is why the Go model (wit= hout parent-child links) is different:
It makes chaining coroutines hard= er.
But if you don=E2=80=99t need chains, it=E2=80=99s simpler.
And w= hether you need chains or not is a separate question.

Possible scenarios in PHP

Scenario 1

We need to generate a report, where data must be collec= ted from multiple services.

  • We create one coroutine per service.
  • Wait for all of them to finish.
  • Generate the report.

Parent-child model is ideal:
If the parent coroutine is canceled, the child cor= outines are meaningless as well.


Scenario 2

Web server. The API receives a request to create a certificate. The algorithm:

  1. Check if we can do it, then create a DB record= stating that the user has a certificate.
  2. Send a Job =E2=80=93 notify other users who need to kn= ow about this event.
  3. Return the certificate URL (a link with an ID).

Key requirement:

  • Heavy operations (longer than 2-3 seconds) should be performed in a Job-Worker pool to keep the= server responsive.
  • Notifications are sent as a separate Job in a = separate coroutine, which:
    • Can retry sending twice if needed.
    • Implements a fallback mechanism.
    • Is NOT linked to the request coroutine.

Which scenario is more li= kely for PHP?

>=C2=A0
>=C2=A0
To quote someone on FP: "The benefit of functional pr= ogramming is it makes data flow=C2=A0explicit. The downside is it sometimes painfully ex= plicit."=C2=A0
>
If there is a nesting= of 10 functions where parameters are passed explicitly, then the number of= parameters in the top function will be equal to the sum of the parameters = of all other functions, and the overall code coupling will be 100%. Paramet= ers can be grouped into objects (structures), thus reducing this problem. H= owever, creating additional objects leads to the temptation to shove a para= meter into the first available object because thinking about composition is= a difficult task. This means that such an approach either violates SOLID o= r increases design complexity. But usually, the worst-case scenario happens= : developers happily violate both SOLID and design. :)=C2=A0

I= think these principles are more suitable for areas where design planning t= akes up 30-50% of the total development time and where such a time distribu= tion is rational in relation to the project's success.=C2=A0 At the same time, the initial requirements change extremely rarely. PHP ope= rates under completely different conditions: "it was needed yesterday&= quot; :)

>
>=C2=A0As abo= ve, in simpler cases you can just make the context a boring old function pa= rameter,
>

What if a service wants to store speci= fic data in the context?=C2=A0

As for directly passing the context into a function, the coroutine a= lready owns the context, and it can be retrieved from it. This is a consequ= ence of PHP having an abstraction that C/Rust lacks, allowing it to handle = part of the dirty work on behalf of the programmer. It's the same as wh= en you use $this when calling a method.

>
>=C2= =A0Do you have a conc= rete example of where the inconvenience of explicit context is=C2=A0= sufficiently high to = warrant an implicit global and all the impacts that has?
>
=
The refactoring issue. There are five levels of nesting. At the fifth l= evel, someone called an asynchronous function and created a context. Thirty= days later, someone wanted to call an asynchronous function at the first l= evel of nesting. And suddenly, it turns out that the context needs to be ex= plicitly passed. And that's where the fun begins. :)=C2=A0=C2=A0

= ---
Ed.

--0000000000004a13cc062fbd31bc--