Сохранение определенного параметра типа результата при вызове метода с параметризованным параметром типа из другого метода с параметризованным параметром типа - PullRequest
2 голосов
/ 19 апреля 2019

Предположим, я пытаюсь смоделировать сущности, метаданные и репозитории, используя наследование

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

  }

}

Я пытаюсь разобраться:

  1. Почему компилятор считает, чтоretrieved.meta не имеет .version, или, по сути, ничего сверх того, что имеет Any / Object.
  2. Что я могу сделать, чтобы сделать эту работу

1 Ответ

2 голосов
/ 19 апреля 2019

Попробуй исправить подписи

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[_] и Entity[_] в E[_] <: Entity[_] различаются: E[_] является конструктором типа (т.е. вы можете иметь тип E[M] для каждого типа M), Entity[_] aka Entity[Meta] forSome { type Meta } является экзистенциальным типом . Экзистенциальный тип не имеет .version (retrieved.meta был типа Any).


Еще один способ исправить ваш код -

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 вместо .versionmap следует заменить на flatMap). Также Long следует заменить на Int.

Или вы можете поставить верхнюю границу _ <: VersionedMetadata вместо моей <: EntityMetadata. Тогда вы можете оставить .version, .map и Long.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...