Почему извлечение и присвоение отдельных значений кортежей вызывает рекурсивный неявный поиск? - PullRequest
0 голосов
/ 18 мая 2018

Вот код для создания этой ошибки:

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 вызывает ошибку компилятора при неявном поиске.Может кто-нибудь сказать мне, почему это происходит или что вызывает поведение?Мне было бы интересно узнать немного больше о том, что я вижу в выходных данных отладки.Почему определение типа каждой отдельной вещи внутри кортежа вызывает ошибку компилятора, а разрешение типа общего кортежа не вызывает никаких проблем?

...