I would like to mix generic types in my myTest
case class CC(i:Int, s:String, s2:String)
case class Value[V](value: V)
def myTest[F](values: (CC ⇒ Value[F])*) = Unit
class QF[M](x: M) {
def eqs(v: M) = Value(x)
}
implicit def conversion[V](v: V): QF[V] = new QF(v)
myTest(_.i eqs 1)
myTest(_.s eqs "lol")
myTest(_.s eqs "lol", _.s2 eqs "lol2")
It works until that point but I think I'm getting a covariance error when I try to do mix types.
When I do :
myTest(_.i eqs 1, _.s eqs "lol")
I get the following error :
Error:(16, 13) type mismatch;
found : A$A135.this.CC => A$A135.this.Value[Int]
required: A$A135.this.CC => A$A135.this.Value[Any]
myTest(_.i eqs 1, _.s eqs "lol");}
^
Yes that is correct. Because Value
is defined as invariant in V
, it means that Value[Int]
and Value[String]
are not sub-types of Value[Any]
, but Any
is the nearest common super-type of Int
and String
, i.e. F
in myTest
. Because V
is in covariant position, you can simply amend this:
case class Value[+V](value: V)
Not co-variance but existential types. In your function
def myTest[F](values: (CC ⇒ Value[F])*) = Unit
F
can vary from a call to myTest
to another one but is fixed inside the argument list (CC ⇒ Value[F])*
. The types of the arguments in your examples are
scala> val i_eqs = (cc : CC) => cc.i eqs 1
i_eqs: CC => Value[Int] = <function1>
where F
is 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 3 examples because F
is the same for all the elements in the argument list. But when you call
myTest(_.i eqs 1, _.s eqs "lol")
the compiler tries to unify CC => Value[Int]
(the type of the first argument) to CC => Value[String]
(second argument) which reduces to unifying Value[Int]
with Value[String]
. Indeed you can define Value
as covariant and it's probably a good idea. But i guess that what you really want here is to express that the arguments of myTest
is "a list of CC => Value[F]
for some type F
which may differ from an argument to another in the list. Which exactly is existential types :) The long version is probably clearer:
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" about which F
. So Value[Int]
become Value[F]
for an unknown F
and so does Value[String]
. From the outside they both become the same type so it works. This syntax though being very clear can be shortened most of the time by an underscore:
scala> def myTest(values: (CC ⇒ Value[_])*) = Unit
myTest: (values: CC => Value[_]*)Unit.type
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With