I’m tentatively liking this proposal.
The need to write things like Sequence<String>
is real and urgent. Existing approaches present some of Swift’s nastiest sticking points for those not deeply invested in understanding its type system. There’s a progressive disclosure cliff, as it were, in this area of the language.
I wondered a bit about a “named parameter” syntax like Sequence<Element: String>
, with no change to protocol declaration syntax. This has two advantages: (1) no need to create this new notion of “primary” associated types, and (2) more obvious correspondence between the different associated types within a protocol. It does bug me in the proposal how much protocol Sequence<Element>
looks like a generic protocol, and how much it hides the fact that Element
is in fact exactly the same sort of beast as other associated types with the single exception of this convenience at the point of use.
However, the syntactic correspondence between Sequence<String>
and Array<String>
is compelling for usability reasons. Ultimately, even though it is more concise than Sequence<.Element == String>
, the Sequence<Element: String>
syntax still places knowledge burden about the protocol’s structure and about type system esoterica on library clients instead of library authors. So…yes, Sequence<String>
for usability.
Despite that, I’m a bit uneasy about this new notion of “primary” associated types, and explicitly designating it. Without having thought hard about it at all, I wonder if there isn’t some way to infer from the protocol’s structure itself which types are primary?
For example, consider the declaration of Sequence
:
public protocol Sequence {
associatedtype Element
associatedtype Iterator: IteratorProtocol where Iterator.Element == Element
…
}
Iterator
depends on Element
, but not vice versa. In the (two-node) dependency graph of associated types, Iterator
has incoming edges, but Element
does not.
One could (I think?) simply move the type constraint and get an equivalent protocol:
public protocol Sequence {
associatedtype Element where Iterator.Element == Element
associatedtype Iterator: IteratorProtocol
…
}
But there is note of authorial intention in the decision to make Iterator
’s declaration depend on Element
instead of the reverse. Is that sufficient to infer that Element
is primary?
Even as I type this out, I see some arguments against it: too much magic, too little syntactic correspondence between the protocol’s declaration and Sequence<Foo>
. (Where’d the angle braces come from?) Still, I’d be curious to hear the authors’ own thoughts on the philosophy behind what it means for an associated type to be “primary,” the burden of introducing this additional concept, and in particular why inferring primary-ness is a bad idea. This might help ease my discomfort with introducing yet another entity to Swift’s type system.