Type Parameterization in Scala
2 min readMay 14, 2021
Information hiding
- Private constructors
class Queue[T] private (
private val leading: List[T],
private val trailing: List[T]
)
Plus auxiliary constructor:
def this() = this(Nil, Nil)
def this(elems: T*) = this(elems.toList, Nil)
Or plus factory method:
object Queue {
def apply[T](xs: T*) = new Queue[T](xs.toList, Nil)
}
2. Alternative: private class
- Only export a trait that reveals the public interface of the class.
trait Queue[T] {
def head: T
def tail: Queue[T]
def enqueue(x: T): Queue[T]
}object Queue {
def apply[T](xs: T*):Queue[T] = new QueueImpl[T](xs.toList, Nil)
private class QueueImpl[T] (
private val leading: List[T]
private val trailing: List[T]
} extends Queue[T] {
def mirror = ......
def head: T = ......
def tail: QueueImpl[T] = ......
def enqueue(x:T) = ......
}
Some definitions
- Queue above is a generic trait and a type constructor and Queue[String] is a type.
- If S is a subtype of type T, then should Queue[S] be considered a subtype of Queue[T]? —Yes → Queue is covariant | No → Queue is nonvariant.
- Generic types have by default nonvariant subtyping.
trait Queue[+T] { ... } // + indicates covariant subtyping
trait Queue[-T] { ... } // - indicates contravariant subtyping
Queue is contravariant means that if T is a subtype of type S, then Queue[S] is a subtype of Queue[T].
Checking variance annotations
Some covariance might lead to runtime error. For example:
class Cell[+T](init: T) {
private var current = init // violates CONDITION
def get = current
def set(x: T) = { current = x }
}
val c1 = new Cell[String]("abc")
val c2: Cell[Any] = c1
c2.set(1)
val s: String = c1.getclass StrangeIntQueue extends Queue[Int] {
override def enqueue(x: Int) = { // violates CONDITION
println(math.sqrt(x))
super.enqueue(x)
}
}
val x: Queue[Any] = new StrangeIntQueue
x.enqueue("abc")
What’s wrong? As soon as a generic parameter type appears as the type of a method parameter [CONDITION], the containing class or trait may not be covariant in that type parameter.
N.B.
- a reassignable field is treated in Scala as a getter method and a setter method.
- Object private definitions (which can be accessed only from within the object in which they are defined) are omitted Scala check variance annotations.
// Object private definition
private[this] var leading: List[T]