On Nov 15, 2025, at 16:20, Edmond Dantes <[email protected]> wrote:
> 
> Hello.
> 
>> An array_map() always blocks the thread but should never suspend.
> This function is not related to this discussion or to the RFC.

function writeData() {
        return array_map(function($elt) {
                [$path, $content] = $elt;
                return [$path, file_put_contents($path, $content)]; //POSSIBLE 
SUSPENSION POINT
        }, $this->data);
}

Now this array_map function potentially suspends. As of course does this 
writeData(). And whatever calls this writeData(). And so on up the stack.

_Any_ function that calls any other function might have a hidden suspension 
point. And as Rob pointed out, any hook might also have a hidden suspension 
point, so you can't even trust code that doesn't look like it calls functions. 
And even if there are no hooks, __get(), __set(), etc are also there to ruin 
your day.


>> To provide an explicit example for this, code that fits this pattern is 
>> going to be problematic
> 
> Why is this considered a problem if this behavior is part of the
> language’s contract?

Because this RFC *changes the contract* out from every line of php code ever 
written. Code that used to be strictly synchronous now has async suspension 
points it didn't ask for.


>> $this->data can be changed out from under writeData(), which leads to 
>> unexpected behavior.
> 
> So the developer must intentionally create two different coroutines.
> Intentionally pass them the same object.
> Intentionally write this code.
> And the behavior is called “unexpected”? :)

Yes.

To repeat, a core premise of this RFC is:

>     • From a PHP developer's perspective, the main value of this 
> implementation is that they DO NOT NEED to change existing code (or if 
> changes are required, they should be minimal) to enable concurrency. Unlike 
> explicit async models, this approach lets developers reuse existing 
> synchronous code inside coroutines without modification.
>     • Code that was originally written and intended to run outside of a 
> Coroutine must work EXACTLY THE SAME inside a Coroutine without modifications.

With this, the RFC implies that I should be able to take my synchronous PHP 
code and run it in a coroutine with other synchronous PHP code, and it will all 
just work. But obviously that won't work.

I understand that a different interpretation of this wording is, "well, that 
code does exactly what it did before, just that with coroutines, that happens 
to be broken". I could maybe squint and grunt disapprovingly about it being 
"technically correct", except "has a suspension point" is *definitely not* 
exactly how it worked before.


> The changes described in the RFC refer to the algorithm for handling
> I/O functions in blocking mode. And of course these words assume that
> we haven’t lost our minds and understand that you cannot write
> completely different message sequences to the same socket at the same
> time. In practice, changes are of course sometimes necessary, but
> throughout my entire experience working with coroutines, I should note
> that I have never once run into the example you mentioned. Even when
> adapting older projects. And do you know why? Because the first thing
> we refactored in the old code was the places with shared variables.

Well, but that's not what my example was doing. My example was taking a set of 
(filename, content) pairs and writing them to individual files. But it was 
doing it in an async-unsafe way (because that has never before been a 
consideration), and so its data source can be corrupted _while it is executing_.

The async problems in my example might be glaringly obvious (for people skilled 
in the art), but many other async issues are much more subtle, such as Rob 
described in his follow-on email to mine. For people who have never had the 
pleasure of working with async code before, "the world changed out from under 
me" is not a scenario you're accustomed to thinking about.



> But in PHP, colored functions are inconvenient. Overloading I/O
> functions does not lead to serious errors that make developers suffer;
> on the contrary, it saves time and gives the language more
> flexibility.

Of course colored functions are inconvenient. But it's necessary or else you 
open to a whole class of easily avoidable problems. _Those_ problems are _much_ 
more inconvenient than colored functions.

Code that was written to be synchronous should remain synchronous unless it is 
explicitly modified to be asynchronous.


> A developer should strive to minimize asynchronous code in a project.
> The less of it there is, the better. Asynchronous code is evil. An
> anti-pattern. A high-complexity zone. But if a developer chooses to
> use asynchronous code, they shouldn’t act like they’re three years old
> and seeing a computer for the first time. Definitely not. This
> technology requires steady, capable hands :)

What this says to me is, "Here's a foot-gun. Please use it responsibly."

Now, I definitely want to have advanced features available for when they're 
needed. But that said, It would be great if we can avoid introducing new 
foot-guns, especially when we have the knowledge and experience from other 
languages to draw on and do better. And when we do introduce new foot-guns, 
it's better if we can make them strategically-targeted sniper rifles instead of 
blunderbusses.

If we allow for hidden async behavior, the entire system becomes impossible to 
reason about. Some library I'm using can cause an async race condition without 
me asking for it, and I can't know it doesn't unless I audit it and its 
interaction with my code. 

And we know from practice that enough people won't use async responsibly that 
it will make the language look bad. What will happen is a junior dev will 
decide (or be told) that some code is performing poorly and they will see a 
forum post that says "use coroutines" or "use async" and they will cargo-cult 
their way to a hidden problem because they didn't take into consideration the 
full problem space. Not to say that can't happen with explicit suspensions, but 
the ceremony of declaring you have a possible suspension at least gives a 
pointer of, "this is where there's a suspension; what happens if it does?".

-John

Reply via email to