scalashapelessscala-generics

covariant type occurs in invariant position in HList


I am trying to define a type safe heterogeneous list which has restrictions on the types of its elements, enforcing an hierarchy between the elements (e.g type A cannot appear after type B). The trouble happens when attempting to convert my structure to shapeless' HList.

Below is how I define the traits for my type:

sealed trait Hierarchy {
  type HListType <: HList
  def toHList : HListType

  def toCaseClass[C](implicit gen: Generic[C]{type Repr >: HListType}) = gen.from(toHList)
}

sealed trait <::[+H <: ElType[_], +T <: Hierarchy] extends Hierarchy {

  override type HListType = H :: tail.HListType

  val head: H
  val tail: T

  override def toHList: HListType = head :: tail.toHList

}

I get the following error:

Hierarchy.scala:26: covariant type H occurs in invariant position in type shapeless.::[H,<::.this.tail.HListType] of type HListType

This is quite puzzling, since the definition of shapeless.:: defines that both type parameters are covariant.

I am using scala 2.11.11 and shapeless 2.3.2. Is there a way to fix this error?


Solution

  • From the Scala spec:

    The right-hand side of a type alias is always in invariant position.

    So the problem is not from the definition of HList, but from the fact that I'm using the type parameter in a type alias.

    I changed the definition to

    sealed trait <::[+H, +T <: Hierarchy] extends Hierarchy {
    
      type H_ <: ElType[H]
    
      override type HListType = H_ :: tail.HListType
    
      val head: H_
      val tail: T
    
      override def toHList: HListType = head :: tail.toHList
    
    }
    

    And the problem disappears.