[swift-evolution] [Discussion] Generic protocols

Alexis abeingessner at apple.com
Fri Dec 9 23:21:38 CST 2016


It seems like a lot of you are just trying to make different syntaxes for generic protocols, which I’m pretty sure was never the concern about them? We already have reasonable prior art here from the syntax of generic structs. The problem is that they add significant additional complexity to the language (they effectively add Higher Kinded Types to the language, as protocol conformances become functions over types).

I’m also pretty confident that allowing protocols to be multi-conformed without requiring some specific annotation on them is totally busted. The fact that you can uniquely determine the associated type of a conformance from the type of Self is an important property of the type system.

That is, 

func foo<S: Sequence>(seq: S) -> S.Item? { … }

let x: [Int] = …
let y = foo(x)

Only works because ([Int] as Sequence).Item can be uniquely determined. Otherwise you would require an annotation at the call site to “pick” the conformance (in this case y: Int would probably be sufficient, but you can easily imagine more complex cases).

Because of retroactive modeling, you can’t include a rule that says disambiguation is only necessary if a type actually conforms to multiple times. The other conformances could be in another library! This is why you want generic and associated type parameters to be different — it lets the type system know which types are independent, and which are dependent. 

* Iterator can only be implemented once 
* ConvertibleTo<T> can be implemented many times (one for each type T)
* (Self as ConvertibleTo<Int>).SomeAssociatedType is still uniquely determined



> On Dec 9, 2016, at 2:16 PM, Anton Zhilin via swift-evolution <swift-evolution at swift.org> wrote:
> 
> A fundamental problem is, how do we get rid of associatedtype duplicates?
> 
> A pedantic approach is to add trait operations for conformances:
> 
> protocol Constructible {
>     associatedtype Value
>     init(_ value: Value)
> }
> 
> struct MyStruct {
>     conformance Constructible {
>         rename associatedtype ValueInt = Value
>     }
>     conformance Constructible {
>         rename associatedtype ValueString = Value
>     }
> 
>     typealias ValueInt = Int  // or can be inferred
>     init(_ value: Int)
> 
>     typealias ValueString = String  // or can be inferred
>     init(_ value: String)
> }
> This way, if there is a if there is a conflicting member, which does not use any of the associated types, like func foo(), then we can give it different meanings in different conformances.
> Although this approach is the most clean one from theoreticall point of view, choosing different names for associated types would not look very good in practise.
> 
> One possible solution is to always automatically match associatedtypes, without using typealiases.
> 
> protocol ConstructibleFromBoth {
>     associatedtype First
>     associatedtype Second
>     init(first: First)
>     init(second: Second)
> }
> 
> struct MyStruct : ConstructibleFromBoth {
>     init(first: Int)
>     init(second: Double)
> }
> 
> extension MyStruct {
>     init(first: String)   // now there are 2 distinct conformances
> }
> 
> extension MyStruct {
>     init(second: Float)   // now there are 4 distinct conformances
> }
> It introduces another potentially exponential algorithm for compiler. Although, does it? During a conformance test in some generic function, compiler will need to only find the first match or two.
> Anyway, I guess, people would prefer to state explicitly that a type conforms to multiple versions of a protocol.
> 
> Attempt #3. We can resolve the conflict between associated types, if we delete (if trait sense) confliting associated types from the type. But with extensions, all associated types can be made conflicting. So there needs to be some attribute, marking, which of the associated types we don’t want. It can lie at the place of conformance:
> 
> struct MyStruct { }
> extension MyStruct : @dontCreateTypealiases (Constructible where Value == Int) { ... }
> extension MyStruct : @dontCreateTypealiases (Constructible where Value == String) { ... }
> // MyStruct.Value.self  // error, no such type
> 
> struct NormalConformanceTest : Constructible { init(_ value: Float) }
> NormalConformanceTest.Value.self  //=> Float
> Or we can let constrained protocols syntax carry this attribute by default:
> 
> extension MyStruct : (Constructible where Value == Int) { ... }
> // MyStruct.Value.self  // error, no such type
> 
> struct NormalConformanceTest: Constructible { init(_ value: Float) }
> NormalConformanceTest.Value.self  //=> Float
> The only thing left to solve is generic protocol declaration syntax and protocol specialization syntax. I’d like to present two ways to do this. First, taking ideas from Rust:
> 
> protocol ContainsCollection<Element> {
>     associatedtype CollectionType : Collection where CollectionType.Element == Element
>     func collection() -> CollectionType
> }
> 
> extension String : ContainsCollection<Character>, ContainsCollection<UnicodeScalar>, ContainsCollection<CChar> {
>     func collection() -> CharacterView
>     func collection() -> UnicodeScalarView
>     func collection() -> ContiguousArray<CChar>
> }
> Generic parameters are used to disambiguate between different conformances, and associated types are just matched. Explicit typealias specifiction is prohibited, because conflicts.
> 
> Second, without modifying current protocol declaration syntax:
> 
> protocol ContainsCollection {
>     associatedtype Element
>     associatedtype CollectionType : Collection where CollectionType.Element == Element
>     func collection() -> CollectionType
> }
> 
> extension String : ContainsCollection<Element: Character>, ContainsCollection<Element: UnicodeScalar>, ContainsCollection<Element: CChar> {
>     func collection() -> CharacterView
>     func collection() -> UnicodeScalarView
>     func collection() -> ContiguousArray<CChar>
> }
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20161210/6a310193/attachment.html>


More information about the swift-evolution mailing list