加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 用Shapeless递归地将案例类转换为异构列表的奇怪行为

发布时间:2020-12-16 09:10:45 所属栏目:安全 来源:网络整理
导读:昨天晚上我睡得太晚了,试图弄清楚这个无泪的问题,如果我没有把我的胸部放下,我恐怕会吃晚饭,所以在这里. 在这个最小化的版本中,我只是定义一个类型类,它会将案例类递归转换为heterogeneous列表: import shapeless._trait DeepHLister[R : HList] extends De
昨天晚上我睡得太晚了,试图弄清楚这个无泪的问题,如果我没有把我的胸部放下,我恐怕会吃晚饭,所以在这里.

在这个最小化的版本中,我只是定义一个类型类,它会将案例类递归转换为heterogeneous列表:

import shapeless._

trait DeepHLister[R <: HList] extends DepFn1[R] { type Out <: HList }

trait LowPriorityDeepHLister {
  type Aux[R <: HList,Out0 <: HList] = DeepHLister[R] { type Out = Out0 }

  implicit def headNotCaseClassDeepHLister[H,T <: HList](implicit
    dht: DeepHLister[T]
  ): Aux[H :: T,H :: dht.Out] = new DeepHLister[H :: T] {
    type Out = H :: dht.Out
    def apply(r: H :: T) = r.head :: dht(r.tail)
  }
}

object DeepHLister extends LowPriorityDeepHLister {
  implicit object hnilDeepHLister extends DeepHLister[HNil] {
    type Out = HNil
    def apply(r: HNil) = HNil
  }

  implicit def headCaseClassDeepHLister[H,R <: HList,T <: HList](implicit
    gen: Generic.Aux[H,R],dhh: DeepHLister[R],dht: DeepHLister[T]
  ): Aux[H :: T,dhh.Out :: dht.Out] = new DeepHLister[H :: T] {
    type Out = dhh.Out :: dht.Out
    def apply(r: H :: T) = dhh(gen.to(r.head)) :: dht(r.tail)
  }

  def apply[R <: HList](implicit dh: DeepHLister[R]): Aux[R,dh.Out] = dh
}

让我们试试吧!首先我们需要一些案例类:

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)

然后(注意,我已经清理了类型语法,因为这不是一个完全不可读的混乱):

scala> DeepHLister[A :: HNil]
res0: DeepHLister[A :: HNil]{
  type Out = (Int :: String :: HNil) :: HNil
} = DeepHLister$$anon$2@634bf0bf

scala> DeepHLister[B :: HNil]
res1: DeepHLister[B :: HNil] {
  type Out = (
    (Int :: String :: HNil) :: (Int :: String :: HNil) :: HNil
  ) :: HNil
} = DeepHLister$$anon$2@69d6b3e1

scala> DeepHLister[C :: HNil]
res2: DeepHLister[C :: HNil] {
  type Out = (
    ((Int :: String :: HNil) :: (Int :: String :: HNil) :: HNil) ::
    (Int :: String :: HNil) ::
    HNil
  ) :: HNil
} = DeepHLister$$anon$2@4d062faa

到现在为止还挺好.但是之后:

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]
                         ^

这对我来说没有意义 – headCaseClassDeepHLister应该能够生成DeepHLister [B :: HNil]就好了,如果你直接问它.

这种情况发生在2.10.4和2.11.2上,同时使用2.0.0版本和master.我很确定这必须是一个错误,但我并不排除我做错事的可能性.有没有人看到过这样的事情?我的逻辑有什么问题,还是一般限制我错过了?

好的,谢谢你的听力 – 也许现在我可以去读一本书或者一些东西.

解决方法

现在这个工作或多或少是使用最近的无形-2.1.0-SNAPSHOT版本编写的,而这个问题中的样本的一个亲密关系已经被添加到了 example.

原来的问题是,Generic的每个扩展都会将一个新的HList类型引入到DeepHLister类型类实例的隐式解析中,并且原则上可以产生一个HList类型,与之前所见过的类型相关但更复杂同样的决议.该条件跳过散点检查器并中止解析过程.

为什么这种情况发生在D而不是C的确切细节潜伏在Scala的类型检查器的实现细节上,但是,粗略的近似是,在C的解决方案中,我们看到A(A) (较小),所以分歧检查器很高兴我们的类型是收敛的;相反,在D的分辨率期间,我们看到B(较大)之前的A(较小),因此发散检验器(保守地)保留.

这种在无形2.1.0中的修复是最近增强的Lazy类型构造函数和相关的隐式宏基础设施.这允许更多的用户控制发散,并支持使用隐式解析来构造递归隐含值,这对递归类型自动导出类型类实例的能力至关重要.可以在无形代码库中找到许多示例,特别是返工类型派生基础架构和Scrap Your Boilerplate实现,这不再需要专门的宏支持,而是完全根据Generic和Lazy原语来实现.这些机制的各种应用可以在无形的示例子项目中找到.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读