从另一个具有参数化类型参数的方法调用具有参数化类型参数的方法时保留特定结果类型参数

Retaining specific result type param when calling a method with parameterised type parameter from another method with parameterised type parameter

假设我正在尝试使用继承对实体、元数据和存储库进行建模

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
}

如果我然后尝试创建一个特征来保存一些用于通用后备存储的方法,在我看来即使类型是已知的......我实际上不能正确使用它们?

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 _
    // typeTest.meta.version // complains about version
    //
    retrieved.meta.version // complains about version

  }

}

我正在努力锻炼:

  1. 为什么编译器认为 retrieved.meta 没有 .version,或者实际上,Any/Object 没有任何东西。
  2. 我能做些什么来完成这项工作

尝试修复签名

def getFromStore[Meta <: EntityMetadata, E[M <: EntityMetadata] <: Entity[M]](
  id: E[Meta]#Id
): Option[E[VersionedMetadata]]

def getVersion[Meta <: EntityMetadata, E[M <: EntityMetadata] <: Entity[M]](
  id: E[Meta]#Id
): Option[Long]

E[_]E[_] <: Entity[_]中的Entity[_]不同:E[_]是一个type constructor (i.e. you can have a type E[M] for every type M), Entity[_] aka Entity[Meta] forSome { type Meta } is an existential type。存在类型没有 .versionretrieved.metaAny 类型)。


另一种修复代码的方法是

def getFromStore[Meta <: EntityMetadata, E[_] <: Entity[_]](
  id: E[Meta]#Id
): Option[E[VersionedMetadata]]

def getVersion[Meta <: EntityMetadata, E[_] <: Entity[_ <: EntityMetadata]](
  id: E[Meta]#Id
): Option[Int] = getFromStore(id).flatMap { retrieved =>
  val typeTest: E[VersionedMetadata] = retrieved

  retrieved.meta.maybeVersion
}

我保留了类型构造函数和存在类型,但将上限 <: EntityMetadata 添加到存在类型 Entity[_ <: ...] 的参数,这是类型参数 E[_] <: ... 的上限。现在 retrieved.meta 的类型是 EntityMetadata 的子类型,所以它有 .maybeVersion 而不是 .version(并且 map 应该替换为 flatMap) .另外 Long 应该替换为 Int.

或者您可以使用上限 _ <: VersionedMetadata 而不是我的 <: EntityMetadata。那么你可以保留 .version.mapLong.