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