В Scala 2 я использовал проекцию типа в простой модели ORM, которая содержит IdEntity
и несколько репозиториев. Теперь я попытался перейти на Scala 3, но застрял, поскольку проекция типа с #
отсутствует.
Упрощенный код выглядит так:
trait IdEntity:
type ID_Type
val id: ID_Type
trait IdLongEntity extends IdEntity:
override type ID_Type = Long
trait IdStringEntity extends IdEntity:
override type ID_Type = String
trait IdRepository[E <: IdEnity]:
def findById(id: E#ID_Type): Option[E]:
Есть идеи, как преодолеть проекцию отсутствующего типа в def findById(id: E#ID_Type)
?
Я уже пробовал использовать совпадение типов, например
object IdEntity:
type Aux[K] = IdEntity {type ID_Type = K}
type IdType[T <: IdEntity] = T match
case IdEntity.Aux[k] => k
но это не работает, так как компиляция считает, что типы не совпадают.
Обновлять:
Благодаря ответу я попробовал классы типов, а также тип соответствия, но оба они терпят неудачу, как только я попробую следующее (https://scastie.scala-lang.org/UtW9u0mbS5ydiZmQNNWV6g):
trait IdEntity:
type ID_Type
val idDefault: ID_Type
val id: Option[ID_Type]
def idOrDefault: ID_Type = id.getOrElse(idDefault)
trait IdRepository[E <: IdEntity](using val e: E):
type ID_Type = e.ID_Type
def delete(id: ID_Type): Either[String, Int] = deleteImpl(id)
def delete(entity: E): Either[String, Int] = delete(entity.idOrDefault) // here the type error occures
protected def deleteImpl(id: ID_Type): Either[String, Int]
trait IdEntity:
type ID_Type
val id: ID_Type
object IdEntity:
type Aux[K] = IdEntity {type ID_Type = K}
trait IdLongEntity extends IdEntity:
override type ID_Type = Long
trait IdStringEntity extends IdEntity:
override type ID_Type = String
// match type
type IdType[T <: IdEntity] = T match
case IdEntity.Aux[k] => k
trait IdRepository[E <: IdEntity]:
def findById(id: IdType[E]): Option[E] = ???
https://scastie.scala-lang.org/QoV6Z7xRTOa6hVhAYOb0uw
это не работает, так как компиляция считает, что типы не совпадают
Вам следует подготовить репродукцию и мы посмотрим, можно ли исправить компиляцию.
trait IdEntity:
type ID_Type
val id: ID_Type
object IdEntity:
type Aux[K] = IdEntity {type ID_Type = K}
trait IdLongEntity extends IdEntity:
override type ID_Type = Long
trait IdStringEntity extends IdEntity:
override type ID_Type = String
// type class
trait IdType[T <: IdEntity]:
type Out
object IdType:
given [K, T <: IdEntity.Aux[K]]: IdType[T] with
type Out = K
trait IdRepository[E <: IdEntity]:
def findById(using idType: IdType[E])(id: idType.Out): Option[E] = ???
https://scastie.scala-lang.org/166gIUf3Rg2DwSgLlheXvQ
Или вы даже можете попробовать использовать классы типов, начиная с IdEntity
, например, создав IdEntity
класс типа:
trait IdEntity:
type ID_Type
val id: ID_Type
trait IdLongEntity extends IdEntity:
override type ID_Type = Long
trait IdStringEntity extends IdEntity:
override type ID_Type = String
trait IdRepository[E <: IdEntity](using val e: E):
def findById(id: e.ID_Type): Option[E]
https://scastie.scala-lang.org/ap4GJY9NQyOyt2eHwobXSg
Как в Scala 3 заменить проекцию общего типа, которая была удалена?
Чем Дотти предлагает заменить проекции шрифтов?
E не является допустимым путем, поскольку не является конкретным типом (Scala 3)
https://docs.scala-lang.org/scala3/reference/dropped-features/type-projection.html
Перевод
// scala 2.13, type projections
trait IdEntity {
type ID_Type
val idDefault: ID_Type
val id: Option[ID_Type]
def idOrDefault: ID_Type = id.getOrElse(idDefault)
}
trait IdRepository[E <: IdEntity] {
type ID_Type = E#ID_Type
def delete(id: ID_Type): Either[String, Int] = deleteImpl(id)
def delete(entity: E): Either[String, Int] = delete(entity.idOrDefault)
protected def deleteImpl(id: ID_Type): Either[String, Int]
}
возможно
// scala 3, match types
trait IdEntity:
type ID_Type
val idDefault: ID_Type
val id: Option[ID_Type]
def idOrDefault: ID_Type = id.getOrElse(idDefault)
object IdEntity:
type Aux[K] = IdEntity {type ID_Type = K}
type IdType[T <: IdEntity] = T match
case IdEntity.Aux[k] => k
// unfortunately, E <: IdEntity.Aux[IdType[E]] doesn't work (match types are not lazy enough): Recursion limit exceeded. Maybe there is an illegal cyclic reference?
trait IdRepository[E <: IdEntity]:
type ID_Type = IdType[E]
def delete(id: ID_Type): Either[String, Int] = deleteImpl(id)
def delete(entity: E {type ID_Type = IdType[E]}): Either[String, Int] = delete(entity.idOrDefault)
protected def deleteImpl(id: ID_Type): Either[String, Int]
или
// scala 3, type classes (path-dependent types)
trait IdEntity:
type ID_Type
val idDefault: ID_Type
val id: Option[ID_Type]
def idOrDefault: ID_Type = id.getOrElse(idDefault)
trait IdRepository[E <: IdEntity](using val e: E):
type ID_Type = e.ID_Type
def delete(id: ID_Type): Either[String, Int] = deleteImpl(id)
def delete(entity: E {type ID_Type = e.ID_Type}): Either[String, Int] = delete(entity.idOrDefault)
protected def deleteImpl(id: ID_Type): Either[String, Int]
Спасибо, я попробовал использовать тип соответствия, но как только у меня появился метод def delete(e: E): Unit = delete(e.id)
, я получил ошибку типа: e.id
тип e.ID_Type
не соответствует IdType[E]
. То же самое с классом типа.
Вот более подробный фрагмент кода, включающий ошибку scastie.scala-lang.org/UtW9u0mbS5ydiZmQNNWV6g
Спасибо большое, обе попытки работают. Один лучше другого?
@MiNoS Нет, оба работают. Проекции типов и классы типов в Scala 2, сопоставление типов и классов типов в Scala 3, семейства типов и классы типов в Haskell. Просто разные подходы к программированию на уровне типа. Оба имеют угловые корпуса. Типы совпадений могут быть сложными на уровне значений, типы, зависящие от пути, могут быть сложными.
Я почти закончил, но застрял в случае, когда мне нужно «привести» экземпляр E
к экземпляру E {type ID_Type = IdType[E]}
, возможно ли это? Я попробовал TypeTest, но не смог заставить его работать.
@MiNoS Пожалуйста, подготовьте воспроизведение (возможно, начните новый вопрос). На уровне значения существует зависимая типизация сопутствующие типы соответствия на уровне типа.
Дело в том, что
E#ID_Type
по сутиAny