1

The following code demonstrates several simple ways that the Scala compiler apparently can't find or can't infer the type of the Head and Tail members of a trait called TCons. The test case makes a ForAll type that serves as a marker that a Can[_] object exists for every type in a typelist, but that's not what I'm asking about. I have a way that works, shown below. I'm wondering why the non-working versions fail.

  import scala.language.higherKinds
  import scala.language.implicitConversions

  sealed trait TList  // A list of types; compile-time only

  trait TNil extends TList

  trait TCons[H, T <: TList] extends TList {
    type Head = H
    type Tail = T
  }

  class ForAll[Can[_], As <: TList]

  implicit def f1[Can[_], As <: TNil] = new ForAll[Can, As] // <-- The base case. This works.

//  implicit def f2[Can[_], As <: TCons[_, _]]
//    (implicit ev1: Can[As#Head], ev2: ForAll[Can, As#Tail])
//  = new ForAll[Can, As]

//  implicit def f3[Can[_], As <: TCons[_, _]]
//    (implicit ev1: Can[As#Head])
//  = new ForAll[Can, As]

//  implicit def f4[Can[_], As <: TCons[_, _]]
//    (implicit ev2: ForAll[Can, As#Tail])
//  = new ForAll[Can, As]

//  implicit def f5[Can[_], H, TL <: TCons[_, _]]
//    (implicit ev1: Can[H],
//              ev2: ForAll[Can, TL])
//  = new ForAll[Can, TCons[H, TL]]

  implicit def f6[Can[_], H, TL <: TList]  // <-- This is the only version that works for TCons.
    (implicit ev1: Can[H],
              ev2: ForAll[Can, TL])
  = new ForAll[Can, TCons[H, TL]]

  class CanCrushEnemies[A]

  implicit val c1 = new CanCrushEnemies[Int]
  implicit val c2 = new CanCrushEnemies[String]

  def crushes[As <: TList](implicit ev: ForAll[CanCrushEnemies, As]) =
    println(s"crushes $ev")

  crushes[TNil]
  crushes[TCons[Int, TNil]]

f6 is the only function that generates the needed ForAll object for a TCons. Here are Scala 2.11.2's error messages for f2f5. (Obviously, f3f5 are just attempts to shake out the problem by making the implicit arguments less stringent.)

f2, f3, f5:

error: could not find implicit value for parameter ev: T1.ForAll[T1.CanCrushEnemies,T1.TCons[Int,T1.TNil]]
  crushes[TCons[Int, TNil]]
         ^

f4:

error: diverging implicit expansion for type T1.ForAll[T1.CanCrushEnemies,T1.TNil]
starting with method f4 in object T1
  crushes[TNil]
         ^
error: diverging implicit expansion for type T1.ForAll[T1.CanCrushEnemies,T1.TCons[Int,T1.TNil]]
starting with method f4 in object T1
  crushes[TCons[Int, TNil]]
         ^

When I turn on both f2 and f6 simultaneously, I get this surprising error message, which might provide a clue:

error: type arguments [Can,_$2] do not conform to class ForAll's type parameter bounds [Can[_],As <: T1.TList]
    (implicit ev1: Can[As#Head], ev2: ForAll[Can, As#Tail])

What is going on here? Is there a rule that you can't look at the type members of a type parameterized with _? That would seem to greatly reduce the usefulness of existential types. And why does Scala fail to see that TCons#Tail <: TList when TCons#Tail = T and T <: TList?

Ben Kovitz
  • 4,920
  • 1
  • 22
  • 50

0 Answers0