Kittens自动推导Show时未复用NonEmptyList已有实例问题咨询
问题代码
package domain import cats.{Show, derived} import cats.data.NonEmptyList import cats.implicits._ case class Error(code: String, message: String) case class Errors(errors: NonEmptyList[Error]) object ShowStuffApp { // import NonEmptyList.catsDataShowForNonEmptyList implicit val show: Show[Errors] = derived.semiauto.show def main(args: Array[String]): Unit = { val errors = Errors(NonEmptyList.one(Error("1234", "Could not process"))) val res = errors.show println(res) } }
运行输出
Errors(errors = NonEmptyList(head = Error(code = 1234, message = Could not process), tail = Nil$()))
问题说明
这段代码运行后,Kittens自动为NonEmptyList推导了一个Show实例,而非使用通过cats.implicits._导入的官方隐式实例;就算取消注释内部的import NonEmptyList.catsDataShowForNonEmptyList也没用。但Kittens文档明确说自动推导时会尊重已有的隐式实例,该怎么解决?
解决方案
问题根源
Kittens的derived.semiauto.show在推导嵌套类型的Show实例时,会优先尝试为嵌套类型(这里是NonEmptyList[Error])自动推导实例,而不是优先查找当前作用域中已有的隐式——因为推导Errors的Show时,Error的Show实例也需要被推导,整个推导上下文的隐式查找逻辑和常规隐式查找优先级不同。
具体解决步骤
先为嵌套的
Error类型定义Show实例
确保Error的Show实例先存在于作用域中,这样推导NonEmptyList[Error]的Show时,会先找到Error的实例,再结合已有的NonEmptyList官方Show实例生成正确的实例。
可以自动推导:implicit val errorShow: Show[Error] = derived.semiauto.show也可以手动定义:
implicit val errorShow: Show[Error] = Show.show(e => s"Error(code=${e.code}, message=${e.message})")确保
NonEmptyList的官方Show实例在推导Errors时可见
在定义Errors的Show实例之前,明确导入NonEmptyList的Show实例,或者确保cats.implicits._的导入在推导代码之前。
修改后的完整代码
package domain import cats.{Show, derived} import cats.data.NonEmptyList import cats.implicits._ case class Error(code: String, message: String) // 先定义Error的Show实例 implicit val errorShow: Show[Error] = derived.semiauto.show case class Errors(errors: NonEmptyList[Error]) object ShowStuffApp { // 显式导入NonEmptyList的官方Show实例,确保推导时能找到 import NonEmptyList.catsDataShowForNonEmptyList implicit val errorsShow: Show[Errors] = derived.semiauto.show def main(args: Array[String]): Unit = { val errors = Errors(NonEmptyList.one(Error("1234", "Could not process"))) println(errors.show) } }
另一种显式指定的方式
如果不想依赖导入顺序,可以在推导Errors的Show时,显式传入NonEmptyList[Error]的隐式实例:
implicit val errorsShow: Show[Errors] = derived.semiauto.show(implicitly[Show[NonEmptyList[Error]]])
修改后效果
运行后会输出NonEmptyList官方Show实例的格式,比如类似:
Errors(errors = Error(code=1234, message=Could not process) :: Nil)
内容的提问来源于stack exchange,提问作者satyagraha

