Recently I started to port a simple ORM from Scala 2 to Scala 3 using generics and match types. After struggling with the type projection (see How to overcome Scala 3 missing type projection?) I now stuck in type chaos ;).
A simplified version of code looks like
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
As you can see I use match types for the ID
of which is fine so fare and use a type TE
which is the generic E
with match typed ID
. Now I have the problem that the basic methods like findById
returns the generic E but if I need to reuse I need TE
instead.
Since both are the same as runtime a asInstanceOf[TE]
work but this is not what I prefer.
Is there a way to 'cast' an instance of E
to TE
since they are the same at runtime?
I already tried TypeTest but this does not work since I work with generics here.
You can try to change the signature of findById
(using TE
instead of E
wherever possible):
trait IdRepository[E <: IdEntity]:
...
def findById(id: Option[ID_Type]): Option[TE]
https://scastie.scala-lang.org/DmytroMitin/vuZAH5crQZSIyHUOwyCfJQ
or use an implicit hint in update
(postponing checking that E =:= TE
till when E
is inferred to be a concrete type):
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
or
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
How to define a scala.ValueOf for tuples in scala 3?
How to prove that `Tuple.Map[H *: T, F] =:= (F[H] *: Tuple.Map[T, F])` in Scala 3