sealed abstract trait HList
case class :+:[H, T <: HList](head: H, tail: T) extends HList {
def :+:[T](v: T) = new :+:(v, this)
}
case object HNil extends HList {
def :+:[T](v: T) = new :+:(v, this)
}
object HListExpt {
def main(args: Array[String]) {
val me: String :+: Int :+: Symbol :+: HNil.type = "Rahul" :+: 20 :+: 'Male :+: HNil
println(me.head, me.tail.head)
}
}
On trying to compile the above code, I get the following compiler error:
error: type mismatch;
found : :+:[java.lang.String,:+:[Int,:+:[Symbol,object HNil]]]
required: :+:[String,:+:[Int,:+:[Symbol,HNil.type]]]
val me: String :+: Int :+: Symbol :+: HNil.type = "Rahul" :+: 20 :+: 'Male :+: HNil
What am I doing wrong here? What would be the correct way to type-annotate the above HList
?
PS: The code compiles fine when I remove the type annotation.
The root problem here is that singleton types are never inferred. Here's a demonstration:
scala> case object A
defined module A
scala> A
res6: A.type = A
scala> identity[A.type](A)
res7: A.type = A
scala> identity(A)
res8: object A = A
Why is this? Quoth Odersky et. al. in Programming in Scala, §27.6:
Usually [singleton] types are too
specific to be useful, which is why
the compiler is reluctant to insert
them automatically.
So, let's explicitly provide the type argument:
sealed abstract trait HList
case class :+:[H, T <: HList](head: H, tail: T) extends HList {
def :+:[T](v: T) = new :+:(v, this)
}
case object HNil extends HList {
def :+:[T](v: T) = new :+:[T, HNil.type](v, this)
}
val me: String :+: Int :+: Symbol :+: HNil.type = "Rahul" :+: 20 :+: 'Male :+: HNil
println(me.head, me.tail.head)
Bonus Link:
- Singleton Types are Mean and Spiteful
I'm not sure why, but if HNil is defined as a class everything compiles:
class HNilClass extends HList {
def :+:[T](v: T) = new :+:(v, this)
}
object HNil extends HNilClass