Looks good to me!

-Thorsten



> Am 12.06.2016 um 15:01 schrieb Антон Жилин via swift-evolution 
> <[email protected]>:
> 
> I've prepared a proper draft:
> 
> https://github.com/Anton3/swift-evolution/blob/generic-protocols/proposals/NNNN-generic-protocols.md
>  
> <https://github.com/Anton3/swift-evolution/blob/generic-protocols/proposals/NNNN-generic-protocols.md>
> 
> - Anton
> 
> 2016-06-10 17:18 GMT+03:00 Brent Royal-Gordon <[email protected] 
> <mailto:[email protected]>>:
> > FWIW they're marked as 'unlikely' here: 
> > https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generic-protocols
> >  
> > <https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generic-protocols>
> >
> > It would probably be useful to have counterarguments against the points 
> > raised in that document if you want to prepare a proposal.
> 
> Here's my counterargument.
> 
>         * * *
> 
> Firstly, I think they're underestimating the feature's utility. Generic 
> protocols (real generic protocols, not Sequence<Element>) are already needed 
> to make several existing or likely future features work better. For instance:
> 
> * Pattern matching
> 
> Currently, if you want to customize your type's behavior in a `switch` 
> statement, you do it in an ad hoc, almost Objective-C-like way: You define a 
> free `~=` operator and the compiler resolves the overloads to magically find 
> and use it. There is no way to constrain a generic parameter to "only types 
> that can pattern match against type X", which seems like a pretty useful 
> thing to offer. For instance, in the past people have suggested some sort of 
> expression-based switch alternative. The lack of a pattern matching protocol 
> makes this impossible to implement in either the standard library or your own 
> code.
> 
> If we had generic protocols, we could define a protocol for this matching 
> operator and fix the issue:
> 
>         protocol Matchable<MatchingValue> {
>                 func ~= (pattern: Self, value: MatchingValue) -> Bool
>         }
> 
>         protocol Equatable: Matchable<Self> {
>                 func == (lhs: Self, rhs: Self) -> Bool
>         }
>         func ~= <T: Equatable>(lhs: T, rhs: T) -> Bool {
>                 return lhs == rhs
>         }
> 
>         extension Range: Equatable, Matchable<Bound> {}
>         func ~= <Bound: Comparable>(pattern: Range<Bound>, value: Bound) -> 
> Bool {
>                 return pattern.lowerBound <= value && value < 
> pattern.upperBound
>         }
> 
> Then you could write, for instance, a PatternDictionary which took patterns 
> instead of keys and, when subscripted, matched the key against each pattern 
> until it found a matching one, then returned the corresponding value.
> 
> * String interpolation
> 
> Currently, StringInterpolationConvertible only offers an 
> `init<T>(stringInterpolationSegment: T)` initializer. That means you 
> absolutely *must* permit any type to be interpolated into your type's string 
> literals. This blocks certain important use cases, like a `LocalizedString` 
> type which requires all strings it interacts with to pass through a 
> localization API, from being statically checked. It also would normally 
> require any type-specific behavior to be performed through runtime tests, but 
> just as in `~=`, the Swift compiler applies compile-time magic to escape this 
> restriction—you can write an `init(stringInterpolationSegment:)` with a 
> concrete type, and that will be preferred over the generic one.
> 
> In theory, it should be possible in current Swift to redefine 
> StringInterpolationConvertible to allow you to restrict the interpolatable 
> values by doing something like this:
> 
>         protocol StringInterpolationConvertible {
>                 associatedtype Interpolatable = Any
>                 init(stringInterpolation: Self...)
>                 init(stringInterpolationSegment expr: Interpolatable)
>         }
> 
> (This is no longer generic because I believe Interpolatable would have to be 
> somehow constrained to only protocol types to make that work. But you get the 
> idea.)
> 
> However, in many uses, developers will want to support interpolation of many 
> custom types which do not share a common supertype. For instance, 
> LocalizedString might want to support interpolation of any LocalizedString, 
> Date, Integer, or FloatingPoint number. However, since Integer and 
> FloatingPoint are protocols, you cannot use an extension to make them 
> retroactively conform to a common protocol with LocalizedString.
> 
> With generic protocols, we could define StringInterpolationConvertible like 
> this:
> 
>         protocol StringInterpolationConvertible<Interpolatable> {
>                 init(stringInterpolation: Self...)
>                 init(stringInterpolationSegment expr: Interpolatable)
>         }
> 
> And then say:
> 
>         extension LocalizedString: 
> StringInterpolationConvertible<LocalizedString>, 
> StringInterpolationConvertible<Integer>, 
> StringInterpolationConvertible<FloatingPoint> {
>                 init(stringInterpolationSegment expr: LocalizedString) {
>                         self.init()
>                         self.components = expr.components
>                 }
>                 init(stringInterpolationSegment expr: Integer) {
>                         self.init()
>                         self.components.append(.integer(expr))
>                 }
>                 init(stringInterpolationSegment expr: FloatingPoint) {
>                         self.components.append(.floatingPoint(expr))
>                 }
>                 init(stringInterpolation strings: LocalizedString...) {
>                         self.init()
>                         self.components = strings.map { $0.components 
> }.reduce([], combine: +)
>                 }
>         }
> 
> This example shows an interesting wrinkle: A generic protocol may have 
> requirements which don't use any of the generic types, so that each of the 
> multiple conformances will require members with identical signatures. When 
> this happens, Swift must only allow the member to be implemented once, with 
> that implementation being shared among all conformances.
> 
> * Subtype-supertype relationships
> 
> Though not currently implemented, there are long-term plans to permit at 
> least value types to form subtype-supertype relationships with each other. A 
> protocol would be a sensible way to express this behavior:
> 
>         protocol Upcastable {
>                 associatedtype Supertype
> 
>                 init?(attemptingCastFrom value: Supertype)
>                 func casting() -> Supertype
>         }
> 
> However, this would require a type to have only one supertype, which isn't 
> necessarily appropriate. For instance, we might want a UInt8 to be a subtype 
> of both Int16 and UInt16. For that to work, Upcastable would have to be 
> generic:
> 
>         protocol Upcastable<Supertype> {
>                 init?(attemptingCastFrom value: Supertype)
>                 func casting() -> Supertype
>         }
> 
>         extension UInt8: Upcastable<Int16>, Upcastable<UInt16> { … }
> 
> Without generic protocols, the only way to offer sufficiently flexible 
> subtyping is to offer it as a one-off, ad-hoc feature with special syntax.
> 
>         * * *
> 
> Secondly, I think the concerns about people trying to use Sequence as a 
> generic protocol aren't that big a deal. To put it simply: Sequence is *not* 
> a generic protocol. The Swift team controls the definition of Sequence, and 
> we define it to not be generic. If people complain, we explain that generic 
> protocols don't actually do the right thing for this and that they should use 
> existentials instead. We put it in a FAQ. It's just not that big a deal.
> 
> The real concern is not that people will try to use Sequence as a generic 
> protocol, but that they will try to inappropriately make their own protocols 
> generic. I see this as a more minor issue, but if we're worried about it, we 
> can address it by changing the mental model to one which doesn't make it look 
> like a generics feature.
> 
> Basically, rather than thinking of this feature as "generic protocols", it 
> could instead be thought of as "associated type overloading": a particular 
> associated type can be overloaded, and you can use a `where` clause to select 
> a particular overload. This would have a different syntax but handle the same 
> use cases.
> 
> For instance, rather than saying this:
> 
>         protocol Matchable<MatchingValue> {
>                 func ~= (pattern: Self, value: MatchingValue) -> Bool
>         }
> 
>         protocol Equatable: Matchable<Self> {
>                 func == (lhs: Self, rhs: Self) -> Bool
>         }
>         func ~= <T: Equatable>(lhs: T, rhs: T) -> Bool {
>                 return lhs == rhs
>         }
> 
>         extension Range: Equatable, Matchable<Bound> {}
>         func ~= <Bound: Comparable>(pattern: Range<Bound>, value: Bound) -> 
> Bool {
>                 return pattern.lowerBound <= value && value < 
> pattern.upperBound
>         }
> 
>         struct PatternDictionary<Matching, Value>: 
> DictionaryLiteralConvertible {
>                 typealias Key = Matchable<Matching>
>                 typealias Value = OutValue
> 
>                 var patterns: DictionaryLiteral<Key, Value>
>                 init(dictionaryLiteral pairs: (Key, Value)...) { patterns = 
> DictionaryLiteral(pairs) }
> 
>                 subscript(matchingValue: Matching) -> Value? {
>                         for (pattern, value) in patterns {
>                                 if pattern ~= matchingValue {
>                                         return value
>                                 }
>                         }
>                         return nil
>                 }
>         }
> 
> You could instead say:
> 
>         protocol Matchable {
>                 @overloadable associatedtype MatchingValue
>                 func ~= (pattern: Self, value: MatchingValue) -> Bool
>         }
> 
>         protocol Equatable: Matchable where MatchingValue |= Self {
>                 func == (lhs: Self, rhs: Self) -> Bool
>         }
>         func ~= <T: Equatable>(lhs: T, rhs: T) -> Bool {
>                 return lhs == rhs
>         }
> 
>         extension Range: Equatable, Matchable {
>                 typealias MatchingValue |= Bound
>         }
>         func ~= <Bound: Comparable>(pattern: Range<Bound>, value: Bound) -> 
> Bool {
>                 return pattern.lowerBound <= value && value < 
> pattern.upperBound
>         }
> 
>         struct PatternDictionary<Matching, Value>: 
> DictionaryLiteralConvertible {
>                 typealias Key = Any<Matchable where .MatchingValue & Matching>
>                 typealias Value = Value
> 
>                 var patterns: DictionaryLiteral<Key, Value>
>                 init(dictionaryLiteral pairs: (Key, Value)...) { patterns = 
> DictionaryLiteral(pairs) }
> 
>                 subscript(matchingValue: Matching) -> Value? {
>                         for (pattern, value) in patterns {
>                                 if pattern ~= matchingValue {
>                                         return value
>                                 }
>                         }
>                         return nil
>                 }
>         }
> 
> (Is `MatchingValue |= Bound` a union type feature? I'm not sure. It does have 
> the syntax of one, but there's a separate overload for each type, so I don't 
> think it really acts like one.)
> 
> This is very nearly the same feature, but presented with different 
> syntax—effectively with a different metaphor. That should prevent it from 
> being abused the way the core team fears it will be.
> 
> (One difference is that this version permits "vacuous" conformances: in 
> theory, there's no reason you couldn't conform to a protocol with an 
> `@overloadable associatedtype` and define zero types. On the other hand, 
> that's not necessarily *wrong*, and might even be useful in some cases.)
> 
> --
> Brent Royal-Gordon
> Architechies
> 
> 
> _______________________________________________
> swift-evolution mailing list
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-evolution

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

Reply via email to