为什么scalac在这里出现“分歧的隐式扩展”错误?
在下面的代码中,我尝试使用shapeless派生类型类实例.但是,在更复杂的案例类(转换为更复杂的HList)的情况下,编译器给我一个“分歧的隐式扩展”,即使它似乎没有两次解析相同类型的隐式类型.也许我错过了编译器的其他一些规则?
(小提琴:https://scalafiddle.io/sf/WEpnAXN/0) import shapeless._ trait TC[T] sealed trait Trait1 case class SimpleClass(a: String) extends Trait1 sealed trait Trait2 case class ComplexClass(a: String,b: String) extends Trait2 object Serialization extends App { //Instances for HList implicit val hnilInstance: TC[HNil] = ??? implicit def hconsInstance[H,T <: HList] (implicit t: TC[T]): TC[H :: T] = ??? //Instances for CoProduct implicit val cnilInstance: TC[CNil] = ??? implicit def cconsInstance[H,T <: Coproduct] (implicit h: TC[H],t: TC[T]): TC[H :+: T] = ??? //Instances for Generic,relying on HNil & HCons implicit def genericInstance[T,H] (implicit g: Generic.Aux[T,H],t: TC[H]): TC[T] = ??? the[TC[SimpleClass :+: CNil]] //Works the[TC[Trait1]] //Works the[TC[ComplexClass :+: CNil]] //Works the[TC[Trait2]] //Fails with diverging implicit expansion } 在尝试解析[TC [Trait1]]时,编译器应该执行以下操作: TC[Trait1] Generic[Trait1] TC[SimpleClass :+: CNil] TC[SimpleClass] Generic[SimpleClass] TC[String :: HNil] TC[CNil] 这似乎工作.但是,对于2字段的case类,编译器无法做到这样的事情 – 所以我想知道:为什么我必须在这里使用Lazy才能使它工作? TC[Trait2] Generic[Trait2] TC[ComplexClass :+: CNil] TC[ComplexClass] Generic[ComplexClass] TC[String :: String :: HNil] TC[CNil] 我已经创建了一些小提琴,因此您可以直接执行代码. 解决方法
几年前,当我在
some issues like this工作时,我发现找出分歧检查器所做的最简单的方法就是将一些printlns抛入编译器并在本地发布.在2.12中,相关代码是主导方法
here,我们可以用这样的方式替换最后一行:
overlaps(dtor1,dted1) && (dtor1 =:= dted1 || { val dtorC = complexity(dtor1) val dtedC = complexity(dted1) val result = dtorC > dtedC println(if (result) "Dominates:" else "Does not dominate:") println(s"$dtor (complexity: $dtorC)") println(s"$dted (complexity: $dtedC)") println("===========================") result }) 然后我们可以sbt publishLocal scalac并尝试编译你的代码: Dominates: TC[shapeless.::[String,shapeless.::[String,shapeless.HNil]]] (complexity: 7) TC[shapeless.:+:[ComplexClass,shapeless.CNil]] (complexity: 6) =========================== 这里的问题是我们正在为String :: String :: HNil(树中最低的节点)寻找一个TC实例,但是我们对ComplexClass :: CNil进行了一次开放搜索(三步).编译器认为String :: String :: HNil重叠并支配ComplexClass :: CNil,并且它会因为看起来递归而失败. 这听起来很荒谬,所以我们可以做一个实验,试图通过在副产品部分增加一些复杂性并看看会发生什么来说服自己.我们只需添加一个构造函数: case class Foo(i: Int) extends Trait2 现在一切正常,我们在编译期间收到此消息: Does not dominate: TC[shapeless.::[String,shapeless.:+:[Foo,shapeless.CNil]]] (complexity: 9) 因此,ComplexClass hlist表示仍然与Trait2 coproduct表示重叠,但它并不支配它,因为Trait2表示(我们担心的开放隐式TC搜索的主题)现在更加复杂. 检查员在这里显然太偏执了,它的行为may change in the future,但是现在我们一直坚持下去.正如您所注意到的,最直接和最简单的解决方法是将Lazy放在那里以隐藏来自分歧检查器的假定递归. 但在具体情况下,看起来只是将实例放在TC伴侣对象中也是如此: import shapeless._ sealed trait Trait1 case class SimpleClass(a: String) extends Trait1 sealed trait Trait2 case class ComplexClass(a: String,b: String) extends Trait2 trait TC[T] object TC { //Instances for HList implicit def hnilInstance: TC[HNil] = ??? implicit def hconsInstance[H,T <: HList](implicit t: TC[T]): TC[H :: T] = ??? //Instances for CoProduct implicit def cnilInstance: TC[CNil] = ??? implicit def cconsInstance[H,T <: Coproduct](implicit h: TC[H],t: TC[T] ): TC[H :+: T] = ??? //Instances for Generic,relying on HNil & HCons implicit def genericInstance[T,H](implicit g: Generic.Aux[T,t: TC[H] ): TC[T] = ??? } 然后: Does not dominate: TC[shapeless.::[String,shapeless.CNil]] (complexity: 16) 为什么像这样移动东西会增加副产品的复杂性?我不知道. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |