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

Maximilian Hünenberger m.huenenberger at me.com
Fri Jan 29 16:30:11 CST 2016



> Am 26.01.2016 um 19:48 schrieb David Waite <david at alkaline-solutions.com>:
> 
> 
>>> On Jan 26, 2016, at 10:32 AM, Maximilian Hünenberger <m.huenenberger at me.com> wrote:
>>> 
>>>> Am 26.01.2016 um 00:27 schrieb David Waite <david at alkaline-solutions.com>:
>>>> 
>>>> // 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 don't want implicit/inferred covariant types.
>> In this case invariant array:
>> 
>> let aArray: Array<where .Element == A> = [1,2,3] 
>> // which is equivalent to
>> let aArray: [A] = [1,2,3] 
> 
> Ahh ok, you are asking for an invariant array of A’s, then. Method calls and properties may have covariant properties (such as assignment allowing any subclass of A), but the representation of Array internally is always of a list of A protocol instances.
> 
> A covariant _constraint_ is instead saying that you are referencing one of many different possible concrete Array types - some array based around internally having a list of A, or an array based around some particular subtype of A. 
> 
> This will generally be restricted further for safety reasons. For example, a subscript assignment would first cast up to A, then have to cast down to some particular subtype of A. That downcast is unsafe and may fail based on the concrete kind of array behind the protocol.
> 
>>>> 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.
>> 
>> "aArray" is not covariant to "intArray" although each element is covariant to any type which conforms to "A”.
> 
> I think we may be working through the communication issue. But basically:
> 
> let aArray: Array<where .Element: A> = [1,2,3] 
> 
> is specifying a type formed by a covariant constraint on Element. This means this could be a concrete array of A’s, or one of Ints, or one of some other implementation of A.
> 
> So lets name them explicitly:
> 
> var col1: CollectionType<where .Element == A>
> let xArray:[Int] = [1,2,3]
> let yArray:[A] = [1,2,3] // an Array of ‘A's
> 
> col1 = xArray // fails
> col1 = yArray // succeeds
> 
> let zxArray:Array<where .Element:A> = xArray // similar to defining a protocol implicitly for arrays of A or of A-subtypes, of which [Int] conforms
> let zyArray:Array<where .Element:A> = yArray // same for [A]
> 
> col1 = zxArray // fails
> col1 = zyArray // still fails
> 
> Even though dynamically the zyArray type is compatible, the compiler is reasoning statically that zxArray and zyArray both could be non-working types, and therefore must be blocked for safety reasons.

That's exactly what I was thinking.

> 
>>> 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? 
>> 
>> I was inspired from the current generic system of functions:
>> 
>> func takeCollection<C: CollectionType where C.Generator.Element == A>(c: C) {}
>> 
>> takeCollection(intArray) // fails
>> takeCollection(aArray)  // works
>> 
>> 
>> Or how would you interpret "CollectionType<where .Element == A>" in contrast to "CollectionType<where .Element: A>”?
> 
> I think we had a confusion issue about whether A was an invariant or covariant constraint in the post I was responding to. I’m assuming now that you actually did mean it to be invariant.

Yes "==" is an invariant constraint. Even though "A" as single type would be covariant to "Int".

> 
>>> protocol<> is there for describing multiple protocols, so I’d go for the 1st.
>> 
>> +1 For using the first
>> 
>> Suggestion:
>> separate declaration of T to make it consistent with current generic syntax
>> 
>> protocol<T where Self: SequenceType<where .Element == T>, Self: Indexable<where .Index == T>>
>> 
>> This example could also be rewritten to:
>> protocol<where Self: SequenceType, Self: Indexable, Self.Element == Self.Index>
> 
> How about 
> 
> protocol<SequenceType, Indexable where .Element == .Index>

Oh my example was probably "too simple". I thought of declaring an additional generic parameter but thinking about it, it would probably not be usable.

- Maximilian
> 
> -DW
> 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160129/0606f6b3/attachment.html>


More information about the swift-evolution mailing list