Как в scala указать компилятору реализовать эквивалентность двух абстрактных типов?

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

Ясно, что компилятор scala облажался в этих случаях: если я перейду:

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

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


person tribbloid    schedule 19.06.2020    source источник
comment
Примечание: это правильно работает в Dotty.   -  person HTNW    schedule 20.06.2020
comment
об этом следует сообщить здесь: github.com/scala/bug/issues   -  person Dmytro Mitin    schedule 20.06.2020
comment
Большое спасибо @Dmytro Mitin. Хорошая мысль, выложу туда   -  person tribbloid    schedule 21.06.2020
comment
Выполнено! github.com/scala/bug/issues/12046   -  person tribbloid    schedule 21.06.2020


Ответы (1)


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

No.

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, просто имеет общий тип 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]
person Willis Blackburn    schedule 20.06.2020
comment
Мне все равно, если это строка, она разрешается в a.out.S -> a.this.S -> a.S. Здесь, я думаю, все согласны с тем, что компилятор scala потерпел неудачу. Мне просто нужно знать, какой алгоритм приводит к такому сбою и есть ли обходной путь для этого. - person tribbloid; 20.06.2020
comment
Можете ли вы использовать предложение Out#SS? - person Willis Blackburn; 20.06.2020