You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 13:38:18