Объяснение для «незаконной циклической ссылки», связанной с последствиями - PullRequest
0 голосов
/ 21 февраля 2019

В моем проекте у меня есть тип 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 Ответ

0 голосов
/ 21 февраля 2019

Вы сталкиваетесь с 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 неявный класс, а не в какой-то момент до этого.

...