> On Oct 23, 2025, at 04:18, Rob Landers <[email protected]> wrote:
> 
> On Thu, Oct 23, 2025, at 09:59, Edmond Dantes wrote:
>> 
>> 
>> I’d like you to have the full information necessary to make an
>> informed decision.
>> 
>> There are two or more channel objects that need to be awaited. How
>> should this be implemented?
>> 
>> Currently
>> ```php
>> [$result1, $result2] = awaitAny($channel1, $channel2);
>> ```
>> 
> As for which one I'd rather have, I'd rather have the one where my examples 
> hold true:
> 
> assert(awaitAll($a, $b, $c) === [await($a), await($b), await($c)]);
> 
> It's clear what it does and that it is just a shorthand. When that fails, we 
> have issues.
> 
> — Rob


Just to add to this, in Swift, a Task (the analogue to this proposal's 
Coroutine) can emit, exactly once, a value or an error (exception). Attempting 
to read its value (or error) multiple times gets you the same value back. This 
is very handy in memoization patterns.

In this example, multiple readers can call getImage() with the same id and get 
a value, regardless of whether they're the first (which kicks off image 
loading), whether the image loading has completed, or whether the image load is 
still in progress. (Technically, one could omit the LoadingBox wrapper 
entirely, but that would cause the Task to persist after it's completed, and 
that's a more expensive structure than a simple enum.)

```swift
private enum LoadingBox<T : Sendable> {
        case inProgress(Task<T, Never>) //Task<Success, Failure>
        case ready(T)
        
        var value: T {
                get async {
                        switch self {
                        case .inProgress(let task):
                                return await task.value
                        
                        case .ready(let value):
                                return value
                        }
                }
        }
}

private var imageCache: [ImageId : LoadingBox<Image>] = [:]

func getImage(_ id: ImageId) async -> Image {
        if let cache = imageCache[id] {
                return await cache.value
        }

        let task = Task { ...load the image... }
        imageCache[id] = task
        
        return await task.value
}
```

Ultimately, I would want to be able to write code such as this in PHP (with the 
generics, of course, but I'm not holding my breath).

If coroutines sometimes return single values and sometimes return multiple 
values, it becomes much harder to write a generic async cache/memoization 
system, because you can't indicate via the type system that a single value is 
expected.

-John

Reply via email to