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 | BangRemove[Foo | Bar, Bar]化简后为FooRemove[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
相关产品推荐
相关产品推荐

