为什么scalac会上升;发散性隐式扩展“;这里有错误吗?
在下面的代码中,我尝试使用shapeless派生typeclass实例。但是,对于更复杂的case类(转换为更复杂的HList),编译器会给我一个“发散隐式扩展”,即使它似乎不会两次解析同一类型的隐式类型。也许我遗漏了编译器的其他一些规则 (小提琴:) 这似乎有效。但是,对于2字段的case类,编译器无法执行类似的操作——因此我想知道:为什么我必须在这里使用为什么scalac会上升;发散性隐式扩展“;这里有错误吗?,scala,typeclass,implicit,shapeless,Scala,Typeclass,Implicit,Shapeless,在下面的代码中,我尝试使用shapeless派生typeclass实例。但是,对于更复杂的case类(转换为更复杂的HList),编译器会给我一个“发散隐式扩展”,即使它似乎不会两次解析同一类型的隐式类型。也许我遗漏了编译器的其他一些规则 (小提琴:) 这似乎有效。但是,对于2字段的case类,编译器无法执行类似的操作——因此我想知道:为什么我必须在这里使用Lazy,才能使它工作 TC[Trait2] Generic[Trait2] TC[ComplexClass :+: CNi
Lazy
,才能使它工作
TC[Trait2]
Generic[Trait2]
TC[ComplexClass :+: CNil]
TC[ComplexClass]
Generic[ComplexClass]
TC[String :: String :: HNil]
TC[CNil]
我已经创建了一些fiddle,这样您就可以直接在那里执行代码。几年前,当我在研究时,我发现找出差异检查器所做工作的最简单方法就是将一些
println
放入编译器并在本地发布。在2.12中,相关代码是占主导地位的
方法,我们可以将最后一行替换为如下内容:
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.::[String,shapeless.HNil]]] (complexity: 7)
TC[shapeless.:+:[ComplexClass,shapeless.:+:[Foo,shapeless.CNil]]] (complexity: 9)
因此,ComplexClass
hlist表示仍然与Trait2
coproduct表示重叠,但它并不占主导地位,因为Trait2
表示(我们担心的开放隐式TC
搜索的主题)现在更为复杂
“检查者”显然太偏执了,它的行为也太偏执了,但现在我们还是要面对它。正如您所注意到的,最直接、最简单的解决方法是在其中粘贴一个惰性
,以隐藏发散检查器中假定的递归
不过,在本例中,将实例放在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, H], t: TC[H]
): TC[T] = ???
}
为什么这样移动东西会增加副产品的复杂性?我不知道。我怀疑迈尔斯的答案就是解释,尽管在这个问题上,情况与我的不完全相同。关于犯这些错误的问题,请多解释一下:我没有看到这个问题,@SethTisue,但听起来似乎只是限制
-Xlog隐式
-Xlog>的输出,仅仅拥有当前消息的一个更具体的子集并不能真正帮助解决像这样奇怪的分歧情况。非常好的解释!尽管我们两人似乎都不明白为什么编译器如此偏执,但至少失败点现在已经非常清楚了。谢谢大家!@valenterry是的,不幸的是,我认为“为什么”可能只是“因为这种行为大多是未指定的,并且在某个点上,编译器开发人员认为大多数OK已经足够好了”。
Does not dominate:
TC[shapeless.::[String,shapeless.::[String,shapeless.HNil]]] (complexity: 7)
TC[shapeless.:+:[ComplexClass,shapeless.:+:[Foo,shapeless.CNil]]] (complexity: 9)
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, H], t: TC[H]
): TC[T] = ???
}
Does not dominate:
TC[shapeless.::[String,shapeless.::[String,shapeless.HNil]]] (complexity: 7)
TC[shapeless.:+:[ComplexClass,shapeless.CNil]] (complexity: 16)