如何解决Scala 3中缺失类型投影(#)的问题?
Scala 3 迁移中类型投影替代方案(原Scala 2 ORM模型适配)
问题背景
在Scala 2的简易ORM模型中,通过类型投影实现了包含IdEntity和Repository的结构,但迁移到Scala 3时,因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 <: IdEntity]: def findById(id: E#ID_Type): Option[E]
核心问题是def findById(id: E#ID_Type)中的类型投影无法在Scala 3中使用,尝试用匹配类型实现但编译报错:
object IdEntity: type Aux[K] = IdEntity {type ID_Type = K} type IdType[T <: IdEntity] = T match case IdEntity.Aux[k] => k
更新后的问题
尝试类型类和匹配类型方案后,编写代码仍出现类型错误:
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) // 此处类型错误 protected def deleteImpl(id: ID_Type): Either[String, Int]
解决方案
方案1:修正匹配类型提取ID类型
去掉冗余的Aux类型,直接针对IdEntity的类型成员做匹配,替代原类型投影:
type IdType[E <: IdEntity] = E match case ie: IdEntity => ie.ID_Type trait IdRepository[E <: IdEntity]: def findById(id: IdType[E]): Option[E]
IdType[E]可正确提取E的ID_Type类型,完全替代E#ID_Type。
方案2:修复更新后的Repository类型错误
更新后的代码中,Repository通过using clause引入E实例,但entity.idOrDefault的类型与当前ID_Type存在差异,可通过两种方式修复:
方式A:统一使用匹配类型定义ID_Type
type IdType[E <: IdEntity] = E match case ie: IdEntity => ie.ID_Type trait IdRepository[E <: IdEntity]: type ID_Type = IdType[E] 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]
此方式无需依赖E的实例,直接通过匹配类型提取类型成员,避免实例依赖带来的类型不一致问题。
方式B:细化类型约束
若必须保留using E的设计,可明确约束entity的ID_Type与当前Repository的ID_Type一致:
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 = ID_Type }): Either[String, Int] = delete(entity.idOrDefault) protected def deleteImpl(id: ID_Type): Either[String, Int]
通过细化entity的类型约束,消除类型不匹配错误。
方案3:改用类型参数替代类型成员(推荐)
从根源上避免类型投影问题,将IdEntity的类型成员改为类型参数,这是Scala 3更推荐的写法:
trait IdEntity[ID]: val id: ID trait IdLongEntity extends IdEntity[Long] trait IdStringEntity extends IdEntity[String] trait IdRepository[ID, E <: IdEntity[ID]]: def findById(id: ID): Option[E]
该写法逻辑清晰,无需类型投影,完全适配Scala 3的设计趋势。
内容的提问来源于stack exchange,提问作者MiNoS
相关产品推荐
相关产品推荐

