Search code examples
scalagenericscovariance

What is the type of a covariant generic list that contains different types?


I have played around with generics in Scala and created the following:

package playingaround.playground

object myList extends App {

  abstract class MyList[+T] {
    def head: T
    def tail: MyList[T]
    def isEmpty: Boolean
    def add[C >: T](int: C): MyList[C]
    def printElements: String
  }

  object Empty extends MyList[Nothing] {
    override def head: Nothing = throw new NoSuchElementException
    override def tail: Nothing = throw new NoSuchElementException
    override def isEmpty: Boolean = true
    override def add[C >: Nothing](int: C): MyList[C] = new Cons(int, Empty)
    override def printElements: String = ""
  }

  class Cons[+T](h: T, t: MyList[T]) extends MyList[T] {
    override def head: T = h
    override def tail: MyList[T] = t
    override def isEmpty: Boolean = false
    override def add[C >: T](int: C): MyList[C] = new Cons(int, this)
    override def printElements: String = if (t.isEmpty) "" + h else h + " " + t.printElements
  }

  // Define lists
  val intList = new Cons(1, new Cons(2, new Cons(3, Empty)))
  val strList = new Cons("a", new Cons("b", new Cons("c", Empty)))
  val mixList = new Cons("a", new Cons(2, new Cons(0.5, Empty)))
  // Print them
  println(mixList.printElements) // a 2 0.5
}

What confuses me is how "mixList" works. What type is mixList? And if I were to define it's type explicitly in the val, what would I have to define it as?


Solution

  • I'll just add that you can always see the type of something in REPL

    scala> abstract class MyList[+T]
    defined class MyList
    
    scala> object Empty extends MyList[Nothing]
    defined object Empty
    
    scala> class Cons[+T](h: T, t: MyList[T]) extends MyList[T]
    defined class Cons
    
    scala> val mixList = new Cons("a", new Cons(2, new Cons(0.5, Empty)))
    mixList: Cons[Any] = Cons@67d32a54
    

    Alternatively you can see the type with the following function

    import scala.reflect.runtime.universe.{TypeTag, Type, typeOf}
    def getType[A: TypeTag](a: A): Type = typeOf[A]
    
    println(getType(mixList)) // Cons[Any]
    

    By the way, in Dotty (Scala 3) inferred type of mixList is also Cons[Any] but you can annotate it manually with type Cons[String | Int | Double] or MyList[String | Int | Double].