Thank you for your thoughtful feedback. I will attempt to consolidate some answers/updates/comments into one reply. I will also update the proposal at the link above to include this content shortly.
This was intended to be more like
+ function. For now, to simplify the proposal, I've removed them.
We think cancel should be synchronous, especially since the compiler will call it for you (see below for more on this). This is discussed in Alternatives Considered as well.
We will also update the proposal to suggest that deinit should be equivalent to cancel if it exists on the iterator.
Awaiting Many Things
A future enhancement (either in the standard library or a higher level library) could be to introduce a kind of buffering
AsyncSequence. As an initializer argument it could be told how many things it should attempt to eagerly fetch and act as a kind of signal smoother.
We've investigated the plans for the overhead of the
await keyword itself (not including any user code) and we believe it is low enough to not be an issue on its own.
AsyncGenerator but would prefer to leave the
Generator name for future language enhancements.
Stream is a type in Foundation, so we did not reuse it here to avoid confusion.
We considered a shorter syntax of
await...in. However, since the behavior here is fundamentally a loop, we feel it is important to use the existing
for keyword as a strong signal of intent to readers of the code. Although there are a lot of keywords, each one has purpose and meaning to readers of the code.
Add APIs to iterator instead of sequence
We went off and explored this idea in depth. It is certainly a compelling argument. However, we ultimately came back around to the decision that consistency with the existing
Sequence APIs is the tradeoff decision we would like to propose.
We discussed applying the fundamental API (
reduce, etc.) to the
AsyncIterator protocol instead of
AsyncSequence. There has been a long-standing (albeit deliberate) ambiguity in the
Sequence API -- is it supposed to be single-pass or multi-pass? This new kind of iterator & sequence could provide an opportunity to define this more concretely.
While it is tempting to use this new API to right past wrongs, we maintain that the high level goal of consistency with existing Swift concepts is more important.
for...in cannot be used on an
Iterator -- only a
Sequence. If we chose to make
for...in as described here, that leaves us with the choice of either introducing an inconsistency between
Iterator or giving up on the familiar
for...in syntax. Even if we decided to add
Iterator, it would still be inconsistent because we would be required to leave
for...in syntax on the existing
Another point in favor of consistency is that implementing an
AsyncSequence should feel familiar to anyone who knows how to implement a
We are hoping for widespread adoption of the protocol in API which would normally have instead used a
Notification, informational delegate pattern, or multi-callback closure argument. In many of these cases we feel like the API should return the 'factory type' (an
AsyncSequence) so that it can be iterated again. It will still be up to the caller to be aware of any underlying cost of performing that operation, as with iteration of any
Move-only iterator and removing Cancel
We discussed waiting to introduce this feature until move-only types are available in the future. This is a tradeoff in which we look to the Core Team for advice, but the authors believe the benefit of having this functionality now has the edge. It will likely be the case that move-only types will bring changes to other
Iterator types when it arrives in any case.
Prototyping of the patch does not seem to indicate undue complexity in the compiler implementation. In fact, it appears that the existing ideas around
defer actually match this concept cleanly. I've updated the proposal to show how this could work.
We have included a
__consuming attribute on the
cancel function, which should allow move-only iterators to exist in the future.