首页 > 解决方案 > 无形。如何按键过滤 LabelledGeneric 记录?

问题描述

我有下一段要用于创建类型类实例的代码:

def productsLogShow[HK <: HList, T, H <: HList, K](hideFieldsWithKeys: HK)(
  implicit lg: LabelledGeneric.Aux[T, H], lsh: LogShow[H], keys: Keys.Aux[H, K]
): LogShow[T] = {
  LogShow.create { value =>
    val record = lg.to(value)

    // ...
    //filter somehow record and get a record without 
    // keys which are in 'hideFieldsWithKeys'

    // ...
  }
}

那么,如何过滤记录并使用正确的hideFieldsWithKeys参数类型?

更新:应该根据 Dmitro 的回答工作的完整代码片段

object Main extends App {

  trait LogShow[T] {
    def show(value: T): String
  }

  object LogShow {
    def apply[T: LogShow]: LogShow[T] = implicitly[LogShow[T]]

    def create[T](f: T => String): LogShow[T] = new LogShow[T] {
      override def show(value: T): String = f(value)
    }
  }

  import LogShow.create
  implicit val StringShow: LogShow[String] = create(identity)

  import shapeless._
  import shapeless.labelled.FieldType
  import shapeless.ops.hlist.LeftFolder
  import shapeless.ops.record.Remover

  import scala.reflect.ClassTag
  def productsLogShow[HK <: HList, T, H <: HList, H1 <: HList, K <: HList](hideFieldsWithKeys: HK)
                                                                          (implicit
                                                                           ct: ClassTag[T],
                                                                           lg: LabelledGeneric.Aux[T, H],
                                                                           rem: RemoverAll.Aux[H, HK, H1],
                                                                           lsh: LogShow[H1]): LogShow[T] = {
    LogShow.create { value =>
      val record = lg.to(value)
      val clearedRecord = rem(record, hideFieldsWithKeys)

      s"${ct.runtimeClass.getSimpleName}:\n${lsh.show(clearedRecord)}"
    }
  }

  trait RemoverAll[L <: HList, K <: HList] extends DepFn2[L, K]

  object RemoverAll {
    type Aux[L <: HList, K <: HList, Out0 <: HList] = RemoverAll[L, K] {type Out = Out0}

    def create[L <: HList, K <: HList, Out0 <: HList](f: (L, K) => Out0): Aux[L, K, Out0] = new RemoverAll[L, K] {
      override type Out = Out0

      override def apply(l: L, k: K): Out0 = f(l, k)
    }

    implicit def mk[K <: HList, L <: HList, Out <: HList](implicit leftFolder: LeftFolder.Aux[K, L, removeKeys.type, Out]): Aux[L, K, Out] =
      create((l, k) => leftFolder(k, l))

    object removeKeys extends Poly2 {
      implicit def cse[K, L <: HList, V, Out <: HList](implicit remover: Remover.Aux[L, K, (V, Out)]): Case.Aux[L, K, Out] =
        at((l, _) => remover(l)._2)
    }

  }


  implicit def hconsLogShow[K <: Symbol, H, T <: HList](implicit
                                                        wt: Witness.Aux[K],
                                                        lshHead: LogShow[H],
                                                        lshTail: LogShow[T]): LogShow[FieldType[K, H] :: T] =
    LogShow.create { value =>
      s"${wt.value.name}:    ${lshHead.show(value.head)}\n${lshTail.show(value.tail)}"
    }

  implicit val hnilLogShow: LogShow[HNil] = LogShow.create(_ => "")


  //test
  case class Address(country: String, street: String)

  implicit val inst: LogShow[Address] = productsLogShow('country :: HNil) 
  //could not find implicit value for parameter lg: shapeless.LabelledGeneric.Aux[T,H]
  //[error]   implicit val inst: LogShow[Address] = productsLogShow('country :: HNil)

  println(implicitly[LogShow[Address]].show(Address("Ukraine", "Gorkogo")))
}

标签: scalashapeless

解决方案


尝试

  import shapeless.ops.hlist.LeftFolder
  import shapeless.ops.record.{Keys, Remover}
  import shapeless.{::, DepFn2, HList, HNil, LabelledGeneric, Poly2, Witness}
  import shapeless.record.Record

  def productsLogShow[HK <: HList, T, H <: HList, H1 <: HList, K <: HList](hideFieldsWithKeys: HK)(
    implicit lg: LabelledGeneric.Aux[T, H], lsh: LogShow[H], /*keys: Keys.Aux[H, K]*/ rem: RemoverAll.Aux[H, HK, H1]
  ): LogShow[T] = {
    LogShow.create { value =>
      val record = lg.to(value)
      val record1 = rem(record, hideFieldsWithKeys)
      ???
    }
  }

  trait RemoverAll[L <: HList, K <: HList] extends DepFn2[L, K]

  object RemoverAll {
    type Aux[L <: HList, K <: HList, Out0 <: HList] = RemoverAll[L, K] { type Out = Out0 }
    def create[L <: HList, K <: HList, Out0 <: HList](f: (L, K) => Out0): Aux[L, K, Out0] = new RemoverAll[L, K] {
      override type Out = Out0
      override def apply(l: L, k: K): Out0 = f(l, k)
    }

    implicit def mk[K <: HList, L <: HList, Out <: HList](implicit leftFolder: LeftFolder.Aux[K, L, removeKeys.type, Out]): Aux[L, K, Out] =
      create((l, k) => leftFolder(k, l))

    object removeKeys extends Poly2 {
      implicit def cse[K, L <: HList, V, Out <: HList](implicit remover: Remover.Aux[L, K, (V, Out)]): Case.Aux[L, K, Out] =
        at((l, _) => remover(l)._2)
    }
  }

  implicitly[RemoverAll.Aux[Record.`'i -> Int, 's -> String, 'b -> Boolean`.T, Witness.`'s`.T :: Witness.`'i`.T :: HNil, Record.`'b -> Boolean`.T]]

换行

implicit val inst: LogShow[Address] = productsLogShow('country :: HNil) 

import shapeless.syntax.singleton._
implicit val inst: LogShow[Address] = productsLogShow('country.narrow :: HNil)

'country有类型Symbol而不是必要的单例类型Witness.`'country`.T


推荐阅读