如何从HList创建Lenses的HList

3

我正在编写一个通用的表格查看器,它应该能够显示任何类型的Seq[Row],其中Row <: HList。Table类如下:

class Table[TH<:HList, TR<:HList](val hdrs: TH, val rows: Seq[TR])

当某人点击表格查看器的一列时,我希望重新绘制整个表格并按该列的排序方式排序。为此,我需要能够分配一个函数来对特定列进行排序。使用透镜似乎是一个合理的选择。

def sort[Elem<:Nat](lens: Lens[R, Elem]) = {
   ...
   table.rows.sortBy(lens.get(_)) //sort the rows of the table using the lens
}

然后我需要将该函数绑定到表头的点击事件上。在第一次尝试中,我会像这样使用scalajs-react构建html表头。

def header = {
    tr(for (ci <- 0 to tab.hdrs.runtimeLength) yield 
          th(onclick --> B.sort(hlistNthLens[R,nat(ci)]))(tab.hdrs(ci).toString))
}

这是在表头上设置一个onClick事件来调用上面的sort方法。但这不起作用,因为使用Int命名为ci丢失了类型结构信息。真正需要做的是保留所有类型信息,让编译器知道HList的特定字段是列表的第n个元素,以便于镜头的构建可以在类型层面上工作。
所以这当然是使用shapeless进行编程的困难之处。
所需的函数是将我从HList的任何子类带到Lenses的HList的函数,每个Lense都会选择该特定子类的HList任何实例中的元素。
 def lenses[H <: HList] = /* return an HList of Lenses, where 
        the first Lens will always select the first element of any 
        instance of `H`, the second will always select the second
        element of any instance of `H`, ... */

(理想情况下,可以将其推广以允许用户组合这些镜头,这样用户就可以选择主排序顺序和次要排序顺序。)

嗯,你有一个镜头列表,需要将函数应用于任何一个镜头元素吗? - DaunnC
2
我不是很清楚你在问什么。你能详细说明一下代码示例吗? - Travis Brown
我正在编写的代码在这里 我的 akka.http.model.Uri 仓库 ScalaReactSpeedTest.scala 文件中。在那个版本中,第69行当然是错误的。 - Henry Story
1个回答

1

好的,我想我找到了答案。初步测试似乎确认了这一点。

scala> :paste
// Entering paste mode (ctrl-D to finish)

import shapeless._
import shapeless.ops.hlist.At
import shapeless.syntax.std.tuple._

final class myHListOps[L <: HList](l: L) {

  import hlistaux._

  def extractors(implicit extractor : Extractor[_0, L,L]) : extractor.Out = extractor()
}

object hlistaux {
  trait Extractor[HF<:Nat, In <: HList, Remaining<: HList] extends DepFn0 { type Out <: HList }

  object Extractor {
    def apply[HL <: HList]
    (implicit extractor: Extractor[_0, HL,HL]):
       Aux[_0, HL, HL, extractor.Out] = extractor

    type Aux[HF<:Nat, In <: HList, Remaining<: HList, Out0 <: HList] = Extractor[HF, In, Remaining] { type Out = Out0 }

    //To deal with case where HNil is passed. not sure if this is right.
    implicit def hnilExtractor: Aux[_0, HNil, HNil, HNil] =
      new Extractor[_0, HNil, HNil] {
        type Out = HNil
        def apply(): Out = HNil
      }

    implicit def hSingleExtractor1[N<:Nat, In<:HList, H ]
    (implicit att : At[In, N]): Aux[N, In, H::HNil, At[In,N]::HNil] =
      new Extractor[N, In, H::HNil] {
        type Out = At[In,N]::HNil
        def apply(): Out = att::HNil
      }


    implicit def hlistExtractor1[N <: Nat, In<:HList, H, Tail<: HList]
    (implicit mt : Extractor[Succ[N], In, Tail],
              att : At[In, N])
    :Aux[N, In, H::Tail, At[In,N]::mt.Out] = {
      new Extractor[N, In, H::Tail] {
        type Out = At[In,N]::mt.Out

        def apply(): Out = {
          att :: mt()
        }
      }
    }
  }
}

// Exiting paste mode, now interpreting.

import shapeless._
import shapeless.ops.hlist.At
import shapeless.syntax.std.tuple._
defined class myHListOps
defined object hlistaux

scala> val l = "Hello"::HNil
l: shapeless.::[String,shapeless.HNil] = Hello :: HNil

scala> val lo = new myHListOps(l).extractors
lo: shapeless.::[shapeless.ops.hlist.At[shapeless.::[String,shapeless.HNil],shapeless._0],shapeless.HNil] = shapeless.ops.hlist$At$$anon$54@12d33d1c :: HNil

scala> lo.head(l)
res0: lo.head.Out = Hello

scala> val m = 42::l
m: shapeless.::[Int,shapeless.::[String,shapeless.HNil]] = 42 :: Hello :: HNil

scala> val mo = new myHListOps(m).extractors
mo: shapeless.::[shapeless.ops.hlist.At[shapeless.::[Int,shapeless.::[String,shapeless.HNil]],shapeless._0],shapeless.::[shapeless.ops.hlist.At[shapeless.::[Int,shapeless.::[String,shapeless.HNil]],shapeless.Succ[shapeless._0]],shapeless.HNil]] = shapeless.ops.hlist$At$$anon$54@5e181eeb :: shapeless.ops.hlist$At$$anon$55@1960690 :: HNil

scala> mo.head(m)
res3: mo.head.Out = 42

scala> mo.tail.head(m)
res4: mo.tail.head.Out = Hello

这是经过一些工作后的代码要点 https://gist.github.com/bblfish/d8c2e6e1e88203b91104 - Henry Story

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接