Переполнение стека в классе типов с неявным преобразованием

Я сделал общий DynamoFormat за Scanamo что бы поставить любой объект, который имеет Circe"s Encoder а также Decoder определяется в базе данных как строка Json.

import com.gu.scanamo.DynamoFormat
import io.circe.parser.parse
import io.circe.syntax._
import io.circe.{Decoder, Encoder}

object JsonDynamoFormat {    
  def forType[T: Encoder: Decoder]: DynamoFormat[T] = DynamoFormat.coercedXmap[T, String, Exception] {
    s => parse(s).flatMap(_.as[T]).fold(err => throw err, obj => obj)
  } {
    obj => obj.asJson.noSpaces
  }
}

Затем я добавил неявное преобразование (к тому же object JsonDynamoFormat) автоматически предоставлять эти форматеры.

implicit def jsonToFormat[T: Encoder: Decoder]: DynamoFormat[T] = JsonDynamoFormat.forType[T]

Когда я импортирую его, компилятор разрешает форматирование успешно, однако во время выполнения я получаю переполнение стека в JsonDynamoFormatгде звонки jsonToFormat а также forType чередовать бесконечно:

Exception in thread "main" java.lang.StackruError
    at JsonDynamoFormat$.forType(JsonDynamoFormat.scala:12)
    at JsonDynamoFormat$.jsonToFormat(JsonDynamoFormat.scala:9)
    at JsonDynamoFormat$.forType(JsonDynamoFormat.scala:13)
    at JsonDynamoFormat$.jsonToFormat(JsonDynamoFormat.scala:9)
    ...

Я не могу понять, что здесь происходит. Кто-нибудь может пролить свет на это?

1 ответ

Решение

Отладка ошибок Scala может быть довольно сложной задачей. Вот несколько советов, которые могут помочь:

  • включить scalacOptions ++= Seq("-Xlog-implicits") опция компилятора. Это напечатает неявный журнал поиска, и может быть полезно понять, где именно неявные разрывы цепей.

  • Добавить сплайн libraryDependencies ++= Seq(compilerPlugin("io.tryp" %% "splain" % "0.2.4")) улучшить неявную читаемость журнала отладки.

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

Обычно такая ситуация распознается во время компиляции, и компиляция выдает ошибку "расходящихся имплицитов", но эта ошибка может быть ложноположительной, и поэтому авторы библиотеки обычно обходят ее, используя такую ​​технику, как Lazy класс из бесформенного. Однако в случае фактического ошибочного циклического следствия это приведет к ошибке времени выполнения, а не к ошибке времени компиляции.

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