Suppose I'm attempting to model entities, metadata, and repositories using inheritance
trait EntityMetadata {
def maybeVersion: Option[Int]
}
// For creation
case object NoMetadata extends EntityMetadata {
def maybeVersion: Option[Int] = None
}
// For other cases
final case class VersionedMetadata(version: Int) extends EntityMetadata {
def maybeVersion: Option[Int] = Some(version)
}
trait Entity[Meta <: EntityMetadata] {
type Id
def id: Id
def meta: Meta // Meta is paremeterised
}
If I then try to create a trait to hold some methods for a general purpose backing store, it seems to me like even though the types are known...I can't actually use them properly?
trait BackingStore {
// Method for retrieving an entity by id
// `Meta` doesn't really matter here, but we can't
// wild-card it. We return the Entity with VersionedMetadata
// since it's been stored if we can find it
def getFromStore[Meta <: EntityMetadata, E[_] <: Entity[_]](
id: E[Meta]#Id
): Option[E[VersionedMetadata]]
// Just for demo purposes, try to retrieve something by id
// and return its metadata version
def getVersion[Meta <: EntityMetadata, E[_] <: Entity[_]](
id: E[Meta]#Id
): Option[Long] = getFromStore(id).map { retrieved =>
// So far so good, we know it's E[VersionedMetadata]
val typeTest: E[VersionedMetadata] = retrieved
//
// value version is not a member of _$2
// typeTest.meta.version // complains about version
//
retrieved.meta.version // complains about version
}
}
I'm trying to work out:
- Why the compiler thinks that
retrieved.meta
doesn't have.version
, or, in fact, anything really beyond whatAny
/Object
has. - What I can do to make this work