Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to express type constraints for higher kinded type

Tags:

types

scala

I am trying to create a list of some trait, parameterised by a type using the CRTP, and cannot figure out how to express the type constraints. Here is some sample code that illustrates the problem:

trait A[X] {
  def x: X
}

trait B[Y <: A[Y]] {
  def y(i: Int): Y
}

case class C(i: Int) extends A[C] {
  def x = C(i)
}

case class D(i: Int) extends A[D] {
  def x = D(i)
}

case class E() extends B[C] {
  def y(i: Int) = C(i)
}

case class F() extends B[D] {
  def y(i: Int) = D(i)
}

object Program extends App {
  def emptyList[X[_ <: Z forSome { type Z <: A[Z] } ]]() = collection.mutable.ListBuffer.empty[X[_]]

  val myList = emptyList[B]()
  myList += E()
  myList += F()

  println(myList.map(_.y(2).x))
}

So here I'm trying to create a list of objects that conform to the B trait. However this code will not compile, and gives the following error:

kinds of the type arguments (B) do not conform to the expected kinds of the type parameters (type X). B's type parameters do not match type X's expected parameters: type Y's bounds >: Nothing <: A[Y] are stricter than type _'s declared bounds >: Nothing <: Z forSome { type Z <: A[Z] } val myList = emptyList[B] ()

To me it seems like _ <: Z forSome { type Z <: A[Z] } is indeed at least as strict as Y <: A[Y] but maybe I'm missing something.

So the question is - what should the constraints be on the emptyList function to correctly handle B?

like image 775
fhusb Avatar asked Nov 02 '12 11:11

fhusb


1 Answers

After some trial and error, I got it to work. Note: the compiler tells us that type parameters in A[+X] and B[+Y] must be covariant.

trait A[+X] {
  def x: X
}

trait B[+Y <: A[Y]] {
  def y(i: Int): Y
}

case class C(i: Int) extends A[C] {
  def x = C(i)
}

case class D(i: Int) extends A[D] {
  def x = D(i)
}

case class E() extends B[C] {
  def y(i: Int) = C(i)
}

case class F() extends B[D] {
  def y(i: Int) = D(i)
}


object Test extends App {
  def emptyList[X[Y <: A[Y]]] = collection.mutable.ListBuffer.empty[X[Y forSome {type Y <: A[Y]} ]]

  val myList = emptyList[B]
  myList += E()
  myList += F()

  println(myList.map(_.y(2).x))
}
like image 88
Kipton Barros Avatar answered Oct 28 '22 15:10

Kipton Barros