scanLeft 在 shapeless 中使用 HMap

scanLeft using HMap in shapeless

我使用 shapeless HMap 作为案例的底层结构 class 作为共享同一父对象的其他对象的聚合器。 parent trait 允许访问一个名为 arity 的 Int 值 属性。我想在 HMap 上使用 scanLeft 来计算 HList 中对象的累积 aritys。我成功地编写了 Poly 对象,允许使用 foldLeft 计算所有 arity 的总和,但是当尝试将相同的概念应用于 scanLeft 时,它并没有工作了。

Q1:有人看到我应该如何修改 pointAccumulate 以支持 scanLeft 操作吗?我期望 lookup 是这样的 Nat(2) :: Nat(5) :: Nat(8) :: HNil(使用下面的示例)。

Q2:之后我将使用lookup来搜索知道组合结构中累积数量的元素的索引。鉴于此,可以使用 HList 还是我应该确保我得到 lookup 作为 List[Int]?

import shapeless._
import ops.hlist.{At, LeftFolder, LeftScanner}

object Point { type Point = (Double, Double) }

import Point._

sealed trait Shape {
    val arity: Int
}

case class Line(p0: Point, p1: Point) extends Shape {
    val arity = 2
}

case class Triangle(p0: Point, p1: Point, p2: Point) extends Shape {
    val arity = 3
}

case class Canvas[L <: HList](shapes: L)
                             (implicit
                              val ev: LUBConstraint[L, Shape],
                              val lf: LeftFolder.Aux[L, Int, pointAccumulate.type, Int],
                              val ls: LeftScanner[L, Int, pointAccumulate.type]) {

    lazy val parameterCount: Int = shapes.foldLeft(0)(pointAccumulate)

    lazy val lookup = shapes.scanLeft(0)(pointAccumulate)
}

object pointCount extends Poly1 {
    implicit def default[T <: Shape] = at[T](_.arity)
}

object pointAccumulate extends Poly2 {
    implicit def default[T <: Shape](implicit pt: pointCount.Case.Aux[T, Int]) =
        at[Int, T] { (i, p) => i + pointCount(p) }
}

object App {

    def main(args: Array[String]): Unit = {

        val l0 = Line((-2, 2), (2, -2))
        val tr1 = Triangle((0,0), (0, 1), (1, 0))
        val tr2 = Triangle((1,1), (1, 2), (2, 1))

        val c = Canvas(l0 :: tr1 :: tr2 :: HNil)
        println(c.lookup)
    }
}

我使用@devkat 的提示设法解决了这个问题。这是工作代码

import shapeless._
import ops.hlist.{At, LeftFolder, LeftScanner}

object Point { type Point = (Double, Double) }

import Point._

sealed trait Shape {
    val arity: Int
}

case class Line(p0: Point, p1: Point) extends Shape {
    val arity = 2
}

case class Triangle(p0: Point, p1: Point, p2: Point) extends Shape {
    val arity = 3
}

case class Canvas[L <: HList, LL <: HList](shapes: L)
                             (implicit
                              val ev: LUBConstraint[L, Shape],
                              val lf: LeftFolder.Aux[L, Int, pointAccumulate.type, Int],
                              val ls: LeftScanner.Aux[L, Int, pointAccumulate.type, LL],
                              val ev2: LUBConstraint[LL, Int]) {

    lazy val parameterCount: Int = shapes.foldLeft(0)(pointAccumulate)

    lazy val lookup = shapes.scanLeft(0)(pointAccumulate)
}

object pointCount extends Poly1 {
    implicit def default[T <: Shape] = at[T](_.arity)
}

object pointAccumulate extends Poly2 {
    implicit def default1[T <: Shape](implicit pt: pointCount.Case.Aux[T, Int]) =
        at[T, Int] { (p, i) => i + pointCount(p) }

    implicit def default2[T <: Shape](implicit pt: pointCount.Case.Aux[T, Int]) =
        at[Int, T] { (i, p) => i + pointCount(p) }
}

object App2 {

    def main(args: Array[String]): Unit = {

        val l0 = Line((-2, 2), (2, -2))
        val tr1 = Triangle((0,0), (0, 1), (1, 0))
        val tr2 = Triangle((1,1), (1, 2), (2, 1))

        val c = Canvas(l0 :: tr1 :: tr2 :: HNil)
        println(c.parameterCount)
        println(c.lookup)
    }
}