4.1 Objects Everywhere
Pure Object Orientation:
A pure object-oriented language is one in which every value is an object.
If the language is based on classes, this means that the type of each value is a class.
4.2 Functions as Objects
(x: Int) => x * x
is expanded to:
{ class AnonFun extends Function1[Int, Int] {
def apply(x: Int) = x * x
}
new AnonFun
}
or, shorter, using anonymous class syntax:
new Function1[Int, Int] {
def apply(x: Int) = x * x
}
Note that a method such as
def f(x: Int): Boolean = ...
is not itself a function value.
But if f is used in a place where a Function type is expected, it is converted automatically to the function value
(x: Int) => f(x)
or, expanded:
new Function1[Int, Boolean] { def apply(x: Int) = f(x) }
eta-expansion: the transformation that converts the name F to this anonymous function, is called in lambdacalculus, eta expansion.
4.3 Subtyping and Generics
- S <: T means: S is a subtype of T, and
- S >: T means: S is a supertype of T, or T is a subtype of S.
[S >: NonEmpty <: IntSet]
would restrict S any type on the interval between NonEmpty and IntSet.
The Linkov Substitution Principle:
The following principle, started by Barbara Liskov, tell us when a type can be a subtype of another.
If A <: B, then everything one can to do with a value of type B one should also be able to do with a value of type A.
The actual definition Liskov used is a bit more formal. It says:
Let q(x) be a property provable about objects x of type B. Then q(y) should be provable for objects y of type A where A <: B.
4.4 Variance
Say C[T] is a parameterized type and A,B are types such that A <: B.
In general, there are three possible relationships between C[A] and C[B]:
C[A] <: C[B] C is covariant
C[A] >: C[B] C is contravariant
neither C[A] nor C[B] is a subtype of the other C is nonvariant
Scala lets you declare the variance of a type by annotating the type parameter:
class C[+A] { ... } C is covariant
class C[-A] { ... } C is contravariant
class C[A] { ... } C is nonvariant
Typing Rules for Functions
Generaly, we have the following rulw for subtyping between function types:
if A2 <: A1 and B1 <: B2, then
(A1 => B1) <: (A2 => B2)
So functions are contravariant in their argument type(s) and covariant in their result type.
- covariant type parameters can only appear in method results
- contravariant type parameters can only appear in method parameters
- invariant type parameters can appear anywhere
4.5 Decomposition
4.6 Pattern Matching
Match Syntax:
Rules:
- match is followed by a sequence of cases, pat => expr.
- Each case associates an expression expr with a pattern pat.
- A MarchError exception si thrown if no pattern matches the value of the selector.
e match { case pat1 => expr1
... ...
case patn => exprn }
What Do Patterns Match?
- A constructor pattern C(p1, ..., pn) matches all the values of type C (or a subtype) that have been constructed with arguments matching the patterns p1, ..., pn.
- A variable pattern x matches any value, and binds the name of the variable to this value
- A constant pattern c matches values that are equal to c( in the sense of ==)
4.7 Lists
The list is a fundamental data structure in functional programming.
A list having x1, ..., xn as elements is written List(x1, ..., xn)