Things will get better once we have existentials.

> On 29 Jun 2016, at 08:49, Robert Widmann via swift-evolution 
> <[email protected]> wrote:
> 
> Yes, the restriction "sucks", but it is there for a reason.  A protocol is 
> not just a convenient collection of methods and properties you can generalize 
> over, it's a contract.  Each tells you something about what its implementers 
> have to do before they can call themselves 'Equatable' or 'MyProtocol' etc.  
> Let's see what happens at a very high level if we relax this constraint for 
> Equatable as you have written here.
> 
> protocol Equatable {
>   func ==(_: Equatable, _: Equatable) -> Bool
> }
> 
> And now two implementers (implementation abbreviated for brevity)
> 
> extension String: Equatable {}
> extension Int: Equatable {}
> 
> // ...
> 
> Given this definition, the following typecheck
> 
> 1 == 2 // false
> "A" == "A" // true
> 1 == "1" // ?
> "ABC" == 123 // ?
> 
> Being Equatable suddenly must include a component of self-identity.  We have 
> to be able to constrain the implementation to only those Equatable things 
> that look like ourselves.  Thus, Self constraints.  Because 'MyProtocol' is 
> not defining a protocol for things that understand equality, it is defining 
> an equivalence relation over all possible implementations of ==, and that 
> means that anything goes.  
> 
> So you might modify this to use associated types then.  What about an 
> iteration that asks the implementer to specify the type made in the 
> comparison?
> 
> protocol Equatable {
>   associatedtype Comparator
>   func ==(_: Equatable.Comparator, _: Equatable.Comparator) -> Bool
> }
> 
> This also solves nothing.  You can't actually constrain the associated types 
> here with a needed equality constraint.  You can only push the problem down a 
> needless level of abstraction.
> 
> Yes it's a pain to have to use Generics to reify restricted protocols.  Yes 
> it's a pain to give up use of protocol-ified collections.  Yes it's not 
> immediately obvious why these restrictions are in place.  But to drop them 
> would severely dilute the intended semantics and use of protocols that 
> require knowledge of their reifications.  Unfortunately, equality just 
> happens to be one such protocol.
> 
> There are ways around this.  If you have a sealed hierarchy you can write an 
> enum that enumerates all possible implementations and delegates it's 
> equatable conformance out to them.  Often, identity can be found elsewhere in 
> a type.  For example, a hypothetical 'UUIDable' protocol could specify it's 
> implementers produce a String UUID that could be stored in collections 
> instead of UUIDable types themselves.  For most other cases try to 
> re-evaluate.  Why do you need to generalize over this set of types in this 
> way?  Is there some other more generic way of handling this case?
> 
> ~Robert Widmann
> 
> 2016/06/28 23:17、Riley Testut via swift-evolution <[email protected]> 
> のメッセージ:
> 
>> Hello all,
>> 
>> If you’ve been (attempting) protocol-oriented development in your own 
>> projects, I’m sure you’ve come across a particular build error at one point:
>> 
>>> Protocol ‘MyProtocol' can only be used as a generic constraint because it 
>>> has Self or associated type requirements
>> 
>> To be frank, this restriction in the current Swift model sucks, a lot. In 
>> *many* cases, this prevents me from using protocols, and instead I have to 
>> fall back to using concrete types.
>> 
>> Here are a couple examples of using protocols with collections that should 
>> work fine, but simply don’t:
>> 
>> A Set of Types Conforming to Protocol
>> 
>> protocol MyProtocol: Hashable {}
>> 
>> let set = Set<MyProtocol>() // ERROR: Protocol ‘MyProtocol' can only be used 
>> as a generic constraint because it has Self or associated type requirements
>> 
>> When declaring a Set, the generic type of the Set’s contents must conform to 
>> Hashable. Following this, it would appear that you should be able to declare 
>> a Set containing types conforming to a given protocol which in turn conforms 
>> to Hashable. Nope! This also means you can’t have a Set<Hashable> (so no 
>> type-erased Sets for you!). One potential workaround is to use a box type, 
>> but if exposing the set to a user, this is essentially a leaky abstraction.
>> 
>> Finding a Protocol Type Instance in an Array
>> 
>> protocol MyProtocol {}
>> struct MyStruct: MyProtocol {}
>> 
>> var array = [MyProtocol]()
>> array.append(MyStruct())
>> 
>> let index = array.index(of: MyStruct()) // ERROR: Cannot invoke 'index' with 
>> an argument list of type '(of: MyStruct)'
>> 
>> So, we can’t use Set as a collection for our protocol types, let’s use Array 
>> instead! Not so fast: because MyProtocol doesn’t conform to Equatable, we 
>> can’t use the Array.index(of:) function to find it. Easy fix though, just 
>> make MyProtocol conform to Equatable, right?
>> 
>> protocol MyProtocol: Equatable {}
>> struct MyStruct: MyProtocol {}
>> 
>> var array = [MyProtocol]() // ERROR: Protocol ‘MyProtocol' can only be used 
>> as a generic constraint because it has Self or associated type requirements
>> 
>> Nope! Now that it conforms to Equatable, it can no longer be used in Array’s 
>> type declaration. However, there is a (somewhat) workaround for this problem:
>> 
>> protocol MyProtocol {}
>> func ==(lhs: MyProtocol, rhs: MyProtocol) -> Bool { return true }
>> 
>> struct MyStruct: MyProtocol {}
>> 
>> var array = [MyProtocol]()
>> array.append(MyStruct())
>> 
>> let index = array.index(where: { $0 == MyStruct() })
>> 
>> Basically, we can define the == function for MyProtocol, and then instead of 
>> using Array.index(of:), we use Array.index(where:) to manually compare each 
>> item to see if it matches, aka what Array.index(of:) would do for us 
>> normally if we simply could declare MyProtocol as conforming to equatable.
>> 
>> TL;DR
>> Swift really pushes the idea of protocol-oriented programming, and for the 
>> most part this works well. However, due to some underlying restrictions in 
>> the current Swift model, you can’t use protocols in all the same places you 
>> can use concrete types, which sucks. This is especially confusing for 
>> beginners who are trying to use protocols, but get frustrated when it 
>> doesn’t work where they want it to (and don’t understand why), so they fall 
>> back to using concrete types (usually implemented with class inheritance). 
>> For this reason, I think these restrictions need to be fixed ASAP, or else 
>> the Swift language is essentially pushing people away from protocol-oriented 
>> programming.
>> 
>> Riley Testut
>> 
>> _______________________________________________
>> 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
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to