Select the Nth element of the list of HList lists and return this value as an HList of values

I have an HList in which each column represents a table column. Each list in an HList has the same length.

I would like to write a function that selects individual rows of this table as a tuple or HList of values. In the end, I will convert this to something more reasonable (like the Case class).

import shapeless.PolyDefns.~> import shapeless.{HList, HNil} val a = List(1,2,3) :: List("a", "b", "c") :: List(true, false, true) :: HNil object broken extends (HList ~> HList) { def apply[T](n:Int, l:HList): HList = { // I want to pick out the nth element of each HList // so in the above example, if n==1 // I want to return // 2 :: "b" :: false :: HNil ??? } } broken(1,a) 

Can I fix this function so that it works in accordance with what I described in the comments?

Bonus points: can I write this as an iterator that converts my HList "a" above into a sequence (Int, String, Boolean) or equivalent HList?

+7
scala shapeless
source share
1 answer

There are several ways to do this, but I would go with a custom type class:

 import shapeless._ trait RowSelect[L <: HList] extends DepFn2[L, Int] { type Row <: HList type Out = Option[Row] } object RowSelect { def select[L <: HList](l: L, i: Int)(implicit rs: RowSelect[L]): rs.Out = rs(l, i) type Aux[L <: HList, Row0 <: HList] = RowSelect[L] { type Row = Row0 } implicit val hnilRowSelect: Aux[HNil, HNil] = new RowSelect[HNil] { type Row = HNil def apply(l: HNil, i: Int): Option[HNil] = Some(HNil) } implicit def hconsRowSelect[A, T <: HList](implicit trs: RowSelect[T] ): Aux[List[A] :: T, A :: trs.Row] = new RowSelect[List[A] :: T] { type Row = A :: trs.Row def apply(l: List[A] :: T, i: Int): Option[A :: trs.Row] = for { h <- l.head.lift(i) t <- trs(l.tail, i) } yield h :: t } } 

Which works as follows:

 scala> println(RowSelect.select(a, 0)) Some(1 :: a :: true :: HNil) scala> println(RowSelect.select(a, 1)) Some(2 :: b :: false :: HNil) scala> println(RowSelect.select(a, 2)) Some(3 :: c :: true :: HNil) scala> println(RowSelect.select(a, 3)) None 

A RowSelect instance for L indicates that L is an hlist with all List elements and provides an operation that optionally selects an element at the specified index from each List .

You have to do the same thing with NatTRel or a combination of ConstMapper and ZipWith and Poly2 , but the custom type class brings everything together together and in many cases allows a more convenient composition.

For example, in this case, the solution to your bonus question can be quite simply written in terms of RowSelect :

 def allRows[L <: HList](l: L)(implicit rs: RowSelect[L]): List[rs.Row] = Stream.from(0).map(rs(l, _).toList).takeWhile(_.nonEmpty).flatten.toList 

And then:

 scala> allRows(a).foreach(println) 1 :: a :: true :: HNil 2 :: b :: false :: HNil 3 :: c :: true :: HNil 

And similarly, if you want to convert these whips into tuples:

 def allRows[L <: HList, R <: HList](l: L)(implicit rs: RowSelect.Aux[L, R], tp: ops.hlist.Tupler[R] ): List[tp.Out] = Stream.from(0).map(rs(l, _).map(tp(_)).toList).takeWhile(_.nonEmpty).flatten.toList 

What gives you:

 scala> allRows(a) res7: List[(Int, String, Boolean)] = List((1,a,true), (2,b,false), (3,c,true)) 

And so on.

+7
source share

All Articles