Test if a type conforms to a non-existential protocol

If we literally just want the Bool result for isBidirectional, that’s actually pretty easy. Here’s a quick demo for all 5 of the standard Collection subprotocols.

First some empty enums:

enum BidirectionalCollectionMarker<T> {}
enum LazyCollectionMarker<T> {}
enum MutableCollectionMarker<T> {}
enum RandomAccessCollectionMarker<T> {}
enum RangeReplaceableCollectionMarker<T> {}

Then some empty conformances:

protocol ConformanceMarker {}

extension BidirectionalCollectionMarker   : ConformanceMarker where T: BidirectionalCollection {}
extension LazyCollectionMarker            : ConformanceMarker where T: LazyCollectionProtocol {}
extension MutableCollectionMarker         : ConformanceMarker where T: MutableCollection {}
extension RandomAccessCollectionMarker    : ConformanceMarker where T: RandomAccessCollection {}
extension RangeReplaceableCollectionMarker: ConformanceMarker where T: RangeReplaceableCollection {}

And finally the Collection properties:

extension Collection {
  var isBidirectional   : Bool { BidirectionalCollectionMarker<Self>.self    is ConformanceMarker.Type }
  var isLazy            : Bool { LazyCollectionMarker<Self>.self             is ConformanceMarker.Type }
  var isMutable         : Bool { MutableCollectionMarker<Self>.self          is ConformanceMarker.Type }
  var isRandomAccess    : Bool { RandomAccessCollectionMarker<Self>.self     is ConformanceMarker.Type }
  var isRangeReplaceable: Bool { RangeReplaceableCollectionMarker<Self>.self is ConformanceMarker.Type }
}

For actual use, it probably makes sense to have static Collection.isX properties as well, in which case the instance properties could just call Self.isX.

In any event, here’s a helper for checking all the capabilities of a Collection instance:

CollectionCapabilities
struct CollectionCapabilities {
  var isBidirectional   : Bool
  var isLazy            : Bool
  var isMutable         : Bool
  var isRandomAccess    : Bool
  var isRangeReplaceable: Bool
}

extension Collection {
  var capabilities: CollectionCapabilities {
    CollectionCapabilities(
      isBidirectional   : isBidirectional,
      isLazy            : isLazy,
      isMutable         : isMutable,
      isRandomAccess    : isRandomAccess,
      isRangeReplaceable: isRangeReplaceable
    )
  }
}

• • •

Edit: Here’s a slight variation that adds a 2nd empty protocol in order to shorten the line length of the Collection properties, and make things read a little better:

Version 2
extension Collection {
  var isBidirectional   : Bool { BidirectionalCollectionMarker<Self>.conforms }
  var isLazy            : Bool { LazyCollectionMarker<Self>.conforms }
  var isMutable         : Bool { MutableCollectionMarker<Self>.conforms }
  var isRandomAccess    : Bool { RandomAccessCollectionMarker<Self>.conforms }
  var isRangeReplaceable: Bool { RangeReplaceableCollectionMarker<Self>.conforms }
}

protocol ConformanceMarker {}

extension ConformanceMarker {
  static var conforms: Bool { Self.self is Conforming.Type }
}

enum BidirectionalCollectionMarker<T>   : ConformanceMarker {}
enum LazyCollectionMarker<T>            : ConformanceMarker {}
enum MutableCollectionMarker<T>         : ConformanceMarker {}
enum RandomAccessCollectionMarker<T>    : ConformanceMarker {}
enum RangeReplaceableCollectionMarker<T>: ConformanceMarker {}

protocol Conforming {}

extension BidirectionalCollectionMarker   : Conforming where T: BidirectionalCollection {}
extension LazyCollectionMarker            : Conforming where T: LazyCollectionProtocol {}
extension MutableCollectionMarker         : Conforming where T: MutableCollection {}
extension RandomAccessCollectionMarker    : Conforming where T: RandomAccessCollection {}
extension RangeReplaceableCollectionMarker: Conforming where T: RangeReplaceableCollection {}
2 Likes