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 f2–f5. (Obviously, f3–f5 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?