Pregunta

I have a confusing problem in my project and can't quite solve it, so please help me!

Here is a sample code that simplifies my original one:

trait Sample[A] {
    def doit(param: A)
}

case object SampleEx1 extends Sample[Int] {
    def doit(param: Int) = {
        param + 0
    }
}

Now I need to make A covariance for outer reasons, but it results in an error as commented out:

trait Sample[+A] {
    def doit(param: A) // ERR: covariant type A occurs in contravariant position in type A of value param
}

case object SampleEx1 extends Sample[Int] {
    def doit(param: Int) = {
        param + 0
    }
}

So I stacoverflowed and found a solution with another type B, but then another error happens:

trait Sample[+A] {
    def doit[B >: A](param: B)
}

case object SampleEx1 extends Sample[Int] {
    def doit[Int](param: Int) = {
        param + 0 // type mismatch; found : Int(0) required: String
    }
}

Apparently param is no longer Int because of [B >: Int].

I tried solving this one with myself and with google but couldn't get it. Could anyone help? Thank you so much! :))

¿Fue útil?

Solución

The first error covariant type A occurs in contravariant position in type A of value param means that if a generic type Foo declares itself to be covariant over T (i.e. Foo[+T]), it means that its methods can only return T and not require it. Otherwise type consistency will be violated. For instance you could pass in an instance of Sample[Dog] where Sample[Animal] is required, and then something could call doit(new Duck) on it, even though Sample[Dog]#doit can only handle instances of Dog. However, return values behave the exact opposite way in this context (I'll let you figure out why).

However this

def doit[Int](param: Int)

means doit has a type parameter called Int, which has nothing to do with the Int type (although it sure does seem like it does at first impression, which is why you should never use type parameter names that coincide with the names of other/built-in types). So the error you're getting is because Int in that context means "any type", and using + on any type will fall back to string concatenation as opposed to arithmetic addition.

instead you need (to correctly inherit from Sample[+A]):

def doit[B >: Int](param: B)

however, that will still not allow you to do addition on param because param is now any supertype of Int, not Int itself or a subtype thereof.

So I do not see how you can "fix" this—the way variance works fundamentally simply doesn't allow for generic types to be covariant over method parameters. This has nothing to do with Scala really. But see e.g. http://blogs.atlassian.com/2013/01/covariance-and-contravariance-in-scala/ or http://docs.scala-lang.org/tutorials/tour/variances.html for more information on how variance works and why it has to work exactly like it does (in any language implementing correct variance rules).

I think a better way in general to get a really helpful answer on Stackoverflow is to also describe what you really need to achieve, not just the implementation you've been working on so far.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top