Scala 试图用Shapeless递归地将case类转换为异构列表的奇怪行为
我昨晚熬夜想弄明白这个不成形的问题,我担心如果我不把它从我的胸口说出来,它会吃掉我的夜晚,所以就这样吧 在这个最小化版本中,我只是定义了一个类型类,它将递归地将案例类转换为列表: 然后(请注意,我已经清理了类型语法,因为这不是一个完全不可读的混乱): 到目前为止还不错。但是:Scala 试图用Shapeless递归地将case类转换为异构列表的奇怪行为,scala,scala-macros,shapeless,Scala,Scala Macros,Shapeless,我昨晚熬夜想弄明白这个不成形的问题,我担心如果我不把它从我的胸口说出来,它会吃掉我的夜晚,所以就这样吧 在这个最小化版本中,我只是定义了一个类型类,它将递归地将案例类转换为列表: 然后(请注意,我已经清理了类型语法,因为这不是一个完全不可读的混乱): 到目前为止还不错。但是: scala> DeepHLister[D :: HNil] res3: DeepHLister[D :: HNil] { type Out = ((Int :: String :: HNil) :: B :: H
scala> DeepHLister[D :: HNil]
res3: DeepHLister[D :: HNil] {
type Out = ((Int :: String :: HNil) :: B :: HNil) :: HNil
} = DeepHLister$$anon$2@5b2ab49a
B
未被转换。如果我们打开-Xlog implicits
,这是最后一条消息:
<console>:25: this.DeepHLister.headCaseClassDeepHLister is not a valid implicit value for DeepHLister[shapeless.::[B,shapeless.HNil]] because:
hasMatchingSymbol reported error: diverging implicit expansion for type DeepHLister[this.Repr]
starting with method headNotCaseClassDeepHLister in trait LowPriorityDeepHLister
DeepHLister[D :: HNil]
^
:25:this.DeepHLister.headcaseClassedeephlister不是DeepHLister[shapeless:[B,shapeless.HNil]]的有效隐式值,因为:
hasMatchingSymbol报告的错误:类型DeepHLister[this.Repr]的发散隐式扩展
从trait LowPriorityDeepHLister中的方法headNotCaseClassDeepHLister开始
迪普利斯特[D::HNil]
^
这对我来说没有意义-headCaseClassDeepHLister
应该能够生成DeepHLister[B::HNil]
很好,如果你直接问它,它确实可以
这在2.10.4和2.11.2以及2.0.0版本和主版本中都会发生。我很确定这一定是个bug,但我不排除我做错了什么的可能性。以前有人见过这样的东西吗?我的逻辑是否有问题,或者缺少对Generic
的某些限制
好的,谢谢你的收听,也许现在我可以去读一本书或别的什么了。我采取了一种稍微不同的方法
trait CaseClassToHList[X] {
type Out <: HList
}
trait LowerPriorityCaseClassToHList {
implicit def caseClass[X](implicit gen: Generic[X]): CaseClassToHList[X] {
type Out = generic.Repr
} = null
}
object CaseClassToHList extends LowerPriorityCaseClassToHList {
type Aux[X, R <: HList] = CaseClassToHList[X] { type Out = R }
implicit def caseClassWithCaseClasses[X, R <: HList](
implicit toHList: CaseClassToHList.Aux[X, R],
nested: DeepHLister[R]): CaseClassToHList[X] {
type Out = nested.Out
} = null
}
trait DeepHLister[R <: HList] {
type Out <: HList
}
object DeepHLister {
implicit def hnil: DeepHLister[HNil] { type Out = HNil } = null
implicit def caseClassAtHead[H, T <: HList](
implicit head: CaseClassToHList[H],
tail: DeepHLister[T]): DeepHLister[H :: T] {
type Out = head.Out :: tail.Out
} = null
def apply[X <: HList](implicit d: DeepHLister[X]): d.type = null
}
现在,这与使用最新的shapeless-2.1.0-SNAPSHOT构建编写的内容大致相同,并且这个问题中的示例的一个近亲作为一个示例添加到了这里 原始版本的问题是,
泛型
的每次扩展都会在DeepHLister
类型类实例的隐式解析中引入一个新的HList
类型,原则上,可能产生一个HList
类型,该类型与以前在相同分辨率下看到的某些类型相关,但比它们更复杂。这种情况会使散度检查器跳闸并中止解析过程
关于为什么D
而不是C
会发生这种情况的确切细节隐藏在Scala的typechecker实现的细节中,但是,粗略地说,区别在于在C
的解析过程中,我们看到a
之前的B
(较大)因此,散度检测器很高兴我们的类型正在收敛;相反,在解析D
时,我们看到A
(较小)在B
(较大)之前,因此散度检查器(保守地)退出
shapeless 2.1.0中的修复方法是最近增强的
Lazy
类型构造函数和相关的隐式宏基础结构。这允许更多的用户控制发散,并支持使用隐式解析来构造递归隐式值,这对于自动为递归类型派生类型类实例的能力至关重要。在不成形的代码库中可以找到许多这样的例子,特别是返工的类型类派生基础结构和废弃的样板文件实现,它们不再需要专用的宏支持,而是完全按照泛型
和惰性
原语来实现。这些机制的各种应用可以在shapeless示例子项目中找到 我对DeepLister[B::HNil]
也有类似的问题。我5分钟前刚刚复制了代码,所以我将进一步研究。这对于白盒宏来说非常容易。@GuillaumeMassé,没错,当然,但目标是将宏的使用限制在Shapess提供的几个基本宏上。如果宏可以简化代码,为什么要限制宏的使用?我尽量不使用宏,因为它们使代码更难理解和维护。它们要求你具备宏观领域的知识,这是一个相当复杂的领域。这个域名可能会变得更容易理解。嘿@EECOLOR,谢谢-我一直想花更多的时间来研究这个域名,但没有意识到赏金即将到期。当我有时间的时候,我会再加上一笔赏金。对于shapeless 2.3.2有什么变化吗?示例没有编译,它抱怨2 head*隐式的返回类型。。。我用的是2.11.11如果你能用2.11.11重现一个错误,请在变形问题跟踪器上报告。
scala> DeepHLister[D :: HNil]
res3: DeepHLister[D :: HNil] {
type Out = ((Int :: String :: HNil) :: B :: HNil) :: HNil
} = DeepHLister$$anon$2@5b2ab49a
<console>:25: this.DeepHLister.headCaseClassDeepHLister is not a valid implicit value for DeepHLister[shapeless.::[B,shapeless.HNil]] because:
hasMatchingSymbol reported error: diverging implicit expansion for type DeepHLister[this.Repr]
starting with method headNotCaseClassDeepHLister in trait LowPriorityDeepHLister
DeepHLister[D :: HNil]
^
trait CaseClassToHList[X] {
type Out <: HList
}
trait LowerPriorityCaseClassToHList {
implicit def caseClass[X](implicit gen: Generic[X]): CaseClassToHList[X] {
type Out = generic.Repr
} = null
}
object CaseClassToHList extends LowerPriorityCaseClassToHList {
type Aux[X, R <: HList] = CaseClassToHList[X] { type Out = R }
implicit def caseClassWithCaseClasses[X, R <: HList](
implicit toHList: CaseClassToHList.Aux[X, R],
nested: DeepHLister[R]): CaseClassToHList[X] {
type Out = nested.Out
} = null
}
trait DeepHLister[R <: HList] {
type Out <: HList
}
object DeepHLister {
implicit def hnil: DeepHLister[HNil] { type Out = HNil } = null
implicit def caseClassAtHead[H, T <: HList](
implicit head: CaseClassToHList[H],
tail: DeepHLister[T]): DeepHLister[H :: T] {
type Out = head.Out :: tail.Out
} = null
def apply[X <: HList](implicit d: DeepHLister[X]): d.type = null
}
case class A(x: Int, y: String)
case class B(x: A, y: A)
case class C(b: B, a: A)
case class D(a: A, b: B)
object Test {
val z = DeepHLister[HNil]
val typedZ: DeepHLister[HNil] {
type Out = HNil
} = z
val a = DeepHLister[A :: HNil]
val typedA: DeepHLister[A :: HNil] {
type Out = (Int :: String :: HNil) :: HNil
} = a
val b = DeepHLister[B :: HNil]
val typedB: DeepHLister[B :: HNil] {
type Out = ((Int :: String :: HNil) :: (Int :: String :: HNil) :: HNil) :: HNil
} = b
val c = DeepHLister[C :: HNil]
val typedC: DeepHLister[C :: HNil] {
type Out = (((Int :: String :: HNil) :: (Int :: String :: HNil) :: HNil) :: (Int :: String :: HNil) :: HNil) :: HNil
} = c
val d = DeepHLister[D :: HNil]
val typedD: DeepHLister[D :: HNil] {
type Out = ((Int :: String :: HNil) :: ((Int :: String :: HNil) :: (Int :: String :: HNil) :: HNil) :: HNil) :: HNil
} = d
}