Generic Protocols using Templates

I understand that this is one of those "easier said than done" issues, but I think in a truly Object-Oriented programming language, protocols should follow in the footsteps of functions, structures, and classes by using templates.

Templated functions, structures, and classes seem to be the definition of functions that take in a couple of types in brackets <Arg1,Arg2> and produce a specialized function, structure, or class. When it comes to protocols I feel like using associatedtypes is taking the path of "bend to what's easiest for writing the compiler thing" rather than the best route in pursuing a language that can express all kind of objects. I worry it will lead to problems in the future and a bunch of crappy workarounds.

There is probably a better example I could make, but the gist of my proposal is instead of writing this definition:

protocol IteratorProtocol {

associatedtype Element

mutating func next() -> Element?

}

writing this one:

protocol IteratorProtocol {

mutating func next() -> Element?

}
.

I also think that there should be a MutableReference type in Swift, especially when it comes to something like an iterator, but that's not the hill I want to die on.

Could someone explain to me why we can't do something like this? I'm sure the implementers of Swift did not want to use the associatedtype approach when they came up with the language. The main use case would be instantiating objects of type Collection the same way we can instantiate objects of type ARSessionDelegate.

I’m pretty sure they did, actually.

You can learn more about the generics design from documentation in the compiler repository. For example, Generics.rst and GenericsManifesto.md (the part about generic protocols might especially interest you).

There’s quite a lot of documentation on this decision if you look for it.

6 Likes

It should be protocol IteratorProtocol<Element> {, shouldn't it?
As for the pitch:
I'd welcome the option to get rid of those AnyFoo-types, and write Sequence<Int> instead of SequenceOfInt — and I know there are others who would like to have that, too.
But my impression is that core considers associated types to be superior, and that (I don't know why) Swift should not support both concepts :frowning:

Swift does not have templated functions, structures, or classes. It has generic functions, structures, and classes, which are superficially similar, but actually pretty different in their semantics. The distinction between the two things is not just in what they're called.

This article is generally a pretty good summary of the high-level differences between generics and templates in various languages. It's rather unfortunate that languages seem to have broadly settled on using angle brackets for a wide array of similar-looking but semantically-different concepts, but also no one seems to have a clearly better idea, so here we are.

13 Likes

You're conflating two different things here, and as a result misrepresenting the position you're attributing to the core team.

Sequence<Int> is a potential syntax for a type-erased sequence of Int, a possible compiler-supported replacement for the hand-rolled struct AnySequence<Int>. But that does not make it an alternative for associated types, because they are a related but different concept. Alternatively, Swift could potentially have been designed to stick the associated types in angled brackets after the protocol name, instead of using associated types, but that is very different to type erasure.

Protocols without associated types are really two things: they are a way to express a constraint on a type, for use with generics, and they are a type-erased box into which you can put any type that conforms. Protocols with associated types don't support this second feature, because there are complications that need resolving e.g. how would you handle the method signatures that used the associated types.

Bear in mind, Element is not the only associated type on Sequence. There is also the type of the Iterator – the type that allows you to iterate the sequence. You might say, well, ok stick that in the angle brackets too. But that would work against the type erasure, because now Sequence<Int, [Int].Iterator> and Sequence<Int, Range<Int>.Iterator> are different types.

That's not how AnySequence works – it type-erases iterators into AnyIterator. You'd probably want Sequence<Int> to do the same thing – but this doesn't just fall out naturally. There are language design decisions here to be considered to allow the author of the Sequence protocol to specify what Sequence<Int> means, and how to handle the other associated types on the protocol.

15 Likes

As far as I remember the last known direction of the syntax the core team presented wasn‘t Sequence<Int> but rather Sequence<.Element == Int>. The former is too much convenience for the feature you describe and confusing with non existing generic protocols (similar to generic traits in Rust; Disclaimer I don‘t know Rust, but I read that part of the feature documentation which to me appeared exactly like generic protocols, regardless of their complexity or language user confusion the core team always mentions).

Many if not most of the problems a user would actually like to use generic protocols instead of PATs could be solved with some additional boilerplate through generic proxy types and generic associated type. The latter was accepted into the generic manifesto but this feature was never really discussed on SE yet.

The core team as a group hasn’t presented any official direction on this – these are all only sketches of ways we could go, none of which are an officially endorsed plan.

2 Likes

I didn't say other: I explicitly wrote about "both concepts", implying the difference.
Obviously, one of those is implemented in Swift, the other is not… why should that be if not because the chosen concept is superior in some way?

That is not what I have been thinking of: It would be a stripped-down variant of Sequence, but a protocol of its own. It would not have any requirements that need more than Element, and it would not be a PAT.
I can create such a protocol easily (that is SequenceOfInt), but I have to iterate that process for every kind of element. I don't think it's feasible to generate such "specialized" protocols automatically without huge changes of the language, but generics would still save a lot repetition.