I am writing a generic table viewer which should be able to display any type of Seq[Row]
where Row <: HList
. The Table class looks like this:
class Table[TH<:HList, TR<:HList](val hdrs: TH, val rows: Seq[TR])
When someone clicks on a column of the table viewer I would like to redraw the whole table sorted on the Ordering of that column. For that I need a to be able assign a function to sort the table on a particular colum. Using lenses for that seems 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
}
I then need to tie that function to a click event on the table header. On a first naive attempt I would build the html header like this 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 setting up a onClick even on the table header to call the sort
method above. But this won't work, because one has lost the type structure information by using the Int
named ci
. One really needs to keep all the type information around for the compiler to know that a particular field of the HList is the n
th element of the list, so that the construction of the lens can work at the type level.
So that is of course the difficult part of programming with shapeless.
The function that is needed is one that would take me from any subclass of HList
to an HList of Lenses each of which would select that element for any instance of that particular 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 then generalise this to allow one to combine these lenses, so that a user could select a primary then a secondary sort order. )
Ok I think I found the answer. 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
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.