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