[swift-evolution] Partially Constrained Protocols [Was: [Proposal] Separate protocols and interfaces]

David Waite david at alkaline-solutions.com
Mon Jan 25 17:27:20 CST 2016


> On Jan 25, 2016, at 2:52 PM, Maximilian Hünenberger <m.huenenberger at me.com> wrote:
> 
> I don't think that this would be confusing since `where` indicates that the type is used in a more generic way.
> 
> 
> You've convinced me. I wanted to keep the generic syntax simple but having thought over it your proposal it much better in handling more complex cases without introducing additional syntax.
> Also considering Swifts type inference where types with long names are easier to use.
> 
> 
> A "small" overview of several examples:
> 
> Using the former example of CollectionType which is used as a more concrete type (using a "leading dot" which is the same as `Self.`).
> 
> // These examples are only a demonstration of how you could use collection type
> 
> CollectionType<where .Generator.Element == String, .Index == Int, .SubSequence: Self>
> 
> CollectionType<where .Generator.Element == String, .Index == Int>
> 
> CollectionType<where .Generator.Element == String>
> 
> // if having `Element` as associated type
> CollectionType<where .Element == String>

One possible/fun option for syntax would be *if* Generics changed to have the types labelled. In that case, “where” becomes a keyword to distinguish a partial vs full constraint. 

For instance, Optional<T == BooleanType> would describe a concrete Optional holding some implementation of BooleanType, while Optional<where T:BooleanType> would be a generated protocol to umbrella every Optional that matches (e.g. Optional<T == Bool>, Optional<T == ObjCBool>, …)

Then the syntax could be identical, e.g. RawRepresentable<RawValue == Int> vs RawRepresentable<where RawValue:SignedNumberType>

For protocols with self requirements you would likely force “where” syntax. I can say Equatable<Self == Int>, but the protocol can *only* be implemented by one type in the system, Int. I can’t think of a reason to support this.

> // covariance and invariance
> 
> protocol A {}
> extension Int: A {}
> 
> let intArray: [Int] = [1, 2, 3]
> let aArray: [A] = [1, 2, 3]

probably still:
  let aArray: Array<where .Element: Int> = [1,2,3] 

if you really want covariance. The limitations (detailed in the other email reply) are such that it is better to have clear syntax so people recognize there may be ramifications.

I could however see a way of doing
  let aCovariantArray: Array<where .Element: Int> = [1,2,3] 
  let aArray = aCovaraintArray as [A]

being allowed, just as "intArray as [A]" works today due to internal behavior.

I detailed the ramifications of “is a concrete array of some specific implementation of A” behavior in the other half of my response - you are basically blocking a good deal of mutation behavior due to type safety. The restrictions are such that 

> var col1: CollectionType<where .Element == A>
> col1 = intArray // doesn't work since Int != A
> col1 = aArray // obviously works

The last line actually will fail as well if aArray is covariant on Element being A.

CollectionType “col1” is being constrained by Elements which *are* A, while aArray is a Collection type which is constrained by Elements being A *or* any subtype. if “intArray” doesn’t work directly and isn’t safe to work directly, why would discarding some of its type information by assigning to aArray make a difference? 

> var col2: CollectionType<where .Element: A>
> // both work since `Int` and `A` conform to `A` (currently in Swift 2.2 `A` does not conform to `A`, I don't know if this is a feature)
> col2 = intArray
> col2 = aArray

Yep!

> // with a concrete type using the example above:
> 
> // replace type of `col1` with (all replacements are equivalent)
> `Array<A>` or `Array<where .Element == A>` or `[A]` or `[where .Element == A]`
> 
> // replace type of `col2` with (all replacements are equivalent)
> `Array<where .Element: A>` or `[where .Element: A]`

I think yes, although col1 = aArray will still fail :D

> // to be discussed: using many protocols together with protocol<>
> 
> // declaring a generic type T which is used in two protocols
> protocol<where T, Self: SequenceType<where .Element == T>, Self: Indexable<where .Index == T>>
> 
> // although in this case it can be written as
> SequenceType<where T, .Element == T, Self: Indexable<where .Index == T>>
> 
> Even though the latter one is shorter I'm skeptical about using `Self:` in protocol where clauses since at a first glance it implies that the type is only a `SequenceType`.

protocol<> is there for describing multiple protocols, so I’d go for the 1st.

-DW

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160125/626708ee/attachment.html>


More information about the swift-evolution mailing list