subprotocol as type when super protocol has an associated type


(Benjamin Spratling) #1

I’m running into trouble using protocols as types when they have an associated type. I understand the restriction of using a protocol with an associated type only as a generic constraint, since our down-casting syntax has no way of checking the associated types. However, if I’m considering whether an instance conforms to a sub protocol, which introduces no additional associated types itself, when the instance is already known to conform to the super-protocol which introduces the associated type, I believe such behavior is well-defined, and should be supported. So, I’m asking for help to put together a proposal to get this added.

Here’s an example:
3 protocols, one base Protocol which declares an associated type.
And 2 sub-protocols.

protocol A {
  associatedType V
}

protocol B : A {
  func someFunction<V>(V?)->String
}

protocol C : B {
  func someOtherFunction<V>()->V?
}

Note that the sub protocols do not add additional associated types.

When I want to use these protocols, using the base protocol as a generic constraint works fine.

class M {
  func doSomething<P, V>(a:P, completion:@escaping(V?)->()) where P : A, V==P.V {
    if let c = a as? C {
      c.someOtherFunction(…)
      … more code
    } else if let b = a as? B {
      b.someFunction(…)
      … more code
    }
  }
}

However, when I try to check whether the instance conforms to either of the sub-protocols, I get an error, saying protocols with associated types can only be used as generic constraints.

I believe that in this case, since the sub-protocols do not add any additional associated types, this code should be well-defined, and compilable.


(Anders) #2

Whether the protocol inherits from another protocols does not matter. As
long as the protocol requirements include associated types, it would not
be possible to be used as existentials (for now).
Moreover, even if you use the "subprotocols" inside a generic
environment that constrains `A`, it does not change how the symbol `B`
and `C` mean inside that context. They are still unbound protocols.
The feature you called for is generalized existentials. It is on the
Generic Manifesto and had a few long discussion threads on the list
before. Apparently, it is out of scope for Swift 4, and whether it is in
scope of Swift 5 remains to be seen.
By the way, for your code snippet, apparently you may simply add
"doSomething" overloads for `P: B` and `P: C`. The compiler will pick
the most specific variant for you at compile time. Deferring to runtime
with existentials is not necessarily the only option.
On Sun, Jun 25, 2017, at 01:50 AM, Benjamin Spratling via swift-evolution wrote:> I’m running into trouble using protocols as types when they have an

···

associated type. I understand the restriction of using a protocol
with an associated type only as a generic constraint, since our down-
casting syntax has no way of checking the associated types. However,
if I’m considering whether an instance conforms to a sub protocol,
which introduces no additional associated types itself, when the
instance is already known to conform to the super-protocol which
introduces the associated type, I believe such behavior is well-
defined, and should be supported. So, I’m asking for help to put
together a proposal to get this added.>
Here’s an example:
3 protocols, one base Protocol which declares an associated type.
And 2 sub-protocols.

protocol A {
associatedType V
}

protocol B : A {
func someFunction<V>(V?)->String
}

protocol C : B {
func someOtherFunction<V>()->V?
}

Note that the sub protocols do not add additional associated types.

When I want to use these protocols, using the base protocol as a
generic constraint works fine.>
class M {
func doSomething<P, V>(a:P, completion:@escaping(V?)->()) where P : A,
V==P.V {> if let c = a as? C {
c.someOtherFunction(…)
… more code
} else if let b = a as? B {
b.someFunction(…)
… more code
}
}
}

However, when I try to check whether the instance conforms to either
of the sub-protocols, I get an error, saying protocols with associated
types can only be used as generic constraints.>
I believe that in this case, since the sub-protocols do not add any
additional associated types, this code should be well-defined, and
compilable.>
_________________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Benjamin Spratling) #3

The feature you called for is generalized existentials. It is on the Generic Manifesto and had a few long discussion threads on the list before. Apparently, it is out of scope for Swift 4, and whether it is in scope of Swift 5 remains to be seen.

Thanks, I wasn’t sure of the right name for the kind of feature. I’ll be sure to watch for any related proposals and vote yes early and often. :wink:
I must have missed the discussions. I had switched to digests for a while, which made following any actual discussion nearly intractable.

By the way, for your code snippet, apparently you may simply add "doSomething" overloads for `P: B` and `P: C`. The compiler will pick the most specific variant for you at compile time. Deferring to runtime with existentials is not necessarily the only option.

Yes, that’s what I’m doing now, and it felt unnecessary. There were pieces of my logic which had to be duplicated.

-Ben

···

On Jun 24, 2017, at 6:24 PM, Anders <hello@andersio.co> wrote: