Объяснение для "незаконной циклической ссылки", связанной с последствиями
В моем проекте у меня есть тип 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
неявный класс, а не в какой-то момент до этого.