Scala中如何通过forSome类型定义正确表达自引用存在类型
Scala 2 F-Bound自引用类型的简写实现方案
你遇到的两类问题都是Scala 2编译器的长期遗留缺陷:this.type不适合需要构造同类型新实例的场景、存在类型无法正确追踪递归F-Bound的类型边界,这些问题都已经在Dotty/Scala 3中被彻底修复。
之前的尝试写法为什么失效
你之前写的存在类型定义:
type SomeFoo = Foo[SelfType] with SelfType forSome { type SelfType <: Foo[SelfType] }
存在两个无法绕过的问题:
- 语法层面Scala 2的存在类型解析逻辑,不会把
with后面的SelfType和存在块中声明的存在变量做绑定——它会将with修饰的类型视为独立的未知类型,和存在块里的SelfType完全没有关联,自然无法匹配自引用约束。 - 就算调整语法让绑定成立,那个从2009年遗留的存在类型边界追踪bug,也会在类型检查阶段直接丢失
SelfType <: Foo[SelfType]的约束,触发编译器异常或者类型不匹配错误。
可稳定落地的实现方案
Scala 2中没有办法在完全不改动原有Foo定义的前提下,写出完全等价于[F <: Foo[F]] => Foo[F]的顶级存在类型别名,这是编译器bug直接堵死的路径。但有两个可以大幅减少签名冗余的成熟方案:
- 最推荐的方案:将外部泛型参数转为内部抽象类型成员
把原来带外部F-Bound泛型参数的特质,改为带内部抽象类型成员的定义,从根源上绕开存在类型对外部泛型的递归解析bug:
这种改造的子类定义成本几乎为零:原来的实现子类trait Foo { // 把自引用约束的类型从外部泛型移到内部抽象类型 type SelfType <: Foo { type SelfType = SelfType } this: SelfType => final def foo: SelfType = newInstance(??? /*原有业务逻辑保持不变*/) protected def newInstance(i: Int): SelfType }class MyFoo extends Foo[MyFoo],改造后只需要写class MyFoo extends Foo { override type SelfType = MyFoo },逻辑完全不变,也没有额外的语法负担。
改造完成后,你需要的简写完全可以正常工作,没有任何额外的类型负担:
这种写法的优势是完全不损失原有类型安全:访问// 直接用Foo本身作为简写别名即可 def process(f: Foo): Unit = ???f.foo方法时,编译器依然可以正确追踪到返回值就是当前实例对应的SelfType,不会触发之前的存在类型bug。 - 最小改动的存在类型尝试(有局限性)
如果不想改动原有Foo的带外部泛型的定义,可以尝试不带with修饰的纯存在类型定义:
这种写法在简单场景下可以正常接收任意满足F-Bound约束的Foo实例,但一旦触发那个已知的存在类型边界丢失bug(比如调用返回SelfType的方法),就会抛出类型不匹配错误,稳定性远不如第一种方案。type AnyFoo = Foo[x] forSome { type x <: Foo[x] }
问题表述的优化建议
当前的问题描述可以更聚焦核心矛盾,减少对两个独立编译器限制的平铺,让回答者更快定位需求:
我在Scala 2中实现F-Bound多态模式的特质时,为了绕开存在类型无法正确追踪递归类型边界的编译器bug,不得不在所有方法签名上手写
[F <: Foo[F]] Foo[F]的泛型约束。当特质本身带有多个其他业务类型参数时,方法签名会变得极度冗长。我尝试编写存在类型别名来简化签名,但所有写法都会触发编译器的类型解析bug。请问有没有稳定的方式实现这类自引用类型的简短别名,让方法可以直接通过别名接收任意满足F-Bound约束的实例?
内容的提问来源于stack exchange,提问作者Creos
相关产品推荐
相关产品推荐

