gpt4 book ai didi

scala - 使用 Generic 从嵌套案例类中收集数据

转载 作者:行者123 更新时间:2023-12-01 09:06:47 25 4
gpt4 key购买 nike

是否可以提供一个通用函数来遍历任意案例类层次结构并从选定字段中收集信息?在以下代码段中,此类字段被编码为 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
}

最佳答案

恐怕这是不可能的。 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)

关于scala - 使用 Generic 从嵌套案例类中收集数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26489182/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com