I'm on the road to learn Scala and I'm having a hard time understanding contravariants, covariants, invariance, etc. From Why doesn't the example compile, aka how does (co-, contra-, and in-) variance work? I have learned how functions can be considered subtypes of another function. (Really useful to know!)
The code below is what I believe are the important pieces to solving my puzzle. I have extracted parts that I think would add unneeded complexity to the problem. According to the example I have a Student object that will act as a factory to generate functions.
Functions will take in types or subtyes of AnyVal (Int, Double, Long, etc.), and the return output will be of the same input type. To achieve this the student class takes in a generic (A) that is a subtype of AnyVal. The abstract class is there so that I can refer a list of these students by doing something like List[Master[AnyVal]]( Student.func1).
The problem is that I cannot have the line "val function: List[A] => A" as I get error "covariant type A occurs in contravariant position in type => List[A] => A of value function". I don't know why the return type must be the contravariant of A. I can somewhat accept this fact for name based on the Function1 trait.
So how would I define my function in the abstract Master class so that the return type is a contravariant of type A? I found an example of how to define this with function definitions (Ex. def function[B >: A](v: B): List[B]), but how would I implement this with an anonymous function? Remember "A" in the master abstract class must be covariant since there will be a list of function that take in all AnyVal types (Int, Double, etc.)
Really appreciate the help! Let me know if any of my terminology is off. - Scala Learner
abstract class Master[+A] {
val function: List[A] => A
}
class Student[A <: AnyVal](val function: List[A] => A) extends Master[A]
object Student {
def func1 =
new Student((params: List[Int])=>params(0) + params(1))
}
val myFunc = Student.func1
val someList = List[Master[AnyVal]](myFunc)
It is very difficult to create generic classes or traits parameterised with a covariant type. Once you add the variance annotation, you cannot use the type as a parameter for any of the class methods. This is because functions are contra-variant in their parameters and covariant in their return types. There is a very good explanation in this accepted answer.
If you really want the type to be covariant, you have to write all the methods to take parameters of type [B >: A] - that is, to take supertypes of A as their parameter type. This can be quite challenging; the online Scala documentation has an example of the acrobatics that might be required.