Study note of the chapter 19, 20, and 21 of the book: Programming In Scala, 4th Edition.

Chapter 19: Type Parameterization

19.1 Introduction

Type parameterization allows type contructors using generic classes and traits. A constructor type of genecric class or trait is not a regular data type. A constructed type with all specified type arguments is called a parameterized type. Varianace defines subtype relationships of parameterized types.

The List class uses a fully persistent data structure, where old versions remain available even after extensions or modifications.

Scala doesn’t have an explicit primary constructor. The primary constructor is defined implicitly by the class parameters and body. You can make it private by putting a private modifier in front of the class parameter list.

You can define a public trait and define a private class in a singleton object.

19.2 Variance

If S is a subtype of T, then

  • if F[_] is covariant/flexible, F[S] is a subtype of F[T].
  • if F[_] is contravariant, F[S] is a supertype of F[T].
  • if F[_] is novariant/regid, there is no subtype relationship between F[S] and F[T].

In Scala, Array is novariant. The asInstanceOf[Array[SuperType]] allows an array to be casted as an array of its supert type.

The compiler checks that type parmaters are used in right positions. Covriant types must be in positive position, controvriant types in negative position, and novariant types in neutral position.

To classify the positions, the compiler starts from outside to the inside. usually the nesting levels are classified the same as the enclosing level, with the following exceptional rules:

  • Method value parameter positions are classified to the flipped classification. The flip of a positive is a negative, a flip of a negative is a positive and a flip of a neutral is neutral.
  • The position of the type parameter of a method is also flipped.
  • The classification of the position of the type parameter of a type depends on the variance of the corresponding type parameter. For covariant type, the position stays the same. For contravariant, the it flips. For novariant, the position is neutral.

It is worth pointing out that var class parameters are treated as methods because the compiler generates getter methods for them. An exeption is no checking is done if the class parameters are object private by having a private[this] modifier.

The [U >: T] syntax defines a lower bound T for U. U is in a negative position and T is in a positive position. The lower bound is often used to obtain covariance.

The [U <: T] syntax defines an upper bound T for U. U is in a negative position and T is in a positive position. Upper bound is not commonly used in Scala alone because implicit parameter and context bound often provide better solutions.

Chapter 20: Abstract Members

Scala allows abstract fields include method, val, var and type. An abstract type in scala is a abstract type member of a class or trait. Abstract classes or abstract traits are not abstract types.

A type member has two purpose: a short, descriptive alias for a typoe and abstract type that must to be defined in subclasses.

An abstract val defines a value that stays the same after initialization. It cannot be overridden by an abstract var or an abstract method.

A class parameter argument is evaluated before it is passed to the class constructor. A val defintion in a subclass, is evaluated after the superclass has been iinitialized. Use pre-initialized vals or lazy vals.

Singleton objects are lazy.

Upper bound types are often used with abstract types to specify that type constraints for subclasses.

Type defined in a class or an object is a path-dependent type that can be referenced using an object identity. The type of different objects that has the same class share the same type.

However, different objects don’t share the same inner class. The Enumeration defines an inner class Value and a method Value that returns an instance of the path dependent class Value.

Chapter 21: Implicit Conversioins and Parameters

Implicits are used in three places:

  • conversions to an expected type
  • coversions to the receiver of a method call
  • implicit function parameters.

21.1 Implicit Conversion Rules

The implicit conversions are used to extend and modify the behavior of existing classes, in a controlled way.

The implicit conversions are governed by the following rules:

  • Only defintions marked implicit are available.
  • The converter must be a single identifier in scope, or be associated with the source or target type. For example, in the companion object of the source type or target type. The type could be high kind type such as F[A] and both F and A’s companion object are available.
  • Only one implicit is inserted.
  • Explicit converter has high priority.

The receiver conversion servers two purposes: integration of new classes by converting existing ones that can work with the new classes, often using existing methods; supporting new methods to use domain specific languages (DSL).

21.2 Implict Class

An implicit class is a class that is marked by the implicit keyword. According to the SIP-13-implicit class, the purpose is to provide extension methods to another type. The compiler generates an implicit conversion from the class’s constructor parametr to itself. There are three limitations:

  • It must be defined where method defintions are allowed, i.e., within some other object, class, or trait. The compiler will generate a class and an implicit method.
  • An implicit class cannot be a case class. Because the generated method has a name conficting with the generated companinon object of the case class.
  • Its constructor must have exactly one parameter. The conversion is one to one.

Implicit parameter is used in method calls and new class instances. It is used to provide more support functions for an earlier parameter list. It is better than upper bound because upper bound requires a stronger is-a constraint where the implicit parameter only provides additional functions without changing the existing types.

Though an anonymous function type is enough, it is a style rule to use a meaningful name for the implicit parameter type. This gives the purpose of the function and reduce the chances of unintentional imiplicit usage.

The standard library define an implicitly method: def implicitly[T](implicit t: T) = t. Thus t can be replaced by implicitly[T] when there is an implicit t in scope.

The context bound of [A: T] adds an implicit parameter of (implicit t: T) as the last parameter list.

Working together, the imiplict class and the implict parameter allow converting a type T to another type F to uses methods defined in F. The F is called type class.

Scala reports an error if there are multiple conversions available. Use specific rules to avoid the multiple conversion problem. An implicit conversion is more specific than antoher if

  • The argument type of the first is a subtype ofanother’s
  • Both conversions are methods, the enclosing class of the first extends the enclosing class of the latter.

Use -Xprint:typer to see the conversions being applied.