Тестирование двух бесформенных типов HList в scala на эквивалентность через неявные
Мне интересно проверить, являются ли две гетерогенные записи 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
}
2 ответа
Я верю Align[M,L]
Класс typeclass поддерживает то, что вы хотите, он позволяет вам переставлять элементы одного списка в соответствии с порядком другого с теми же типами.
Вот функция, которая, я думаю, делает то, что вы хотите. Он скажет вам, если два эквивалентных списка имеют одинаковые значения для каждого типа. Если два списка не имеют одинаковые типы, он не скомпилируется.
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
работает: он просто перебирает один список и вытаскивает первый элемент другого с тем же типом. Но если вы используете литералы с одинарной типизацией, это не должно быть проблемой.
Так что это работает с вышеуказанными записями, по крайней мере, в отношении ключей:
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
}
Я считаю, что это должно делать то, что вы хотите, вы пробовали это?