通过隐式测试两个 scala 无形状 HList 类型的等效性



我有兴趣测试两个HList异构记录是否"等效";也就是说,它们具有相同的键/值对,但不一定具有相同的顺序。是否有预定义的类型谓词可以执行下面的代码片段中EquivHLists执行的操作?

// shapeless heterogeneous records with "equivalent" types.
// these should compile if given as the arguments to 'f' below.
val hrec1 = ("a" ->> 1) :: ("b" ->> 2) :: HNil
val hrec2 = ("b" ->> 2) :: ("a" ->> 1) :: HNil
// only compiles if two HList records contain same information
def f(hr1: H1 <: HList, hr2 : H2 <: HList)(implicit equiv: EquivHLists[H1, H2]) = {
// biz logic
}

我相信Align[M,L]typeclass 支持您想要的内容,它允许您重新排列一个 hlist 的元素以匹配另一个具有相同类型的顺序。

这是一个我认为可以做你想要的功能。 它会告诉您两个等效的 hlist 是否对每种类型具有相同的值。 如果两个列表的类型不同,则不会编译。

import shapeless._
import ops.hlist._
def equiv[H <: HList, L <: HList]
(h : H, l : L)(implicit align: Align[H, L]): Boolean = align(h) == l
scala> equiv(3 :: "hello" :: HNil, "hello" :: 3 :: HNil)
res11: Boolean = true
scala> equiv(4 :: "hello" :: HNil, "hello" :: 3 :: HNil)
res12: Boolean = false
scala> equiv(4 :: "hello" :: HNil, "hello" :: 3.0 :: HNil)
<console>:19: error: could not find implicit value for parameter align: shapeless.ops.hlist.Align[Int :: String :: shapeless.HNil,String :: Double :: shapeless.HNil]

编辑:经过一些进一步的实验,如果 hlist 有多个相同类型的值,这将给出漏报:

scala> equiv(3 :: "hello" :: 4 :: HNil, 4 :: "hello" :: 3 :: HNil)
res14: Boolean = false

这是因为Align的工作方式:它基本上只是迭代一个hlist,然后用相同的类型提取另一个元素的第一个元素。 但是,如果您使用的是单例类型文字,那么这应该不是问题。

因此,至少在键方面,这确实适用于上述记录:

scala> equiv(hrec1, hrec2)
res16: Boolean = true
//change one of the keys
scala> val hrec3 = ("c" ->> 2) :: ("a" ->> 1) :: HNil
hrec3: Int with shapeless.labelled.KeyTag[String("c"),Int] :: Int with shapeless.labelled.KeyTag[String("a"),Int] :: shapeless.HNil = 2 :: 1 :: HNil
scala> equiv(hrec1, hrec3)
<console>:27: error: could not find implicit value for parameter align ...
//change one of the values, it compiles but returns false
scala> val hrec4 = ("b" ->> 2) :: ("a" ->> 3) :: HNil
hrec4: Int with shapeless.labelled.KeyTag[String("b"),Int] :: Int with shapeless.labelled.KeyTag[String("a"),Int] :: shapeless.HNil = 2 :: 3 :: HNil
scala> equiv(hrec1, hrec4)
res18: Boolean = false
def f(hr1: H1 <: HList, hr2 : H2 <: HList)(implicit equiv: H1 =:= H2) = {
// biz logic
}

我相信这应该做你想做的事,你试过吗?

最新更新