> On Aug 16, 2016, at 2:49 PM, Charles Srstka via swift-evolution 
> <[email protected]> wrote:
> 
> MOTIVATION:
> 
> Suppose we have a bunch of peppers, and we’d like to make a function to pick 
> them. We could just take an array, but Swift supports many types of sequence 
> types beyond a simple array, and it would be nice to support those as well, 
> particularly since we have this one client who stores his peppers in a custom 
> sequence type called “Peck”, and we like to prevent him from having to 
> convert to arrays all the time. We can do this with generic functions:
> 
> protocol Pepper {}
> 
> func pick<PepperType:Sequence>(peppers: PepperType) where 
> PepperType.Iterator.Element == Pepper {
>     // pick a peck of peppers
> }
> 
> let peck: [Pepper] = ...
> 
> pick(peppers: peck)
> 
> However, this convenience method falls down as soon as we have a peck of 
> *pickled* peppers:
> 
> struct PickledPepper: Pepper {}
> 
> let peck = [PickledPepper()]
> 
> pick(peppers: peck) // error: Generic parameter ‘PepperType’ could not be 
> inferred
> 
> We can fix that by declaring the generic constraint to take any type that 
> conforms to Pepper, instead of Pepper itself:
> 
> protocol Pepper {}
> 
> struct PickledPepper: Pepper {}
> 
> func pick<PepperType:Sequence>(peppers: PepperType) where 
> PepperType.Iterator.Element: Pepper {
>     // pick a peck of peppers
> }
> 
> let peck = [PickledPepper()]
> 
> pick(peppers: peck) // works :-)
> 
> However, this now fails if we try to pass in a collection of items still 
> typed as Peppers:
> 
> let peck: [Pepper] = [PickledPepper()]
> 
> pick(peppers: peck) // error: Generic parameter ‘PepperType’ could not be 
> inferred
> 
> The workaround is to declare the convenience method twice:
> 
> func pick<PepperType:Sequence>(peppers: PepperType) where 
> PepperType.Iterator.Element == Pepper {
>     // pick a peck of peppers
> }
> 
> func pick<PepperType:Sequence>(peppers: PepperType) where 
> PepperType.Iterator.Element: Pepper {
>     // do the same exact thing!
> }
> 
> This leads to a lot of copy-paste code, the non-ideal nature of which should 
> be clear. Unfortunately, when this has come up on the list in the past, it 
> has been mentioned that there are some cases where an existential of a 
> protocol does not conform to the protocol itself, so it is impossible to make 
> : always match items that are typed to the protocol.
> 
> PROPOSED SOLUTION:
> 
> I propose for Swift 4 a new operator, :==, which would match not only a 
> protocol, but any type that conforms to the protocol, like so:
> 
> func pick<PepperType:Sequence>(peppers: PepperType) where 
> PepperType.Iterator.Element :== Pepper {
>     // promptly pick a peck of plain or possibly pickled peppers
> }
> 
> let peckOfPeppers: [Pepper] = [PickledPepper()]
> pick(peppers: peckOfPeppers)
> 
> let peckOfPickledPeppers = [PickledPepper()]
> pick(peppers: peckOfPickledPeppers)
> 
> DETAILED DESIGN:
> 
> 1. We introduce a new operator :== which works in generic and associated type 
> constraints.
> 
> 2. The new operator matches anything that == would match.
> 
> 3. The new operator also matches anything that : would match.
> 
> 4. If we are in a case where either : or == cannot apply to the protocol on 
> the right of the :== operator, throw an error.
> 
> ALTERNATIVES CONSIDERED:
> 
> Put down our peck of pickled peppers picking procedure, then repeat our peck 
> of pickled peppers picking procedure, permuted to preserve the potentiality 
> of protocol passing. Pah.

This only makes sense as a constraint if P is a model of P, in which case we 
should just accept 'P: P'. You'd only need ':' at that point.

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

Reply via email to