[swift-evolution] protocol can only be used as a generic constraint because it has Self or associated type requirements

Joe Groff jgroff at apple.com
Mon Dec 14 11:45:55 CST 2015


> On Dec 13, 2015, at 9:56 PM, Dave Abrahams via swift-evolution <swift-evolution at swift.org> wrote:
> 
> 
>> On Dec 13, 2015, at 3:55 PM, Marc Knaup via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> 
>> Hey guys,
>> 
>> I'm looking at Swift 3.0's goal to improve generics.
>> 
>> Is there any info yet if and how we will be able to refer to instances of protocols that have associated types?
> 
> As far as I know, this isn't a solvable problem.

There are solutions to common patterns that we can and should support. If there are only associated types, then the dynamic type 'protocol<Fooable where Foo == SomeSpecificType> is often useful; the standard library itself fakes dynamic type containers for SequenceType and GeneratorType that do this.

For protocols like Equatable and Hashable with Self parameter requirements, it's true that the compiler can't implicitly produce an Equatable dynamic type that conforms to Equatable. There are a couple possible solutions to this. There's a fairly obvious generalized implementation that could be provided manually:

extension Equatable: Equatable {}
func ==(x: Equatable, y: Equatable) -> Bool {
  // If the values are the same type, use that type's == operator.
  // Pretend that 'as? dynamicType' works
  if let yAsX = y as? x.dynamicType {
    return x == yAsX
  }
  // Values of different type aren't ==.
  return false
}

We'd want to tweak the language rules for implicit promotion so that things like `1 == 1.0` don't implicitly choose the heterogeneous Equatable implementation of `==`, but this would otherwise allow for protocols to require Equatable and Hashable without forgoing the ability to be useful dynamic types.

Another possibility is to generalize Equatable so that a conforming type or refining protocol can control how dynamic its equatability is:

protocol Equatable {
  // The upper bound of type that this type can be equated with. Defaults to `Self`.
  typealias EquatesWith = Self

  // `EquatesWith` must be `Self` or a supertype of `Self`.
  where Self: EquatesWith

  func ==(_: EquatesWith, _: EquatesWith) -> Bool
}

A dynamic protocol can then refine Equatable or Hashable by equating with its own dynamic type, rather than defaulting to strict equatability:

protocol Drawable: Hashable {
  // All Drawables must be equatable with all other Drawables.
  where EquatesWith == Drawable

  func draw()
}

-Joe

>> What is the difficulty in supporting this?
> 
> Here's a simple example:
> 
> protocol P {
>   typealias A
>   var x: A { get set }
> }
> 
> struct Y : P {
>   var x: Int
> }
> 
> struct Z : P {
>   var x: String
> }
> 
> func f(p1: P, p2: P) {
>   p1.x = p2.x // assigning an Int to a String?
> }
>   
>> Simple examples:
>> var list = [Hashable]()
>> var hashable: Hashable = 2
>> 
>> Right now all we get is
>> protocol 'Hashable' can only be used as a generic constraint because it has Self or associated type requirements
> 
> 
> In this case it's "self requirements" inherited from Equatable that make instances of Hashable impossible.
> That said, providing for sets/dictionaries of heterogeneous values is a problem we're interested in solving in the standard library.
> 
> -Dave
> 
> 
> 
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151214/17d64118/attachment.html>


More information about the swift-evolution mailing list