> 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