>
>  Thinking aloud, perhaps we could cause `new Fiber` to create an
automatic async block?
>

The main issue with Fibers is their switching logic:
If you create Fiber A and call another Fiber B inside it, Fiber B can only
return to the Fiber that created it, not just anywhere. However, the
Scheduler requires entirely different behavior.

This creates a conflict with the Scheduler. Moreover, it can even break the
Scheduler if it operates based on Fibers. That's why all these strange
solutions in the RFC are just workarounds to somehow bypass this problem.
But it seems we've already found an alternative solution.

> I cannot speak for Derick.

Of course, I just mean that he probably won't be happy about it :)

> No, just those functions/objects that necessarily involve running async
control commands.  Most wouldn't.
> They would just silently context switch when they hit an IO operation
(which as noted above is transparency supported, which is what makes this
> work) and otherwise behave the same.

So it's something more like Go or Python.

>
>  $val = async(function(AsyncContext $ctx) use ($stuff, $fn) {
>  $result = [];
>  foreach ($stuff as $item) {
>   $result[] = $ctx->run($fn);
>}
>
>  // We block/wait here until all subtasks are complete, then the async()
call returns this value.
>  return $result;
> });

Do I understand correctly that at the point $val =
async(function(AsyncContext $ctx) use ($stuff, $fn) execution stops until
everything inside is completed?

If so, let me introduce a second semantic option (for now, I'll remove the
context and focus only on the function).

```php
$url1 = 'https://domain1.com/';
$url2 = 'https://domain2.com/';

$url_handle = fn(string $url) => file_get_contents($url);

$res = Async\start(function() use ($url1, $url2, $url_handle) {
    $res1 = Async\run($url_handle, $url1);
    $res2 = Async\run($url_handle, $url2);

    Async\run(fn() => sleep(5));

    // some logic here

    return $merged_result;
});
```
What's Happening Here:

   1. After calling $res = Async\start(), the code waits until the entire
   block completes.
   2. Inside Async\start, the code waits for all nested coroutines to
   finish.
   3. If a coroutine has other nested coroutines, the same rule applies.

Rules Inside an Asynchronous Block:

   1. I/O functions do not block coroutines within the block.
   2. Creating a new Fiber is not allowed — an exception will be thrown:
   you cannot use Fiber.
   3. Unhandled exceptions will be thrown at the point of $res =
   Async\start().

Coroutine Cancellation Rules:

Canceling a coroutine cancels it and all its child coroutines (this cannot
be bypassed unless the coroutine is created in a different context).

How does this option sound to you?

Essentially, this is Kotlin, but it should also resemble Python. However,
unlike Kotlin, there are no special language constructs here—code blocks
naturally serve that role. Of course, syntactic sugar can be added later
for better readability.

And if you like this, I have good news: there are no implementation issues
at this level.

In terms of semantic elegance, the only thing that bothers me is that return
behavior is slightly altered — meaning the actual "return" won’t happen
until all child functions complete. This isn’t very good, and Kotlin’s
style would fit better here.

But on the other hand — can we live with this?

> I cannot speak to JS Symbols as I haven't used them.
> I am just vhemently opposed to globals, no matter how many layers they're
wrapped in. :-)  Most uses could be replaced by proper DI or partial
application.

You won’t be able to use DI because you have only *one service (instance of
class)* for the entire application, not a separate service for each
coroutine. This service is shared across the application and can be called
from any coroutine. As a result, the service needs memory slots to store or
retrieve data. DI is a mechanism used once during service initialization,
not every time a method is called.

The only question is whether to use open text keys in the context, which is
unsafe and can lead to collisions, or to use a unique key-object that is
known only to the one who created it. (If PHP introduces object constants,
this syntax would also look elegant.)

There is, of course, another approach: making Context any arbitrary object
defined by the user. But this solution has a known downside — lack of a
standard interface.

> (Or even just $ch->inPipe and $ch->outPipe, now that we have nice
property support.)

Just a brilliant idea. :)

Have a good day!

Ed.

Reply via email to