[swift-evolution] [swift-evolution-announce] [Review] SE-0089: Replace protocol<P1, P2> syntax with Any<P1, P2>

Douglas Gregor dgregor at apple.com
Fri Jun 17 09:11:47 CDT 2016


> On Jun 16, 2016, at 9:46 AM, Thorsten Seitz via swift-evolution <swift-evolution at swift.org> wrote:
> 
>> 
>> Am 16.06.2016 um 17:36 schrieb Paul Cantrell <cantrell at pobox.com <mailto:cantrell at pobox.com>>:
>> 
>>> On Jun 16, 2016, at 8:29 AM, Thorsten Seitz via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> 
>>> Protocols are a mechanism for deriving types from each other whereas generics are a way to parameterize types. My point was that Swift's other way to parameterize types, namely by associated types, is very similar to generics with wildcards when looking a the existentials of such protocols.
>> 
>> This has been a point of confusion for me as well. I keep hearing that associated types are different from generic protocols, but this seems like a distinction without a difference.
>> 
>> Suppose Swift allowed generic protocols. How would a hypothetical Collection<Foo> be different in practice from the proposed existential Any<Collection where .Element == Foo>?
>> 
>> Yes, in the realm of type theory and compiler internals they might represented differently, sure. But in practice, in terms of what code can actually do? I know of only two differences:
>> 
>> 1. A type can only conform to any given protocol with one set of type parameters. (Nothing can be both Collection<Foo> and Collection<Bar>.)
>> 
>> 2. When a type conforms to Collection, it declares “associatedtype Foo” instead of “: Collection<Foo>”, and Foo can be inferred by the compiler in some circumstances. That’s handy, but it’s a syntactic difference.
> 
> That syntactic difference is *very* handy IMO for the following reason: with generics I have to repeat all types over and over again which gets ugly when I have levels of nesting where type parameters are constrained by other generics, which requires adding their parameters to the parameter list. Essentially the nested parameters have to be fully flattened because each type parameter has to be explicitly specified.
> 
> I’ll try to show that with a simplified example:
> 
> // with associated types
> 
> protocol Edge {
>     associatedtype VertexType
> 
>     var source: VertexType { get }
>     var target: VertexType { get }
> }
> 
> protocol Graph {
>     associatedtype EdgeType : Edge
>     
>     var vertices: [EdgeType.VertexType] { get }
>     var edges: [EdgeType] { get }
>     
>     func outEdges(vertex: EdgeType.VertexType) -> [EdgeType]
> }
> 
> protocol GraphIterator {
>     associatedtype GraphType : Graph
>     
>     var graph: GraphType { get }
>     
>     var startVertex: GraphType.VertexType { get }
>     
>     func enter(vertex: GraphType.VertexType)
>     func propagate(along edge: GraphType.EdgeType)
>     func finish(vertex: GraphType.VertexType)
> }
> 
> 
> // with generics
> 
> class Edge<VertexType> {
>     var source: VertexType
>     var target: VertexType
> }
> 
> class Graph<VertexType, EdgeType: Edge<VertexType>> {
>     
>     var vertices: [VertexType]
>     var edges: [EdgeType]
>     
>     func outEdges(vertex: VertexType) -> [EdgeType]
>     
> }
> 
> class GraphIterator<VertexType, EdgeType: Edge<VertexType>, GraphType: Graph<VertexType, EdgeType>> {
>     
>     var graph: GraphType
>     
>     var startVertex: VertexType
>     
>     func enter(vertex: VertexType)
>     func propagate(along edge: EdgeType)
>     func finish(vertex: VertexType)
> }
> 
> Note, how the parameter list for GraphIterator exploded, because I had to list each level of nested types down to the VertexType, whereas
> in the associated types example the GraphIterator simply declares an associated type conforming to the topmost type of my nesting, the Graph.
> 
> 
>> 
>> Is there a deeper difference I’m missing?
> 
> Maybe Dave can chime in here?

You can recover an associated type (say, X.Element) by just having the type “X”, but this is not true for a type parameter. That doesn’t matter when you have (or want to specify) that type… for example, your comment that Collection<Foo> and Any<Collection where .Element == Foo> would basically be the same thing.

However, with generalized/enhanced existentials you would be able to write

	var heterogeneousArrayOfCollections: [Collection]
	heterogeneousArrayOfCollections.append([1, 2 3])
	heterogeneousArrayOfCollections.append([“Hello” : 1, “Swift” : 2])

You can’t do that with generic protocols, because there is no common element type:

	var heterogeneousArrayOfCollections: [Collection<???>]

One could perhaps try to rely on subtyping of collections for this specific case

	var heterogeneousArrayOfCollections: [Collection<Any>]
	heterogeneousArrayOfCollections.append([1, 2 3])
	heterogeneousArrayOfCollections.append([“Hello” : 1, “Swift” : 2])

but that’s not something we have now and doesn’t really generalize well in Swift.

- Doug


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160617/4103ddd0/attachment.html>


More information about the swift-evolution mailing list