Объяснение для "незаконной циклической ссылки", связанной с последствиями

В моем проекте у меня есть тип A, используется для аргументов в нескольких местах, где я хочу, чтобы группа типов автоматически преобразовывалась в этот тип. Я реализовал это, используя несколько неявных классов в объекте-компаньоне A, Я удалил все, что не нужно, чтобы вызвать проблему:

trait A
object A {
  implicit class SeqA[T](v: Seq[T])(implicit x: T => A) extends A
  implicit class IntA(v: Int) extends A
  implicit class TupleA(v: (Int, Int)) extends SeqA(Seq(v._1, v._2))
}

Но scalac отклоняет этот код из-за недопустимой циклической ссылки:

$ scalac -version
Scala compiler version 2.12.8 -- Copyright 2002-2018, LAMP/EPFL and Lightbend, Inc.
$ scalac A.scala 
A.scala:5: error: illegal cyclic reference involving class TupleA
  implicit class TupleA(v: (Int, Int)) extends SeqA(Seq(v._1, v._2))
                 ^
one error found

В чем именно проблема здесь? Что компилятор связывает делать / выводить / решать, что связано с недопустимым циклом?

Бонусные баллы за правильный способ реализации этих неявных классов.

1 ответ

Вы сталкиваетесь с SI-9553, ошибкой компилятора, которому три с половиной года.

Причина, по которой ошибка не была исправлена, возможно, отчасти потому, что существует чрезвычайно простой обходной путь - просто поместите явный параметр типа в обобщенный класс, который вы расширяете:

trait A
object A {
  implicit class SeqA[T](v: Seq[T])(implicit x: T => A) extends A
  implicit class IntA(v: Int) extends A
  implicit class TupleA(v: (Int, Int)) extends SeqA[Int](Seq(v._1, v._2))
}

В любом случае, это, вероятно, хорошая идея, так как вы напрашиваетесь на неприятности каждый раз, когда позволяете выводить параметры типа там, где задействованы неявные определения.

В качестве примечания вы можете исследовать подобные проблемы с помощью опции компилятора, такой как -Xprint:typer, В этом случае он показывает следующее в REPL:

// ...
implicit class TupleA extends $line6.$read.$iw.$iw.A.SeqA[Int] {
  <paramaccessor> private[this] val v: (Int, Int) = _;
  def <init>(v: (Int, Int)): $line6.$read.$iw.$iw.A.TupleA = {
    TupleA.super.<init>(scala.collection.Seq.apply[Int](v._1, v._2))({
      ((v: Int) => A.this.IntA(v))
    });
    ()
  }
};
implicit <synthetic> def <TupleA: error>(v: (Int, Int)): <error> = new TupleA(v)

Что в этом случае не очень полезно, но, по крайней мере, это указывает на то, что проблема возникает в определении метода синтетического преобразования для TupleA неявный класс, а не в какой-то момент до этого.

Другие вопросы по тегам