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
?