Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 523
user3127060 Avatar asked Feb 21 '16 11:02

user3127060


1 Answers

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
like image 185
Miles Sabin Avatar answered Nov 01 '22 10:11

Miles Sabin