Thanks, I see how we got here now.
Seems like we have two issues then:
Issue (1): "IteratingIterableProtocol" name is awkward
It's not the authors' fault at all—they did the best they could in the current situation. It makes sense when you consider how we got here:
Sequence.Iterator's protocol is IteratorProtocol.
- So
Iterable.Iterator's protocol can't be IteratorProtocol.
- But to be consistent, it should have
IteratorProtocol in the name.
SpanIterator is already in use elsewhere.
- Hence
IterableIteratingProtocol.
I would however +1 to @Nobody1707's BorrowingIterator over IterableIterator. Drops the Protocol suffix (which is really just a leftover from the old Generator→Iterator rename, not something a new protocol needs to inherit), and it names the iterator by its ownership relationship.
That said, I think this part is actually far less consequential than issue 2.
Issue (2): Putting associatedtype Iterator on both Sequence and Iterable would cause a conflict
The authors had to call Iterable's associated type "IteratingIterator" because of the underlying limitation that Swift currently lacks a way to disambiguate two associated types that share a type name on a type that conforms to those two protocols. There'd be no way to disambiguate Sequence's Iterator from Iterable's Iterator on a type that conforms to both.
Suggested Solution to Issue 2
Adding support for a new syntax feature might requires a new pitch/proposal cycle on its own, and I don't know what the Apple's timeline looks like (and am not suggesting this necessarily delay their feature). However there is precedent for including syntax features with proposals like this, though it's not common.
Caveats aside, ideally, Swift could be updated to support disambiguating associated types of the same name:
/// Declaration site:
struct FileHandleBag: Sequence, Iterable {
typealias Sequence.Iterator = FileHandler
typealias Iterable.Iterator = SpanReader
}
/// Call site:
func processFiles<T: Sequence & Iterable>(handler: T) {
var a = handler.makeIterator() // fine — method name disambiguates
let fileListIterator: (handler as Sequence).Iterator = ...
let byteSpanIterator: (handler as Iterable).Iterator = ...
}
If this proposal already considered this option, please forgive me, as I must have misread.
Discussion of this Solution
Adding disambiguation for associated types would allow Iterable to simply use the associated type Iterator, making it more consistent with Sequence. It would also alleviate the need need for the IteratingIteratorAdapter by allowing callers to disambiguate at the call site even if the associated type names are identical.
More importantly, this would also solve the issue permanently for all future Swift authors, unless it's technically impossible (@xwu chime in?).
Seems better overall for the evolution of the language as a whole, and what kind of precedents changes like this set. Do we add confusing names like IteratingIterableProtocol into the standard library or do we make some tweaks to keep Swift elegant?
At the conformance, as mentioned above, that'd look like qualifying which protocol each witness satisfies:
struct FileHandleBag: Sequence, Iterable {
typealias Sequence.Iterator = FileHandler
typealias Iterable.Iterator = SpanReader
}
Protocol.AssociatedType on the left of a typealias reads naturally and has a clear meaning — you're already declaring witnesses, this just says which protocol's requirement you're satisfying when the names collide.
Hopefully it's not as hard of a change to implement as it might seem:
The witnesses are already separate at the ABI level. Associated types are stored per-conformance, so Sequence's witness table has its own Iterator slot and Iterable's has its own. Witnessing both with different types needs no new runtime machinery; this is a source-level name-resolution problem, not an ABI one. (Happy to be corrected by someone who knows the mangling better than me.)
The source impact is scoped to genuinely-ambiguous sites only. Existing functions written where T: Sequence that name T.Iterator are unaffected and continue compiling untouched. One protocol supplies Iterator, no ambiguity. An error only fires when both Iterator-supplying protocols are named in the same bound, i.e. code that's opted into both after Iterable releases. Even then, it's a compile error, not a silent gotcah, and since the compiler knows the candidates, it can offer a fix-it that inserts the qualifier.
**EG: Someone who later widens a constraint to T: Sequence & Iterable will need to update existing bare T.Iterator uses wherever the compiler can't disambiguate based on member names. But that's local to a function they're already editing, mechanical, and fix-it-able — a one-time migration for adopters, versus the IterableIterator name everyone reads and writes forever. SE-0503 and SE-0346 both establish that carefully-scoped source breaks are acceptable when there's a migration story, and this one's about as mechanical as it gets.
The capability is the point, not the syntax I suggested. SE-0346 considered explicit associated-type signifiers at use sites (the Collection<.Element == String> form), found them parseable, and explicitly left the door open to adding them later, so there's precedent that this shape of syntax is acceptable. Whether the disambiguator ends up (T as Iterable).Iterator or something else is a side question.
Caveats
I kept the ABI claim hedged ("happy to be corrected") because I am just a humble Swift developer. In all likelihood everything I just suggested was already thought about and ruled out as requiring 5 months of compiler refactoring, or something.
Thanks if you actually read all that.