Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/jpa/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala 试图用Shapeless递归地将case类转换为异构列表的奇怪行为_Scala_Scala Macros_Shapeless - Fatal编程技术网

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
}