Comparing two protocols

Hi folks, playing arround with some meta-programming today and stumbled on this:
It is currently possible to write:

protocol A { }
class ClassA: A { } // or enum, struct
ClassA.self is A.Type // returns true, as expected

But when you try to do the same with protocols:

protocol A { }
protocol ProtoA: A { }
ProtoA.self is A.Type // returns false... not what one would expect

Is there a way to write the question: "does any type conforming to ProtocolA conforms to ProtocolB?" (and, of course, in a way that the answer to that question is true when ProtocolA:ProtocolB)

Interestingly, an approach using overloading does not work either:

protocol A { }
class ClassA: A { }
protocol B: A { }

func test<T:A>(_ type: T.Type) -> Bool {
   return true
} 
func test<T>(_ type: T.Type) -> Bool {
   return false
}

test(ClassA.self) // still returns true... :-)
test(B.self) // still returns false... :-(

The answer is 'no'. In Swift there are two types of protocols:

  • protocol the constraint protocol A { ... }
  • protocol the type (also know as existential type) var a: A

Protocol the constraint only 'refines' a different protocol, but does not conform to nor inherit from its parent protocol.

Protocol the type does not conform to its own protocol (except in case of Error). Existentials today are implicit, but we might hopefully change it by adding a prefix keyword any in front of them.

// This is an existential
var a: any A

That might allow us to extend these and make them conform to their protocols.

// not valid Swift code
extension any A: A { ... }

Furthermore T.self returns you one of two different kinds of of metatypes. The mind bending thing is that they are merged. There are plenty of threads here in the forums that mention these things. You can lookup some discussion here:

6 Likes

Thanks Adrian, especially for linking that thread, it's a gold mine :slight_smile:

1 Like