> Am 17.06.2016 um 16:11 schrieb Douglas Gregor <[email protected]>:
> 
> 
>> On Jun 16, 2016, at 9:46 AM, Thorsten Seitz via swift-evolution 
>> <[email protected] <mailto:[email protected]>> wrote:
>> 
>>> 
>>> Am 16.06.2016 um 17:36 schrieb Paul Cantrell <[email protected] 
>>> <mailto:[email protected]>>:
>>> 
>>>> On Jun 16, 2016, at 8:29 AM, Thorsten Seitz via swift-evolution 
>>>> <[email protected] <mailto:[email protected]>> 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
> 
> 

Wildcards would work, too, e.g. in Java it looks like follows:
List<Collection<?>> heterogeneousListOfCollections = new ArrayList<>();

List<Integer> intList = new ArrayList<>();
Collections.addAll(intList, 1, 2, 3);

List<String> stringList = new ArrayList<>();
Collections.addAll(stringList, "Hello", "Swift");

heterogeneousListOfCollections.add(intList);
heterogeneousListOfCollections.add(stringList);


With Austin’s proposal we could even recover the associated type of a concrete 
instance as a path dependent type, making this well typed, for example:

heterogeneousArrayOfCollections.map { xs in xs.min().map { minElement in 
xs.index(of: minElement) } }   // minElement has type xs.Iterator.Element

-Thorsten
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to