> 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
