here’s a useful protocol:
protocol DatabaseCollection
{
...
}
it has lots of requirements, but the only thing we care about right now is that it has an extension method called setup(with:)
that does exactly what it sounds like:
extension DatabaseCollection
{
func setup(with session:Mongo.Session) async throws
{
...
}
}
of course, one protocol DatabaseCollection
couldn’t possibly cover all possible use-cases, we might want to have a derived protocol DatabaseCollectionCapped
¹ like:
protocol DatabaseCollectionCapped:DatabaseCollection
{
var capacity:Int { get }
}
naturally, you need to do some extra things to setup
a DatabaseCollectionCapped
, so it also has an extension method called setup(with:)
that shadows the one in the superprotocol.
extension DatabaseCollectionCapped
{
func setup(with session:Mongo.Session) async throws
{
func _super(self:some DatabaseCollection) async throws
{
try await self.setup(with: session)
}
try await _super(self: self)
...
}
}
that won’t work too well, because every type that conforms to DatabaseCollectionCapped
will get two members called setup(with:)
and only one of them is correct to use. and sometimes we want to intentionally call the wrong one (like in _super(self:)
), but usually we end up unintentionally calling the wrong one.
you could go the opposite direction and make setup(with:)
a requirement of DatabaseCollection
.
protocol DatabaseCollection
{
func setup(with session:Mongo.Session) async throws
...
}
but then the derived protocol loses the ability to call the base protocol’s setup(with:)
implementation. plus, you would never want to implement it directly, you would only ever want to inherit it from a protocol.
what is the best way to achieve a class-like hierarchy of protocol implementations, without actually resorting to class inheritance?
[1] in six months, the name will become less awkward!