alt-f4
alt-f4

Reputation: 2316

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?

Upvotes: 0

Views: 96

Answers (2)

Dmytro Mitin
Dmytro Mitin

Reputation: 51658

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].

Upvotes: 3

Tim
Tim

Reputation: 27356

maxList is Cons[Any]

Covariance basically means that the type matching for T also works for MyList[T]. In other words, if T1 is a subtype of T2, you can use a MyList[T1] where MyList[T2] is required.

So when compiling a call to

Cons[+T](h: T, t: MyList[T])

the compiler will use the same type matching as it would for

Cons[+T](h: T, t: T)

It will pick a T that is compatible with the types of both h and t. This process is repeated for each call to Cons so that the resulting type is one that is compatible with all the types of all the h values in all the Cons objects.

In this case, the only type that is compatible with "a", 2, and 0.5 is Any because that is the only type compatible with String, Int, and Double.

Upvotes: 2

Related Questions