I'm creating a protocol to abstract different models (hand-crafted, decodable, and thrift-generated) like so:
protocol FeedItemModeling {
var text: String? { get }
var url: URL? { get }
}
protocol FeedModeling {
var items: [FeedItemModeling]? { get }
}
This would work great except that thrift models aren't using a basic array, but a TList
which conforms to RandomAccessCollection
among other things. Since both Array
and TList
conform to RandomAccessCollection
, I'm trying to change the protocol definition to:
Attempt A)
var items: RandomAccessCollection<FeedItemModeling>? { get }
which gives the error: "Cannot specialize non-generic type 'RandomAccessCollection'." I assume it's because associatedtype
is a half-baked generics in the sense that it can enforce constraints internally in the protocol itself, but isn't exposed externally; meaning that in our case, RandomAccessCollection
's associatedtypes
are resolved within RandomAccessCollection
but there's no way for FeedModeling
to access it?
Attempt B)
var items: RandomAccessCollection? { get }
gives "Protocol 'RandomAccessCollection' can only be used as a generic constraint because it has Self or associated type requirements" and loses the constraint on FeedItemModeling anyway.
Attempt C)
associatedtype C: RandomAccessCollection
var items: C? { get }
works, but we lost the constraint on FeedItemModeling
, so that's not acceptable.
Attempt D)
associatedtype C: RandomAccessCollection
var items: C<FeedItemModeling>? { get }
gives "Cannot specialize non-generic type 'Self.C'." I assume it's for the same reason as above?
Attempt E)
associatedtype C<E>: RandomAccessCollection where E: FeedItemModeling
var items: C? { get }
gives "Associated types must not have a generic parameter list". I assume it's because now there's a constraint on a constraint, which can't be expressed this way?
Attempt F)
typealias C<E> = RandomAccessCollection
var items: C<FeedItemModeling>? { get }
gives: "Protocol 'RandomAccessCollection' can only be used as a generic constraint because it has Self or associated type requirements". Same reason?
Attempt G)
associatedtype FeedItemModelingList = RandomAccessCollection where RandomAccessCollection.Element: FeedItemModeling
var items: FeedItemModelingList? { get }
gives "Associated type 'Element' can only be used with a concrete type or generic parameter base". I don't understand this one; please let me know why?
Attempt H)
protocol FeedItemModelingList: RandomAccessCollection {}
...
var items: FeedItemModelingList? { get }
gives: "Protocol 'FeedItemModelingList' can only be used as a generic constraint because it has Self or associated type requirements".
Attempt I)
protocol FeedItemModelingList: RandomAccessCollection where Element == FeedItemModeling {}
...
associatedtype L = FeedItemModelingList
var items: L? { get }
seems to work (so far), but is really ugly.
Any better idea?