On Sat, Mar 8, 2025, at 00:21, Rowan Tommins [IMSoP] wrote:
> On 07/03/2025 22:01, Larry Garfield wrote:
> > Is that what you're suggesting?  If so, I'd have to think it through a bit 
> > more to see what guarantees that does[n't] provide.  It might work.  (I 
> > deliberately used spawn instead of "await" to avoid the mental association 
> > with JS async/await.)  My biggest issue is that this is starting to feel 
> > like colored functions, even if partially transparent.
> 
> 
> Yes, that's pretty much what was in my head. I freely admit I haven't 
> thought through the implications either.
> 
> 
> > My biggest issue is that this is starting to feel like colored functions, 
> > even if partially transparent.
> 
> 
> I think it's significantly *less* like coloured functions than passing 
> around a nursery object. You could almost take this:
> 
> async function foo($bar int, $baz string) {
>     spawn something_else();
> }
> spawn foo(42, 'hello');
> 
> As sugar for this:
> 
> function foo($bar int, $baz string, AsyncNursery $__nursery) {
>     $__nursery->spawn( something_else(...) );
> }
> $__nursery->spawn( fn($n) => foo(42, 'hello', $n) );
> 
> However you spell it, you've had to change the function's *signature* in 
> order to use async facilities in its *body*.
> 
> 
> If the body can say "get current nursery", it can be called even if its 
> *immediate* caller has no knowledge of async code, as long as we have 
> some reasonable definition of "current".
> 
> 
> -- 
> Rowan Tommins
> [IMSoP]
> 
The uncoloring of functions in PHP is probably one of the most annoying aspects 
of fibers, IMHO. It's hard to explain unless you've been using them awhile. 
But, with colored functions, the caller has control over when the result is 
waiting on -- it could be now, it could be in a totally different part of the 
program, or not at all. With fibers, the author of the function you are calling 
has control over when the result is waited on (and they don't have control over 
anything they call). This can create unpredictable issues when writing code 
where a specific part wrote some code thinking it had exclusive access to a 
property/variable. However, someone else changed one of the functions being 
called into an async function, making that assumption no longer true.

With colored functions, the person making changes also has to update all the 
places where it is called and can validate any assumptions are still going to 
be true; uncolored functions means they almost never do this. This results in 
more work for people implementing async, but more correct programs overall.

But back to the awaiting on results. Say I want to read 10 files:

for ($i = 0; $i < 10; $i++) $results[] = file_get_contents($file[$i]);

Right now, we have to read each file, one at a time, because this is 
synchronous. Even with this RFC and being in a fiber, the overall execution 
might be non-blocking, but the code still reads one file after another 
sequentially. Fibers do not change this.

With this RFC (in its original form), we will be able to change it so that we 
can run it asynchronously though and choose when to wait:

for($i = 0; $i < 10; $i++) $results[] = async\async(fn($f) => 
file_get_contents($f), $file[$i]);
// convert $results into futures somehow -- though actually doesn't look like 
it is possible.
$results = async\awaitAll($results);

In that example, we are deliberately starting to read all 10 files at the same 
time. If we had colored functions (aka, async/await) then changing 
file_get_contents to async would mean you have to change everywhere it is 
called too. That means I would see that file_get_contents is synchronous and be 
able to optimize it without having to even understand the reasoning (in most 
cases). I was a user of C# when this happened to C#, and it was a pain... So, 
at least with PHP fibers, this won't be AS painful, but you still have to do 
some work to take full advantage of them.

I kind of like the idea of a nursery for async, as we could then update 
file_get_content's return type to something like 
string|false|future<string|false>. In non-async, you have everything behave as 
normal, but inside a nursery, it returns a future that can be awaited however 
you want and is fully non-blocking. In other words, simply returning a future 
is enough for the engine to realize it should spawn a fiber (similar to how 
using yield works with generators). 

In any case, I believe that a nursery requires the use of colored functions. 
That may be good or bad, but IMHO makes it much more useful and easier to write 
correct and fast code.

— Rob

Reply via email to