Not covariance, but existential types. In your function
def myTest[F](values: (CC ⇒ Value[F])*) = Unit
F can vary from a call to myTest to another, but is fixed inside the argument list (CC ⇒ Value[F])* . Types of arguments in your examples:
scala> val i_eqs = (cc : CC) => cc.i eqs 1 i_eqs: CC => Value[Int] = <function1>
where F then Int .
scala> val s_eqs = (cc : CC) => cc.s eqs "lol" s_eqs: CC => Value[String] = <function1>
where F is String . It works for all three examples, because F same for all elements in the argument list. But when you call
myTest(_.i eqs 1, _.s eqs "lol")
the compiler tries to combine CC => Value[Int] (the type of the first argument) into CC => Value[String] (the second argument), which reduces to unifying Value[Int] with Value[String] . Indeed, you can define Value as a covariant, and this is probably a good idea. But I assume that you really want to express here that myTest arguments are a "list CC => Value[F] for some type F , which may differ from the argument to another in the list. :) A longer version is probably understandable:
scala> def myTest(values: (CC ⇒ Value[F] forSome { type F } )*) = Unit myTest: (values: CC => Value[_]*)Unit.type
The added forSome { type F } states that there exists an F for which the argument is of type CC => Value[F] , but "forget" that F So, Value[Int] will become Value[F] for the unknown F , as well as Value[String] . From the side they become the same, so they work. This syntax, although very clear, can be shortened most of the time with an underscore:
scala> def myTest(values: (CC ⇒ Value[_])*) = Unit myTest: (values: CC => Value[_]*)Unit.type