使用Shapeless.LabelledGeneric推导CoProduct实例时缺少隐式值
解决Shapeless中使用LabelledGeneric推导CoProduct实例的编译错误
我最近在学习Shapeless框架时踩了个坑:尝试用LabelledGeneric推导Coproduct实例时,不管是在IDE里编写还是用SBT编译,都会出现编译错误。下面是能直接复现问题的完整代码(补全了缺失的导入部分):
import shapeless.{ :+:, ::, CNil, Coproduct, HList, HNil, Inl, Inr, LabelledGeneric, Lazy, Witness } import shapeless.labelled.{FieldType, field} // 定义密封ADT sealed trait Animal case class Cat(name: String, age: Int) extends Animal case class Dog(breed: String, isGoodBoy: Boolean) extends Animal // 尝试推导LabelledGeneric实例 object AnimalGeneric { val gen = LabelledGeneric[Animal] }
可能的错误原因&解决方案
这类编译错误通常由以下几个原因导致,对应修复方式如下:
Shapeless与Scala版本不兼容
Shapeless对Scala版本匹配要求严格,比如Scala 2.13.x需要搭配Shapeless 2.3.10及以上版本,Scala 2.12对应2.3.7左右。版本不匹配会直接导致编译器无法派生实例。
修复:在build.sbt中设置正确依赖:libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10" // 适配Scala 2.13.x缺少自动推导的导入
Shapeless需要显式导入shapeless.auto._,才能自动为ADT和case class派生LabelledGeneric实例,否则编译器找不到对应的隐式值。
修复:在代码顶部添加导入:import shapeless.auto._编译器无法自动推导复杂实例,需用Lazy延迟推导
对于层级稍复杂的ADT,编译器的隐式推导可能提前终止,这时可以用Lazy延迟实例解析,帮助编译器完成推导。
修复:修改代码为:object AnimalGeneric { val gen = Lazy[LabelledGeneric[Animal]].value }ADT定义不规范
确保ADT是sealed修饰的密封特质/类,且所有子类都是case class或case object——Shapeless只能为密封ADT推导Coproduct实例,非密封ADT无法确定所有子类,自然无法生成对应Coproduct类型。
修复后的完整代码
整合所有修复点后,代码就能正常编译了:
import shapeless.{ :+:, ::, CNil, Coproduct, HList, HNil, Inl, Inr, LabelledGeneric, Lazy, Witness } import shapeless.labelled.{FieldType, field} import shapeless.auto._ sealed trait Animal case class Cat(name: String, age: Int) extends Animal case class Dog(breed: String, isGoodBoy: Boolean) extends Animal object AnimalGeneric { val gen = LabelledGeneric[Animal] // 此时gen的Repr类型为:FieldType[Witness.`"Cat"`.T, Cat] :+: FieldType[Witness.`"Dog"`.T, Dog] :+: CNil }
内容的提问来源于stack exchange,提问作者Som Bhattacharyya
相关产品推荐
相关产品推荐

