Skip to content

Rework AsyncStreaming protocols to use generic `RangeReplaceableCon…#417

Merged
FranzBusch merged 1 commit into
apple:mainfrom
FranzBusch:fb-async-streaming-container
May 14, 2026
Merged

Rework AsyncStreaming protocols to use generic `RangeReplaceableCon…#417
FranzBusch merged 1 commit into
apple:mainfrom
FranzBusch:fb-async-streaming-container

Conversation

@FranzBusch
Copy link
Copy Markdown
Member

…tainer` buffers

Replace InputSpan/OutputSpan with generic Buffer associated types constrained to RangeReplaceableContainer across all four streaming protocols. This sidesteps the limitation that OutputSpan (and other ~Escapable types) cannot be used in async contexts today, while still allowing conforming types to choose a buffer representation optimized for their use case (e.g. UniqueArray for heap-backed storage, or a future stack-allocated container for embedded).

Switch callee-owned protocols from consuming to inout closure semantics, rename forEachChunk to forEachBuffer, and move forEach/collect to future directions in the proposal.

Adds test coverage for AsyncWriter, CallerAsyncReader, and EitherError. Updates all documentation to reflect the new API surface.

@FranzBusch FranzBusch requested a review from Catfish-Man May 13, 2026 13:41
Copy link
Copy Markdown
Member

@Catfish-Man Catfish-Man left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few minor comments, but basically looks great. Thanks for doing this!

upTo limit: Int,
body: (consuming InputSpan<ReadElement>) async throws(Failure) -> Result
) async throws(EitherError<ReadFailure, Failure>) -> Result {
) async throws(EitherError<EitherError<ReadFailure, AsyncReaderLeftOverElementsError>, Failure>) -> Result {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EitherError<EitherError<…>> wow that's a painful type. Do we need a variadic error instead?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you can define a variadic enum that generates cases per pack element. I agree this is awful...

maximumCount: limit - buffer.count
) { (span: consuming InputSpan<ReadElement>) in
guard span.count > 0 else {
try await self.read { (buffer: inout Buffer) throws(AsyncReaderLeftOverElementsError) -> Void in
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My kingdom for better typed throws inference in closures

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC the compiler can do this but we disabled it since it is source breaking. We really should have an upcoming feature that infers this by default again

Comment thread Sources/AsyncStreaming/NNNN-async-streaming.md Outdated

> If you are not sure, pick callee-owned (`AsyncReader`) for read streams and
> caller-owned (`AsyncWriter`) for write streams.
> caller-owned (`CallerAsyncWriter`) for write streams.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm. Weren't we going to give the "good" names to the recommended types?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is still like the current state. I wanna revisit the names altogether. I have some better ideas potentially but for now keeping the two sides aligned is easier to think about.

extension AsyncReader where Self: ~Copyable, Self: ~Escapable {
/// Iterates over all chunks, executing `body` for each buffer until the
/// stream ends.
public consuming func forEachBuffer<Failure: Error>(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to have the proposal reflect the current state or speculate about the possible syntax sugar?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My intention was the current state for now and keep it updated with changes.

@Test
@available(macOS 26.2, iOS 26.2, watchOS 26.2, tvOS 26.2, visionOS 26.2, *)
func collectIntoOutputSpan() async {
// TODO: Cannot test this yet since we can't get `InputSpan`s available in async contexts
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice to see this go :)

@FranzBusch FranzBusch force-pushed the fb-async-streaming-container branch from adaf220 to e739f9b Compare May 14, 2026 20:17
…tainer` buffers

Replace `InputSpan`/`OutputSpan` with generic `Buffer` associated types constrained to `RangeReplaceableContainer` across all four streaming protocols. This sidesteps the limitation that `OutputSpan` (and other `~Escapable` types) cannot be used in async contexts today, while still allowing conforming types to choose a buffer representation optimized for their use case (e.g. `UniqueArray` for heap-backed storage, or a future stack-allocated container for embedded).

Switch callee-owned protocols from `consuming` to `inout` closure semantics, rename `forEachChunk` to `forEachBuffer`, and move `forEach`/`collect` to future directions in the proposal.

Adds test coverage for `AsyncWriter`, `CallerAsyncReader`, and `EitherError`. Updates all documentation to reflect the new API surface.
@FranzBusch FranzBusch force-pushed the fb-async-streaming-container branch from e739f9b to f7a915e Compare May 14, 2026 21:23
@FranzBusch FranzBusch merged commit d0b4a06 into apple:main May 14, 2026
30 of 31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants