带有隐式转换的类型类中的堆栈溢出

时间:2017-08-25 11:43:27

标签: scala implicit-conversion typeclass circe

我为DynamoFormat制作了一个通用Scanamo,它将Circe EncoderDecoder定义的任何对象作为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中出现堆栈溢出,其中对jsonToFormatforType的调用无限地交替:

Exception in thread "main" java.lang.StackOverflowError
    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 个答案:

答案 0 :(得分:2)

调试Scala暗示错误可能非常麻烦。以下是一些可以提供帮助的建议:

  • 启用scalacOptions ++= Seq("-Xlog-implicits")编译器选项。这将打印隐式搜索日志,并且可以用于了解隐式链断开的确切位置。

  • 添加splain libraryDependencies ++= Seq(compilerPlugin("io.tryp" %% "splain" % "0.2.4"))以提高隐式调试日志的可读性。

通常,在运行时使用通常派生的类型类的堆栈溢出是错误的隐式解析的标志。这通常意味着编译器发现了一些循环相关的含义,并使用其中一个来满足另一个含义,反之亦然。

通常情况下,这种情况在编译时被识别,并且编译会产生"不同的含义"错误,但该错误可能是误报,因此库作者通常使用类似Lazy类型的技术来规避它。但是,如果出现实际错误的循环错误,这将导致运行时错误,而不是编译时错误。