Как в scala указать компилятору реализовать эквивалентность двух абстрактных типов? - PullRequest
7 голосов
/ 19 июня 2020

У меня есть простой случай для проверки возможности вывода типа 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

Ошибка для последних 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

Очевидно * Компилятор 1013 * облажался в этих случаях: если я перейду:

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

в Super2, он будет успешно компилироваться. У меня такой вопрос: почему алгоритм вывода статус-кво в этом случае не работает? и как я могу переписать свой код, чтобы обойти эту проблему компилятора?

1 Ответ

2 голосов
/ 20 июня 2020

Если a.SS =:= a.out.SS, тогда мы сможем использовать одно вместо другого. Это так?

Нет.

val x: a.SS = "hello"
val y: a.out.SS = "hello"

Компилируя это, я получаю:

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"

Похоже, Scala понимает, что a.SS действительно String, что неудивительно.

Но ясно, что a.out.SS не String, а ... S.

Как ни странно, это работает, хотя это явно неверно:

val b = A(1)
implicitly[a.out.SS =:= b.out.SS]

Если вы просто определите out как тип this.type, тогда ваш код будет работать.

Лучшее, что я могу придумать, это то, что при определении типа out, Scala не может связать фактический тип, представленный S, с out, поэтому тип SS при просмотре через out просто имеет общий c тип S.

Тип Out, похоже, понимает фактический тип, представленный S, поэтому это работает:

implicitly[a.SS =:= a.Out#SS]
implicitly[a.SS <:< a.Out#SS]
implicitly[a.Out#SS <:< a.SS]

, и это правильно не компилируется:

val b = A(1)
implicitly[a.Out#SS =:= b.Out#SS]
...