[swift-evolution] Generic parameters in "as?" checks

Slava Pestov spestov at apple.com
Wed Jan 20 23:26:12 CST 2016

Here’s an attempt to implement this with existing language features:

> protocol HasElementType {
>   func getElement() -> Any.Type?
> }
> extension HasElementType {
>   func getElement() -> Any.Type? {
>     return nil
>   }
> }
> extension Array : HasElementType {
>   func getElement() -> Any.Type? {
>     return Element.self
>   }
> }
> func isArrayWithElementType<T>(a: Any, _ m: T.Type) -> Bool {
>   guard let aa = a as? HasElementType else { return false }
>   guard let elt = aa.getElement() else { return false }
>   return elt is T.Type
> }
> protocol P {}
> struct X : P {}
> print(isArrayWithElementType([1,2,3,4,5], Int.self))
> print(isArrayWithElementType([1,2,3,4,5], Float.self))
> print(isArrayWithElementType(["a", "b"], String.self))
> print(isArrayWithElementType([X()], X.self))
> print(isArrayWithElementType([X()], P.self))

However, the limitation here is that when we do ‘elt is T.Type’, if elt is a metatype conforming to a protocol P, and T is P, the ‘is’ check just checks for metatype identity and not conformance.

So if a generic parameter T is bound to a concrete type C, then T.Type means C.Type. However, if C is a protocol type, then T.Type is really C.Protocol, not C.Type.

Here’s a silly idea:

- Switch P.Type and P.Protocol syntax where P is a protocol type, so that P.Type is the type of P.self, and P.Protocol is the existential metatype. This makes ‘P.Type’ consistent with ’T.Type’ where T is bound to a protocol type. It also makes ‘P.Protocol’ kind of like a protocol — types that conform to P have metatypes that conform to P.Protocol.

- Add a ‘: protocol’ generic constraint, asserting that the type parameter is some protocol existential type:

  func isArrayWithElementType<T : protocol>(…)

- For a generic type parameter T with an existential constraint, you would be able to write T.Protocol to refer to the existential metatype. T.self would not be an instance of T.Protocol, but the elements of your array that *conform* to T would be instances of T.Protocol.

Maybe also we want to allow casts with a metatype expression, rather than a type identifier, on the RHS.

> Or perhaps, if we need angle brackets to be somewhere, something like this:
> if let arr = someObject as? <T: Foo> Array<T> {

Yeah, it’s going to have to be this, since <T : Foo> is shorthand for <T where T : Foo>, and in general the <> of a bound generic type reference are different from <> in a generic signature — the latter introduces new type variables for instance.

>> }
> Is this a feasible thing to ask for?

I think so, but we have to careful with the design. This ties in with some of the discussions on generalizing existential types, also.

> (Apologies if there’s already a way to do this; I was looking for one without success.)
> Charles
> _______________________________________________
> 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/20160120/286ab321/attachment.html>

More information about the swift-evolution mailing list