covariant type T occurs in invariant position

Vincenzo Maggio picture Vincenzo Maggio · Jul 30, 2012 · Viewed 7.1k times · Source

I'm moving my first steps in Scala and I would like to make the following code works:

trait Gene[+T] {
    val gene: Array[T]
}

The error that the compiler gives is: covariant type T occurs in invariant position in type => Array[T] of value gene

I know I could do something like:

trait Gene[+T] {
    def gene[U >: T]: Array[U]
}

but this doesn't solve the problem because I need a value: pratically what I'm trying to say is "I don't care of the inside type, I know that genes will have a gene field that return its content". (the +T here is because I wanna do something like type Genome = Array[Gene[Any]] and then use it as a wrapper against the single gene classes so I can have a heterogeneous array type) Is it possible to do it in Scala or I'm simply taking a wrong approach? Would it be better to use a different structure, like a Scala native covariant class?

Thanks in advance!

P.S.: I've also tried with class and abstract class instead than trait but always same results!

EDIT: with kind suggestion by Didier Dupont I came to this code:

package object ga {


  class Gene[+T](val gene: Vector[T]){

    def apply(idx: Int) = gene(idx)

    override def toString() = gene.toString

  }

  implicit def toGene[T](a: Vector[T]) = new Gene(a)

  type Genome = Array[Gene[Any]]

}

package test

import ga._

object Test {
    def main(args: Array[String]) {
        val g = Vector(1, 3, 4)

        val g2 = Vector("a", "b")

        val genome1: Genome = Array(g, g2)

        println("Genome")

        for(gene <- genome1) println(gene.gene) 
    }
}

So I now think I can put and retrieve data in different types and use them with all type checking goodies!

Answer

Didier Dupont picture Didier Dupont · Jul 30, 2012

Array is invariant because you can write in it.

Suppose you do

val typed = new Gene[String]
val untyped : Gene[Any] = typed // covariance would allow that
untyped.gene(0) = new Date(...)

this would crash (the array in your instance is an Array[String] and will not accept a Date). Which is why the compiler prevents that.

From there, it depends very much on what you intend to do with Gene. You could use a covariant type instead of Array (you may consider Vector), but that will prevent user to mutate the content, if this was what you intended. You may also have an Array inside the class, provided it is decladed private [this] (which will make it quite hard to mutate the content too). If you want the client to be allowed to mutate the content of a Gene, it will probably not be possible to make Gene covariant.