Вот код для создания этой ошибки:
build.sbt
scalaVersion := "2.11.7"
libraryDependencies ++= Seq(
"ai.x" %% "safe" % "0.1.0"
)
scalacOptions := Seq("-Ytyper-debug") // Only add this if you want to see a bunch of stuff
test.scala
import ai.x.safe._
package object foo {
final implicit val (enc, dec) = {
("x" === "y") -> 0
}
}
Попытка скомпилировать это вызовет эту ошибку:
[info] Compiling 1 Scala source to /tmp/test/target/scala-2.11/classes...
[error] /tmp/test/test.scala:4: recursive value x$1 needs type
[error] final implicit val (enc, dec) = {
[error] ^
[error] one error found
При включенном режиме полной отладки я вижу, что он пытается разрешить ===
, и компилятор просматривает текущие значения, чтобы определить,любой матч.Поскольку (enc, dec)
являются неявными, кажется, что они их тоже используют, и поэтому он пытается их напечатать, вызывая эту неявную рекурсию, на которую жалуется компилятор.
| | | | | |-- "x".$eq$eq$eq("y") EXPRmode-POLYmode-QUALmode (silent: value x$1 in package)
| | | | | | |-- "x".$eq$eq$eq BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value x$1 in package)
| | | | | | | |-- "x" EXPRmode-POLYmode-QUALmode (silent: value x$1 in package)
| | | | | | | | \-> String("x")
| | | | | | | |-- x$1._1 EXPRmode (site: value enc in package)
| | | | | | | | |-- x$1 EXPRmode-POLYmode-QUALmode (site: value enc in package)
| | | | | | | | | caught scala.reflect.internal.Symbols$CyclicReference: illegal cyclic reference involving value x$1: while typing x$1
[error] /tmp/test/test.scala:4: recursive value x$1 needs type
[error] final implicit val (enc, dec) = {
[error] ^
| | | | | | | | | \-> <error>
| | | | | | | | \-> <error>
| | | | | | | |-- x$1._2 EXPRmode (site: value dec in package)
| | | | | | | | |-- x$1 EXPRmode-POLYmode-QUALmode (site: value dec in package)
| | | | | | | | | \-> <error>
| | | | | | | | \-> <error>
| | | | | | | |-- SafeEquals BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value x$1 in package) implicits disabled
| | | | | | | | |-- ai.x.safe.`package` EXPRmode-POLYmode-QUALmode (silent: value x$1 in package) implicits disabled
| | | | | | | | | \-> ai.x.safe.type
| | | | | | | | \-> ai.x.safe.SafeEquals.type <and> [T](l: T)ai.x.safe.SafeEquals[T]
| | | | | | | solving for (T: ?T)
| | | | | | | solving for (T: ?T)
| | | | | | | solving for (T: ?T)
| | | | | | | [adapt] SafeEquals adapted to [T](l: T)ai.x.safe.package.SafeEquals[T] based on pt String("x") => ?{def ===: ?}
| | | | | | | |-- [T](l: T)ai.x.safe.package.SafeEquals[T] EXPRmode-POLYmode-QUALmode (silent: value x$1 in package)
| | | | | | | | \-> ai.x.safe.package.SafeEquals[String]
| | | | | | | |-- ai.x.safe.`package`.SafeEquals[String]("x").$eq$eq$eq BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value x$1 in package)
| | | | | | | | \-> (r: String)Boolean
| | | | | | | \-> (r: String)Boolean
| | | | | | |-- "y" : pt=String BYVALmode-EXPRmode (silent: value x$1 in package)
| | | | | | | \-> String("y")
| | | | | | \-> Boolean
Конечно, я могу заставить его скомпилироватьчто-то вроде этого:
final val (x,y) = {
("x" === "y") -> 0
}
implicit val (f,b) = (x,y)
Поскольку при определении тела {}
импликации не существует, они не мешают неявному поиску компилятора при обнаружении этого ===
из SafeEquals
можно применить к String
и заставить код работать.Теперь у меня действительно нет проблем с этим, потому что это имеет смысл, поскольку можно определить ленивые рекурсивные сериализаторы и другие неявные вещи, которые используют себя без проблем.Поэтому, конечно, компилятор должен смотреть на неявное определение, которое определяется как возможное приложение, чтобы заставить что-то работать.
Но странным для меня является то, что это работает, если вы не извлекаете кортеж непосредственно во время назначения:
final implicit val tuple = {
("x" === "y") -> 0
}
Очевидно, что это не то, что я хочу сделать, поскольку я хочу, чтобы обе вещи в кортеже были неявными (в моем реальном случае это пара кодер / декодер из circe).Но мне просто странно, что использование (как мне кажется,) экстрактора для Tuple2 вызывает ошибку компилятора при неявном поиске.Может кто-нибудь сказать мне, почему это происходит или что вызывает поведение?Мне было бы интересно узнать немного больше о том, что я вижу в выходных данных отладки.Почему определение типа каждой отдельной вещи внутри кортежа вызывает ошибку компилятора, а разрешение типа общего кортежа не вызывает никаких проблем?