Scala3中泛型转依赖类型时,协变与逆变修饰符如何映射?
Scala3协变泛型转依赖类型的正确等价实现
问题背景
根据Scala3文档,泛型类型参数会被视为依赖类型(比如class Map[K, V]等价于class Map { type Map$K; type Map$V })。尝试将带协变修饰符的泛型代码转为依赖类型形式时,编译器报类型不兼容错误,求正确的等价转换代码。
原可正常编译的泛型代码
object AsArg { trait P[+TT] { val vv: TT } trait P1 extends P[Product] { val vv: Product } trait P2 extends P1 with P[Tuple1[Int]] { val vv: Tuple1[Int] } }
转换后报错的依赖类型代码
object AsDependentType { trait P { type TT val vv: TT } trait P1 extends P { type TT <: Product val vv: Product } trait P2 extends P1 with P { type TT <: Tuple1[Int] val vv: Tuple1[Int] } }
报错信息
[Error] ...CovariantDependentType.scala:30:11: error overriding value vv in trait P of type P1.this.TT; value vv of type Product has incompatible type [Error] ...CovariantDependentType.scala:36:11: error overriding value vv in trait P of type P2.this.TT; value vv of type Tuple1[Int] has incompatible type two errors found
正确实现方式
要把协变泛型转成等价的依赖类型,得抓住两个核心点:
- 父特质
P里的vv必须严格对应TT类型,子类型不能直接用宽泛类型覆盖,得靠细化TT的约束来实现协变的子类型替换逻辑。 - 协变
+TT本质是允许子类型的TT是父类型TT的子类型,对应到依赖类型里就是子特质要把TT的范围收窄(要么直接指定具体子类型,要么缩小上界)。
写法一:直接指定具体类型(完全匹配原泛型行为)
object AsDependentType { trait P { type TT val vv: TT } // 对应原代码的P[Product] trait P1 extends P { type TT = Product val vv: TT // 这里写TT和写Product效果一致,但严格对应父特质的类型约束 } // 对应原代码的P1 with P[Tuple1[Int]],因为Tuple1[Int]是Product的子类型,协变允许这样合并 trait P2 extends P1 { override type TT = Tuple1[Int] override val vv: TT } }
写法二:保留上界约束(贴近协变的“子类型兼容”逻辑)
object AsDependentType { trait P { type TT val vv: TT } trait P1 extends P { type TT <: Product val vv: TT // 必须写TT,不能直接写Product,否则会和父特质的vv类型不兼容 } trait P2 extends P1 { override type TT <: Tuple1[Int] // 把TT的上界缩小为Tuple1[Int](Product的子类型) override val vv: TT } }
错误原因分析
原来的报错代码里,P1把vv声明为Product,但父特质P的vv类型是P1.this.TT——而TT <: Product意味着TT可能是Product的某个子类型,这时候Product类型比TT更宽泛,不符合Scala的覆盖规则(子类成员的类型必须是父类成员类型的子类型或相等)。所以编译器会报错说类型不兼容。
内容的提问来源于stack exchange,提问作者tribbloid
相关产品推荐
相关产品推荐

