Test two scala shapeless HList types for equivalence via implicit

生来就可爱ヽ(ⅴ<●) 提交于 2019-12-10 10:44:22

问题


I'm interested in testing whether two HList heterogeneous records are "equivalent"; that is, they have the same key/val pairs, but not necessarily in the same order. Is there a predefined type predicate that does what EquivHLists does in the code fragment below?

// 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
}

回答1:


I believe the Align[M,L] typeclass supports what you want, it lets you rearrange the elements of one hlist to match the order of another with the same types.

Here's a function that I think does what you want. It will tell you if two equivalent hlists have the same values for each type. If the two lists don't have the same types, it won't compile.

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 : after some further experimentation, this will give false negatives if the hlists have more than one value of the same type:

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

This is because of the way Align works: it basically just iterates over one hlist and pulls out the first element of the other with the same type. But if you're using singleton-typed literals then this shouldn't be an issue.

So this does work with the above records at least in regards to the keys:

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



回答2:


def f(hr1: H1 <: HList, hr2 : H2 <: HList)(implicit equiv: H1 =:= H2) = {
  // biz logic
}

I believe this should do what you want, have you tried it?



来源:https://stackoverflow.com/questions/47421083/test-two-scala-shapeless-hlist-types-for-equivalence-via-implicit

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!