Study note of the chapter 19, 20, and 21 of the book: Programming In Scala, 4th Edition.
Chapter 19: Type Parameterization
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
Varianace defines subtype relationships of parameterized types.
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.
S is a subtype of
F[S]is a subtype of
F[S]is a supertype of
regid, there is no subtype relationship between
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
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
flippedclassification. 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
- 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
[U >: T] syntax defines a lower bound
U is in a negative position and
T is in a positive position. The lower bound is often used to obtain covariance.
[U <: T] syntax defines an upper bound
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
20.1 Abstract Memebers
Scala allows abstract fields include method, val, var and type. An
abstract type in scala is a
type T member, without defintion, of a class or trait. Abstract classes or abstract traits are not abstract types.
A type member has two purpose: (1) a short, descriptive alias for a concrete type and (2) an 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
Singleton objects are lazy.
Upper bound types are often used with abstract types to specify that type constraints for subclasses.
20.2 Path-dependent Type
Type defined in a class or an object is a
path-dependent type that can be referenced using an object identity, for example
b.T. The type of different objects that has the same class share the same path-dependent type.
Inner classes are path-dependent type. However, different objects don’t share the same inner class. All objects’ inner class are subtypes of their general type
# is used for classes and
. is used for bojects. An inner class must hold a reference to an enclosing outer class instance to allow the inner class to access members of its outer class.
Enumeration defines an inner class
Value and a method
Value that returns an instance of the path dependent class
Value. Therefore each subtype of
Enumeration has a different path-dependent type of the
Value inner class.
When a class inherits from another, the first is sadi to be a
nominal subtype of the other one. It is nominal because it has a name. Scala also supports
structural subtype via
refinement type. A structural subtype has compatible members to its parent. to use refinement type, simple write the base type, followed by a sequence of members listed in curly braces.
Chapter 21: Implicit Conversioins and Parameters
Implict defintions are converts that the compiler inserts into a program in order to fix a type errors. 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 governed by the following rules:
- Only defintions marked
- 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
A’s companion object are available.
- Only one implicit is inserted.
- Explicit converter has high priority. Insert happens only when there is a type error.
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
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. Therefore defining an implicit class also defines a conversion method and it comes with 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.
21.3 Implicit Parameter
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
def implicitly[T](implicit t: T) = t. Thus
t can be replaced by
implicitly[T] when there is an implicit
t in scope.
context bound of
[A: T] adds an implicit parameter of
(implicit t: T[A]) 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[T] is called a
21.4 Resolving Ambiguity
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.
To use the second rule, it is often to define low prirority more-general conversions in a base type and high priority more-specific conversions in a subtype.
-Xprint:typer to see the implicit converters being applied.