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

Scala 3中如何实现从任意联合类型移除指定类型的类型级操作

Scala 3 联合类型移除指定类型的原生实现

完全可以基于Scala 3原生特性实现和shapeless.ops.coproduct.Remove能力一致的类型操作,不需要引入任何第三方库,核心靠Scala 3的匹配类型完成类型层面的计算,配合轻量的内联方法完成运行时的分支处理。


核心实现

首先定义类型层面的移除操作,用Scala 3的匹配类型递归拆解联合类型:

type Remove[U, T] = U match {
  case T => Nothing
  case a | b => Remove[a, T] | Remove[b, T]
  case rest => rest
}

这个类型定义天然满足转换规则:

  • 联合类型中匹配到要移除的目标类型T时,返回Nothing,而Scala联合类型会自动忽略Nothing(A | Nothing等价于A),不需要额外做类型化简
  • 遇到联合分支时递归拆解,分别对两侧的类型做移除操作后重新组合为联合类型
  • 遇到非目标的单类型直接保留

预期的类型转换效果可以直接被满足:

  • Remove[Foo | Bar | Baz | Bang, Bar] 化简后为 Foo | Baz | Bang
  • Remove[Foo | Bar, Bar] 化简后为 Foo
  • Remove[Bar, Bar] 化简后为 Nothing

接下来实现运行时的错误处理方法,用内联保证编译期类型检查,零额外开销:

import scala.util.Either

inline def handleError[U, T, A](err: U)(f: T => A): Either[Remove[U, T], A] =
  err match {
    case e: T => Right(f(e))
    case other => Left(other.asInstanceOf[Remove[U, T]])
  }

这里的强制类型转换是完全安全的:前面的模式匹配已经拦截了所有属于目标类型T的分支,剩余传入Left的值必然属于移除T后的剩余联合类型,编译器暂时无法通过匹配类型自动推导出这个结论,所以需要手动标记,运行时不会有任何额外开销。


效果验证

直接用给出的场景测试即可:

// 测试用样例定义
case class Foo()
case class Bar()
case class Baz()
case class Bang()
case class Thing()
case class OtherThing()
object OtherThing {
  val default: OtherThing = OtherThing()
}

@main def test() = {
  val result: Either[Foo | Bar | Baz | Bang, Thing] = Left(Bar())
  val otherResult: Either[Foo | Bar, OtherThing] = Left(Bar())

  // 第一个用例,返回类型自动推导为Either[Foo | Baz | Bang, Option[Thing]]
  val recoveredResult = result
    .map(Option.apply)
    .left.flatMap(handleError(_)(_ => None))

  // 第二个用例,返回类型自动推导为Either[Foo, OtherThing]
  val recoveredOther = otherResult
    .left.flatMap(handleError(_)(_ => OtherThing.default))
}

边界场景也符合预期:如果输入的错误类型只有Bar一种,处理后返回的Either[Nothing, A]等价于成功分支的Right(a),不会残留错误类型。如果要移除的类型不在原联合类型中,Remove[U, T]会直接等于原类型U,处理方法不会触发恢复逻辑,直接透传原错误。


内容的提问来源于stack exchange,提问作者Dylan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 05:36:26