This pitch was reviewed, accepted, and implemented for Swift 6.0: swift-evolution/proposals/0421-generalize-async-sequence.md at main · apple/swift-evolution · GitHub
Ah Amazing, sorry I was looking at the wrong link and thought it still needed to go through review
Primary associated types on
AsyncSequence
would enable hiding concrete implementation details behind constrained opaque or existential types, such as in transformation APIs onAsyncSequence
.
Now that this is implemented, what’s stopping the adoption of opaque return types in the AsyncSequence operators of the standard library?
As I see it, this would be the last piece of the puzzle to enable the use of existential AsyncSequence types in interfaces and eliminate the type erasure confusion raised by many here and here.
The current signature of AsyncSequence operators uses concrete types, which most of the time have a generic Base
requirement:
func map<Transformed>(
_ transform: @escaping @Sendable (Self.Element) async -> Transformed
) -> AsyncMapSequence<Self, Transformed>
Using Self
as a generic parameter in the operator signature prevents using it on existential AsyncSequences:
protocol ExistentialService {
func get() -> any AsyncSequence<String, Error>
}
protocol GenericService {
associatedtype GetResult: AsyncSequence<String, Error>
func get() -> GetResult
}
func consume(service: ExistentialService) {
service.get().map { // Member 'map' cannot be used on value of type 'any AsyncSequence<String, any Error>'; consider using a generic constraint instead
$0.capitalized
}
}
func consume(service: any GenericService) {
service.get().map { // Member 'map' cannot be used on value of type 'any AsyncSequence<String, any Error>'; consider using a generic constraint instead
$0.capitalized
}
}
Given map
would hide its implementation details with an opaque return type, the example above would compile just fine:
extension AsyncSequence {
func map<Transformed>(
_ transform: @escaping @Sendable (Self.Element) async -> Transformed
) -> some AsyncSequence<Transformed, Failure> {
AsyncMapSequence<Self, Transformed>(base: self, transform: transform)
}
}