Недавно я начал портировать простой ORM из Scala 2 в Scala 3, используя дженерики и типы соответствия. После борьбы с проекцией типа (см. Как преодолеть отсутствие проекции типа в Scala 3?) я застрял в хаосе типов ;).
Упрощенная версия кода выглядит так
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
trait IdRepository[E <: IdEntity]:
type ID_Type = IdType[E]
type TE = E {type ID_Type = IdType[E]}
def delete(id: ID_Type): Either[String, Int] = deleteImpl(id)
def delete(entity: TE): Either[String, Int] = delete(entity.idOrDefault)
def update(entity: TE): Int = {
findById(entity.id) match {
case None => 0
case Some(e) => updateImpl(e) // type error E is not TE
}
}
def findById(id: Option[ID_Type]): Option[E]
protected def deleteImpl(id: ID_Type): Either[String, Int]
protected def updateImpl(entity: TE): Int
https://scastie.scala-lang.org/lOZYn2o5TLiW1CwIvTdFIw
Как вы можете видеть, я использую типы соответствия, ID
из которых вполне подходят, поэтому используйте тип TE
, который является общим E
с типизированным соответствием ID
. Теперь у меня проблема: базовые методы, такие как findById
, возвращают общий E, но если мне нужно повторно использовать, вместо этого мне нужен TE
.
Поскольку оба варианта аналогичны работе во время выполнения, но я предпочитаю это не это.
Есть ли способ «привести» экземпляр asInstanceOf[TE]
к E
, поскольку они одинаковы во время выполнения?
Я уже пробовал TypeTest, но это не работает, поскольку здесь я работаю с дженериками.
Вы можете попробовать изменить подпись findById
(используя TE
вместо E
везде, где это возможно):
trait IdRepository[E <: IdEntity]:
...
def findById(id: Option[ID_Type]): Option[TE]
https://scastie.scala-lang.org/DmytroMitin/vuZAH5crQZSIyHUOwyCfJQ
или используйте неявную подсказку в update
(отложив проверку этого E =:= TE
до тех пор, пока E
не станет конкретным типом):
import scala.compiletime.summonFrom
trait IdRepository[E <: IdEntity]:
...
inline def update(entity: TE): Int = {
findById(entity.id) match {
case None => 0
case Some(e) => summonFrom {
case _: (E =:= TE) => updateImpl(e)
}
}
}
https://scastie.scala-lang.org/DmytroMitin/vuZAH5crQZSIyHUOwyCfJQ/1
или
trait IdRepository[E <: IdEntity]:
...
def update(entity: TE)(using E =:= TE): Int = {
findById(entity.id) match {
case None => 0
case Some(e) => updateImpl(e)
}
}
https://scastie.scala-lang.org/DmytroMitin/vuZAH5crQZSIyHUOwyCfJQ/2
Как определить scala.ValueOf для кортежей в scala 3?
Как доказать, что `Tuple.Map[H *: T, F] =:= (F[H] *: Tuple.Map[T, F])` в Scala 3
scala 3 отображает кортеж в будущее типов кортежей и обратно
@MiNoS Добро пожаловать. Какой подход вы предпочли?
Хотя первое решение — изменение типа возвращаемого значения — очевидно и пришло мне в голову, оно меня не устраивает. Поэтому я склонен использовать решение (using E =:= TE)
.
Как всегда большое спасибо за объяснения.