Getting elements from an HList

Getting elements from an HList

I toyed around with HList and the following works as expected:
val hl = 1 :: "foo" :: HNil
val i: Int = hl(_0)
val s: String = hl(_1)

However, I can't get the following piece of code working (let's assume for a moment random access on lists is a smart idea ;-)):
class Container(hl: HList) {
    def get(n: Nat) = hl(n)

val container = new Container(1 :: "foo" :: HNil)
val i: Int = container.get(_0)
val s: String = container.get(_1)

I'd like to have get return an Int and String according to it's parameter.  I assume, if possible at all, I have to use Aux or at but I'm not sure how to do this.


Answer 1:

Try something along these lines,

scala> import shapeless._, nat._, ops.hlist._
import shapeless._
import nat._
import ops.hlist._

scala> class Container[L <: HList](hl: L) {
     |   def get(n: Nat)(implicit at: At[L, n.N]): at.Out = hl[n.N]
     | }
defined class Container

scala> val container = new Container(1 :: "foo" :: HNil)
container: Container[shapeless.::[Int,shapeless.::[String,shapeless.HNil]]] = ...

scala> container.get(_0)
res1: Int = 1

scala> container.get(_1)
res2: String = foo

The first crucial difference here is that rather than typing hl as plain HList, which loses all specific information about the types of the elements, we parametrize over the precise type of the argument and preserve its structure as L. The second difference is that we use L to index the implicit At type class instance which is used to perform the indexing in get.

Also note that because there is an implicit conversion from Int literals to Nat‘s you can write,

scala> container.get(0)
res3: Int = 1

scala> container.get(1)
res4: String = foo