通过隐式测试两个 Scala 无形 HList 类型的等价性
Test two scala shapeless HList types for equivalence via implicit
我有兴趣测试两个HList异构记录是否"equivalent";也就是说,它们具有相同的 key/val 对,但顺序不一定相同。是否有一个预定义的类型谓词可以执行下面代码片段中 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
}
def f(hr1: H1 <: HList, hr2 : H2 <: HList)(implicit equiv: H1 =:= H2) = {
// biz logic
}
我相信这应该做你想要的,你试过了吗?
我相信 Align[M,L]
类型类支持您想要的,它允许您重新排列一个 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]
edit :经过进一步实验,如果 hlists 具有多个相同类型的值,这将给出假阴性:
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
我有兴趣测试两个HList异构记录是否"equivalent";也就是说,它们具有相同的 key/val 对,但顺序不一定相同。是否有一个预定义的类型谓词可以执行下面代码片段中 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
}
def f(hr1: H1 <: HList, hr2 : H2 <: HList)(implicit equiv: H1 =:= H2) = {
// biz logic
}
我相信这应该做你想要的,你试过了吗?
我相信 Align[M,L]
类型类支持您想要的,它允许您重新排列一个 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]
edit :经过进一步实验,如果 hlists 具有多个相同类型的值,这将给出假阴性:
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