在scala中,如何指示编译器实现两个抽象类型的等价?

tri*_*oid 7 scala type-inference abstract-type path-dependent-type dependent-type

我有一个简单的案例来测试scala的类型推断能力:

    trait Super1[S] {

      final type Out = this.type
      final val out: Out = this
    }

    trait Super2[S] extends Super1[S] {

      final type SS = S
    }

    case class A[S](k: S) extends Super2[S] {}

    val a = A("abc")

    implicitly[a.type =:= a.out.type]
    // success

    implicitly[a.Out =:= a.out.Out]
    // success

    implicitly[a.SS =:= a.out.SS]
    implicitly[a.SS <:< a.out.SS]
    implicitly[a.out.SS <:< a.SS]
    // oops
Run Code Online (Sandbox Code Playgroud)

最后 4 行的错误如下所示:

[Error] /home/peng/git-spike/scalaspike/common/src/test/scala/com/tribbloids/spike/scala_spike/AbstractType/InferInheritance.scala:28: Cannot prove that a.SS =:= Super2.this.SS.
[Error] /home/peng/git-spike/scalaspike/common/src/test/scala/com/tribbloids/spike/scala_spike/AbstractType/InferInheritance.scala:29: Cannot prove that a.SS <:< Super2.this.SS.
[Error] /home/peng/git-spike/scalaspike/common/src/test/scala/com/tribbloids/spike/scala_spike/AbstractType/InferInheritance.scala:30: Cannot prove that Super2.this.SS <:< a.SS.
three errors found
Run Code Online (Sandbox Code Playgroud)

显然 Scala 编译器在这些情况下搞砸了:如果我移动:

      final type Out = this.type
      final val out: Out = this
Run Code Online (Sandbox Code Playgroud)

Super2它下面会编译成功。我的问题是:为什么现状推理算法在这种情况下不起作用?以及如何重写我的代码来规避编译器的这个问题?

Wil*_*urn 2

如果a.SS =:= a.out.SS那么我们应该能够使用其中之一来代替另一个。是这样吗?

不。

val x: a.SS = "hello"
val y: a.out.SS = "hello"
Run Code Online (Sandbox Code Playgroud)

编译这个我得到:

ScalaFiddle.scala:29: error: type mismatch;
 found   : lang.this.String("hello")
 required: Super2.this.SS
    (which expands to)  S
  val y: a.out.SS = "hello"
Run Code Online (Sandbox Code Playgroud)

看起来 Scalaa.SS确实明白这一点String,这并不奇怪。

但显然a.out.SS不是String,而是…… S

奇怪的是,尽管这显然是错误的,但它仍然有效:

val b = A(1)
implicitly[a.out.SS =:= b.out.SS]
Run Code Online (Sandbox Code Playgroud)

如果您只是定义out为类型,this.type那么您的代码就可以工作。

我能想到的最好的办法是,在定义 的类型时out,Scala 无法将 所表示的实际类型关联起来S,因此通过查看的out类型只有泛型类型。SSoutS

该类型Out似乎确实理解 所表示的实际类型S,因此这是有效的:

implicitly[a.SS =:= a.Out#SS]
implicitly[a.SS <:< a.Out#SS]
implicitly[a.Out#SS <:< a.SS]
Run Code Online (Sandbox Code Playgroud)

并且这正确地无法编译:

val b = A(1)
implicitly[a.Out#SS =:= b.Out#SS]
Run Code Online (Sandbox Code Playgroud)