How to create HList lenses from HList

I am writing a general table viewer that should be able to display any type of Seq[Row] , where Row <: HList . The Table class is as follows:

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

When someone clicks on a table view column, I would like to redraw the entire table sorted in the order of that column. To do this, I need to be able to assign a function to sort the table in a specific column. Using lenses for this seems like one reasonable option.

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

Then I need to associate this function with a click event in the table header. At the first naive attempt, I would build such an html header using scalajs-react

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

This is an onClick setting even in the table header to call the sort method above. But this will not work because you have lost information about the type structure using an Int named ci . In fact, you need to store all type information around so that the compiler knows that the specific HList field is the n element of the list, so the lens design can work at the type level.

So this, of course, is the hard part of formless programming.

The required function is the one that will take me from any subclass of HList to HList lenses, each of which will select this element for any instance of this subclass of 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`, ... */ 

(Ideally, one could generalize this to combine these lenses so that the user can select the primary and then the second sort order.)

+3
source share
1 answer

Ok, I think I found the answer. The first tests seem to confirm this.

 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 
+1
source

Source: https://habr.com/ru/post/973018/


All Articles