Reputation: 2316
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
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
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