使用Generic从嵌套的case类中收集数据

时间:2014-10-21 14:27:40

标签: scala shapeless

是否可以提供遍历任意案例类层次结构并从选定字段收集信息的通用函数?在以下代码段中,此类字段编码为Thing [T]。

该代码段适用于大多数情况。唯一的问题是当Thing包装一个类型类(例如List [String])并且这样的字段嵌套在层次结构的更深处;当它处于顶层时,它可以正常工作。

import shapeless.HList._
import shapeless._
import shapeless.ops.hlist.LeftFolder

case class Thing[T](t: T) {
  def info: String = ???
}

trait Collector[T] extends (T => Seq[String])

object Collector extends LowPriority {
  implicit def forThing[T]: Collector[Thing[T]] = new Collector[Thing[T]] {
    override def apply(thing: Thing[T]): Seq[String] = thing.info :: Nil
  }
}

trait LowPriority {
  object Fn extends Poly2 {
    implicit def caseField[T](implicit c: Collector[T]) =
      at[Seq[String], T]((acc, t) => acc ++ c(t))
  }

  implicit def forT[T, L <: HList](implicit g: Generic.Aux[T, L],
                                   f: LeftFolder.Aux[L, Seq[String], Fn.type, Seq[String]]): Collector[T] =
    new Collector[T] {
      override def apply(t: T): Seq[String] = g.to(t).foldLeft[Seq[String]](Nil)(Fn)
    }
}

object Test extends App {
  case class L1(a: L2)
  case class L2(b: Thing[List[String]])

  implicitly[Collector[L2]] // works fine
  implicitly[Collector[L1]] // won't compile
}

1 个答案:

答案 0 :(得分:1)

我担心这是不可能的。 HList似乎是从静态已知事物构造的编译时间。因此,当您包装类型时,无论出于何种原因,似乎HList无法推断出正确的含义。

以下是从shapeless's flatten example构建的简单示例。

object Test extends App {
  import shapeless._
  import ops.tuple.FlatMapper
  import syntax.std.tuple._

  trait LowPriorityFlatten extends Poly1 {
    implicit def default[T] = at[T](Tuple1(_))
  }
  object flatten extends LowPriorityFlatten {
    implicit def caseTuple[P <: Product](implicit fm: FlatMapper[P, flatten.type]) =
      at[P](_.flatMap(flatten))
  }

  case class AT[T](a: T, b: T)
  case class A2T[T](a: AT[T], b: AT[T])
  case class A2(a: AT[Int], b: AT[Int])

  println(flatten(A2T(AT(1, 2), AT(3, 4))))
  println(flatten(A2(AT(1, 2), AT(3, 4))))
}

你会认为这应该为A2T和A2打印出相同的东西,但事实并非如此。它实际打印出来:

(1,2,3,4)
(AT(1,2),AT(3,4))

所以,我不认为你可以使用Shapeless做你想做的事。

然而!你仍然可以走你的案例类层次结构寻找事物(只是没有形状)。看看这个!

object Test extends App {
  case class Thing[T](t: T) {
    def info: String = toString
  }

  def collect[T](t: T): Iterator[String] = t match {
    case t: Thing[_] => Iterator(t.info)
    case p: Product => p.productIterator.flatMap(collect)
    case _ => Iterator()
  }

  case class L0(a: L1)
  case class L1(a: L2)
  case class L2(a: Thing[List[String]])
  case class MT(a: L2, b: L2, c: Thing[Int])

  println("Case #1")
  collect(L0(L1(L2(Thing(List("a", "b", "c")))))).foreach(println)

  println("Case #2")
  collect(MT(L2(Thing(List("a", "c"))), L2(Thing(List("b"))), Thing(25))).foreach(println)
}

这有输出:

Case #1
Thing(List(a, b, c))
Case #2
Thing(List(a, c))
Thing(List(b))
Thing(25)