见证类型派生问题

时间:2018-04-23 22:12:24

标签: scala shapeless labelled-generic

我想为从案例类中删除字段构建通用解决方案。 使用这个trick我构建了这个工作代码:

  implicit class SemiGenericIgnoringOps[T](t: T) {

    def ignoring[TRepr <: HList, 
                 V, 
                 TargetRepr <: HList, 
                 H <: HList](k: Witness)
                            (implicit
                             gen: LabelledGeneric.Aux[T, TRepr],
                             rem: Remover.Aux[TRepr, k.T, (V, TargetRepr)],
                             upd: Updater.Aux[TargetRepr, FieldType[k.T, V], H],
                             ali: Align[H, TRepr]
    ): SemiGeneric.Aux[T, TargetRepr] = new SemiGeneric[T] {
      type Repr = TargetRepr
      def convert: TargetRepr = gen.to(t) - k
    }
  }

我想将k: Witness替换为HListWitness。但即使添加通用Witness类型参数也会导致编译错误:无法找到Remover的隐式值。

  implicit class SemiGenericIgnoringOps[T](t: T) {

    def ignoring[TRepr <: HList,
                 V,
                 TargetRepr <: HList,
                 H <: HList,
                 W <: Witness](w: W) // added type parameter
                              (implicit
                               gen: LabelledGeneric.Aux[T, TRepr],
                               rem: Remover.Aux[TRepr, w.T, (V, TargetRepr)],
                               upd: Updater.Aux[TargetRepr, FieldType[w.T, V], H],
                               ali: Align[H, TRepr]
    ): SemiGeneric.Aux[T, TargetRepr] = new SemiGeneric[T] {
      type Repr = TargetRepr
      def convert: TargetRepr = gen.to(t) - w
    }
  }

似乎编译器无法导出Witness.T。使用Witness.Aux[R]进行操作无济于事。如何克服这个问题?

1 个答案:

答案 0 :(得分:1)

与您提到的答案相反,您不需要UpdaterAlign,因为convert适用于HList /记录。

以下代码有效:

  import shapeless.ops.record.Remover
  import shapeless.{::, HList, HNil, LabelledGeneric, Witness}

  trait GenericAllKeysRemover[A <: Product, K <: HList] {
    type Out <: HList
    def apply(a: A): Out
  }

  object GenericAllKeysRemover {
    type Aux[A <: Product, K <: HList, Out0 <: HList] = GenericAllKeysRemover[A, K] { type Out = Out0 }
    def instance[A <: Product, K <: HList, Out0 <: HList](f: A => Out0): Aux[A, K, Out0] = new GenericAllKeysRemover[A, K] {
      override type Out = Out0
      override def apply(a: A): Out = f(a)
    }

    def apply[A <: Product, K <: HList](implicit genericAllKeysRemover: GenericAllKeysRemover[A, K]): Aux[A, K, genericAllKeysRemover.Out] = genericAllKeysRemover

    implicit def mkGenericAllKeysRemover[A <: Product, L <: HList, K <: HList, Out <: HList](implicit
      labelledGeneric: LabelledGeneric.Aux[A, L],
      allKeysRemover: AllKeysRemover.Aux[L, K, Out]): Aux[A, K, Out] =
      instance(a => allKeysRemover(labelledGeneric.to(a)))
  }

  trait AllKeysRemover[L <: HList, K <: HList] {
    type Out <: HList
    def apply(l: L): Out
  }

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

    def apply[L <: HList, K <: HList](implicit allKeysRemover: AllKeysRemover[L, K]): Aux[L, K, allKeysRemover.Out] =
      allKeysRemover

    implicit def mkAllKeysRemover[L <: HList]: Aux[L, HNil, L] = instance(identity)
    implicit def mkAllKeysRemover1[L <: HList, H, T <: HList, V, L_removeH <: HList, Out <: HList](implicit
      remover: Remover.Aux[L, H, (V, L_removeH)],
      allKeysRemover: Aux[L_removeH, T, Out]): Aux[L, H :: T, Out] =
      instance(l => allKeysRemover(remover(l)._2))
  }

  case class MyClass(i: Int, s: String, b: Boolean)

  GenericAllKeysRemover[MyClass, Witness.`'s`.T :: Witness.`'b`.T :: HNil].apply(MyClass(1, "a", true)) //1 :: HNil