Is it possible to provide a generic function which would traverse an arbitrary case class hierarchy and collect information from selected fields? In the following snippet, such fields are encoded as Thing[T].
The snippet works fine for most scenarios. The only problem is when Thing wraps a type class (e.g. List[String]) and such field is nested deeper in the hierarchy; when it is on the top level, it works fine.
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
}