Фильтрация HList с использованием супертипа - PullRequest
0 голосов
/ 06 апреля 2020
import shapeless._
import shapeless.labelled._
import shapeless.tag._

Учитывая HList как

case class Foo(a: String, b: Int)

val hlist = LabelledGeneric[Foo].to(Foo("Hello", 42))

и Witness как

val aW = Witness("a")

Я ожидаю два выражения

hlist.filter[String with KeyTag[Symbol with Tagged[aW.T], String]]
hlist.filter[KeyTag[Tagged[aW.T], String]]

дать тот же тип результата. К сожалению, второе выражение означает HNil.

Почему это так? Как я могу фильтровать по таким супертипам?

1 Ответ

1 голос
/ 08 апреля 2020

Тип класса Filter инвариантен, тип данных KeyTag инвариантен относительно типа ключа. Так что это не может работать с супертипом. implicitly[Filter.Aux[Int :: String :: Boolean :: HNil, AnyVal, Int :: Boolean :: HNil]] и implicitly[Filter.Aux[Int :: String :: Boolean :: HNil, AnyRef, String :: HNil]] не компилируются, implicitly[Filter.Aux[Int :: String :: Boolean :: HNil, AnyVal, HNil]] и implicitly[Filter.Aux[Int :: String :: Boolean :: HNil, AnyRef, HNil]] компилируются.

Вы должны использовать Filter следующим образом

implicitly[Filter.Aux[Record.`'a -> String, 'b -> Int`.T, FieldType[Symbol @@ "a", String], Record.`'a -> String`.T]]
implicitly[Filter.Aux[Record.`'a -> String, 'b -> Int`.T, FieldType[Symbol @@ aW.T, String], Record.`'a -> String`.T]]

hlist.filter[FieldType[Witness.`'a`.T, String]] // Hello :: HNil
hlist.filter[FieldType[Symbol @@ Witness.`"a"`.T, String]] // Hello :: HNil
hlist.filter[FieldType[Symbol @@ "a", String]] // Hello :: HNil
hlist.filter[FieldType[Symbol @@ aW.T, String]] // Hello :: HNil
hlist.filter[FieldType[Symbol with Tagged[aW.T], String]] // Hello :: HNil
hlist.filter[String with KeyTag[Symbol with Tagged[aW.T], String]] // Hello :: HNil

Если вы хотите фильтровать по супертипу, тогда вы должны использовать Collect с правильно определенным Poly.

trait SuperPoly[Upper] extends Poly1
object SuperPoly {
  implicit def cse[P <: SuperPoly[Upper], Upper, A](implicit
    unpack: Unpack1[P, SuperPoly, Upper],
    ev: A <:< Upper
  ): poly.Case1.Aux[P, A, A] = poly.Case1(identity)
}

implicitly[Collect.Aux[Int :: String :: Boolean :: HNil, SuperPoly[AnyVal], Int :: Boolean :: HNil]]
implicitly[Collect.Aux[Int :: String :: Boolean :: HNil, SuperPoly[AnyRef], String :: HNil]]

implicitly[Collect.Aux[Record.`'a -> String, 'b -> Int`.T, SuperPoly[KeyTag[_ <: Tagged[aW.T], String]], Record.`'a -> String`.T]]
val aPoly = new SuperPoly[KeyTag[_ <: Tagged[aW.T], String]] {}
implicitly[Collect.Aux[Record.`'a -> String, 'b -> Int`.T, aPoly.type, Record.`'a -> String`.T]]

hlist.collect(aPoly) // Hello :: HNil
...