Hello all.

A few thoughts aloud about the emerging picture.

### Entry point into the asynchronous context
Most likely, it should be implemented as a separate function (I haven't
come up with a good name yet), with a unique name to ensure its behavior
does not overlap with other operators. It has a unique property: it waits
for the full completion of the event loop and the Scheduler.

Inside the asynchronous context, `Fiber` is prohibited, and conversely,
inside a `Fiber`, the asynchronous context is prohibited.

### The `async` operator
The `async` (or *spawn*?) operator can be used as a shorthand for spawning
a coroutine:

```php
function my($param) {}

// Operator used as a function call
async my(1);
or
spawn my(1);

// Operator used as a closure
async {
    code
};

// Since it's a closure, the `use` statement can be used without
restrictions
async use($var) {
    code
};

// Returns a coroutine class instance
$x = async use($var) {
    code
};
```

### The `await` operator
The `await` operator can be added to `async`, allowing explicit suspension
of execution to wait for a result:

```php
$x = await async use($var) {
    code
};
```

## Context Manipulations
I didn't like functions like `overrideContext`. They allow changing the
context multiple times at any point in a function, which can lead to errors
that are difficult to debug. This is a really bad approach. It is much
better to declare the context at the time of coroutine invocation. With
syntax, it might look like this:

```php
async in $context use() {}
async in $context myFun()
async in $context->with("key", value) myFun()
or
spawn in $context ...
```

## Thread
The syntax spawn in/async in can be used not only in standard cases.

```php
$coro = async in new ThreadContext() use($channel) {
    while() {}
};

// This expression is also valid
$coro = async in new $threadPool->borrowContext() use($channel) {
    while() {}
};
```

It is worth noting that $threadPool itself may be provided by an extension
and not be a part of PHP.

## Unrelated Coroutines
An additional way is needed to create a coroutine that is not bound to a
parent.
It's worth considering how to make this as clear and convenient as possible.
Maybe as keyword:

```php
async unbound ...
```

Of course, an `async child` modifier can be used. This is the inverse
implementation, but I think it will not be used often. Making `unbound` a
separate method is not very appealing at the moment because a programmer
might forget to call it. They could forget the word `unbound`, and even
more so a whole method.

## Context Operations
```php
await $context; // Waits for all coroutines in the context
$context.cancel(); // Cancels everything within the context
```

## Flow

I want to thank all the participants in the discussion.
Thanks to your ideas, questions, and examples. A week ago, answering this
question would have been impossible.

If we add exception handling and graceful shutdown, and remove the new
syntax by replacing it with an equivalent of 2-3 functions, we will get a
fairly cohesive RFC that describes the high-level part without unnecessary
details. Channels and even Future can be excluded from this RFC —
everything except the coroutine class and context. Microtasks, of course,
will remain.

As a result, the RFC will be clean and compact, focusing solely on how
coroutines and context work.

Channels, Future, and iterators can be moved to a separate RFC dedicated
specifically to primitives.

Finally, after reviewing the high-level RFCs, we can return to the
implementation — in other words, top-down. Given that the approximate
structure of the lower level is already clear, discussing abstractions will
remain practical and grounded.

Just to clarify, I’m not planning to end the current discussion these are
just intermediate thoughts.

---

Ed.

>

Reply via email to