我正在使用Shapeless,并具有以下方法来计算两个HList之间的差异:

  def diff[H <: HList](lst1: H, lst2:H):List[String] = (lst1, lst2) match {
    case (HNil, HNil)                 => List()
    case (h1::t1, h2::t2) if h1 != h2 => s"$h1 -> $h2" :: diff(t1, t2)
    case (h1::t1, h2::t2)             => diff(t1, t2)
    case _                            => throw new RuntimeException("something went very wrong")
  }


由于该方法的两个参数均采用H,因此我希望此处不编译不同类型的HList。例如:

diff("a" :: HNil, 1 :: 2 :: HNil)


不应该编译,但是可以编译,并且会生成运行时错误:java.lang.RuntimeException: something went very wrong。我可以对类型参数做些什么,使该方法只接受具有相同类型的两侧?

最佳答案

不幸的是,基本的HList特征是未参数化的,因此在您的方法调用中,H只是解析为Hlist(这实际上是任何Hlist的超类型,而与具体的元素类型无关)。
为了解决这个问题,我们必须对定义进行一些更改,而要依靠广义类型约束:

def diff[H1 <: HList, H2 <: HList](lst1: H1, lst2: H2)(implicit e: H1 =:= H2): List[String] = (lst1, lst2) match {
  case (HNil, HNil)                 => List()
  case (h1::t1, h2::t2) if h1 != h2 => s"$h1 -> $h2" :: diff(t1, t2)
  case (h1::t1, h2::t2)             => diff(t1, t2)
  case _                            => throw new RuntimeException("something went very wrong")
}


让我们检查:

scala> diff("a" :: HNil, 1 :: 2 :: HNil)
<console>:12: error: Cannot prove that shapeless.::[String,shapeless.HNil] =:= shapeless.::[Int,shapeless.::[Int,shapele
              diff("a" :: HNil, 1 :: 2 :: HNil)
                  ^

scala> diff("a" :: HNil, "b" :: HNil)
res5: List[String] = List(a -> b)

scala> diff("a" :: 1 :: HNil, "b" :: 2 :: HNil)
res6: List[String] = List(a -> b, 1 -> 2)


现在我们仍然可以“作弊”,并将H1和H2显式设置为HList,然后回到平方。

scala> diff[HList, HList]("a" :: HNil, 1 :: 2 :: HNil)
java.lang.RuntimeException: something went very wrong
  at .diff(<console>:15)
  at .diff(<console>:13)


不幸的是,我认为这不容易解决(虽然确实如此,但是我没有快速解决方案)。

07-24 09:47
查看更多