Week 4 (Types and Pattern Matching)
For reasons of efficiency the Scala compiler represents the values of type scala
as 32-bit [..]
The Boolean
type maps to the JVM's primitive type boolean
abstract class Boolean {
def ifThenElse [T ](t : ⇒ T , e : ⇒ T ): T
def && (x : => Boolean ): Boolean = ifThenElse(x, false )
def || (x : => Boolean ): Boolean = ifThenElse(true , x)
def unary_!: Boolean = ifThenElse(false , true )
def < (x : => Boolean ): Boolean = ifThenElse(False , x)
...
}
Boolean Constants
object true extends Boolean {
def isThenElse [T ](t ⇒ T , e : ⇒ T ) = t
}
object false extends Boolean {
def isThenElse [T ](t ⇒ T , e : ⇒ T ) = e
}
Class overloading
class Int {
def + (that : Double ): Double
def + (that : Float ): Float
def + (that : Long ): Long
...
}
Provide an implementation of the class Nat
that represents non-negative integers.
abstract class Nat {
def isZero : Boolean
def predecessor : Nat
def successor : Nat
}
Do not use standard classes in the implementation
object Zero extends Nat {}
class Succ (n : Nat ) extends Nat {}
Functions as Object (4.2)
Can we implement functions as Objects? — Function values are treated as objects in Scala.
The A ⇒ B
is an abbreviation for the class scala.Function1[A, B]
trait Function1 [A , B ] {
def apply (x : A ): B
}
val f = new Function1 [Int , Int ] {
def apply (x : Int ) = x * x
}
f.apply(7 )
Method is not itself a function value – "eta-expansion".
object List {
// List(1,2) = List.apply(1, 2)
def apply [T ](x : T ): List [T ] = ...
def assertAllPos [S <: InstSet ](r : S ): S = ...
[S <: IntSet]
is an "upper bound" of the type parameter S
– ranges only suptypes
[S >: NonEmpty]
is an "lower bound" – ranges only supertypes
[S >: NonEmpty <: IntSet]
– mixed bound
NonEmpty[] :< IstSet[]
Types for which this relationship holds is a "covariant".
The Liskov Substitution Principle
"If A <: B
, then everything one can to do with A, could be done with B."
Immutable types can be covariant, if some conditions are met.
class C[+A] { ... } – C is covariant
class C[-A] { ... } – C is contravariant
if A1 <: A2
and B1 <: B2
, then A1 ⇒ B1 <: A2 ⇒ B2
trait Function1[-T, +U] {
def apply(x: T): U
}
trait List [+ T ] {
...
// def prepend(elem: T): List[T] = new Cons(elem, this) // ← type error: `val xs: List[IntSet]; xs.prepend(Empty): List[IntSet]`
def prepend [U >: T ] (elem : U ): List [U ] = new Cons (elem, this )
}
object Nil extends List [Nothind ] {
def isEmpty = true
def head : Nothing = throw new NoSuchElementException (" Nil.head" )
def tail : Nothing = throw new NoSuchElementException (" Nil.tail" )
}
object test {
val x : List [String ] = Nil
}
def isInstanceOf [T ]: Boolean // `x instanceof T`
def asInstanceOf [T ]: T // ← type casting in Java: `(T) x`
trait Expr { .. }
case class Number (x : Int ) extends Expr { .. }
case class Sum (e1 : Expr , e2 : Expr ) extends Expr { .. }
We can write Number(1)
instead of new Number(1)
. But classes are empty.
"Pattern matching" is a generalisation of switch
from C/Java to class hierarchies.
e match {
case pat1 ⇒ expr1
...
case patN ⇒ exprN
}
Patterns:
constructors, e.g. Number
, Sum
variables, e.g. n
, e1
, `e2
wildcard patterns _
constants, e.g. 1
, true
Names of constants begin with a capital letter, except true
, nil
, etc.
trait Expr {
def eval : Int = this match {
case Number (n) ⇒ n
case Sum (e1, e3) ⇒ e1.eval() + e2.eval()
}
}
def show (e : Expr ): String = e match {
case Number (x) => x.toString
case Sum (l, r) => show(l) + " + " + show(r)
}
Lists as a part of collections.
to define a list: List(x1, .., xN)
Lists vs Arrays:
lists are immutable
list are recursive, arrays are flat.
val fruits : List [String ] = List (" apple" , " orange" )
Constructors of List
fruit = " apple" :: (" orange :: (" pears " :: Nil))
empty = Nill
A :: B :: C
is interpreted as A :: (B :: C)
.
Operations on List
head
– the first element of the list
till
– the list composed of all els except the first
isEmpty
– true
if the list is empty
List Patterns
Nil
– the Nil constant.
p :: ps
– a pattern that match a list with a head
matching p
and a tail
matching ps
.
List(p1, ..., pN)
– same as p :: .. :: pN :: Nil
x :: y :: List(xs, ys) :: zs
, L >= 3
Sorting List
"Inserting Sort":
def isort (xs : List [Int ]): List [Int ] = xs match {
case List () ⇒ List ()
case y :: ys ⇒ insert(y, isort(ys))
}
def insert (x : Int , xs : List [Int ]): List [Int ] = xs match {
case List () ⇒ List (x)
case y :: ys ⇒ if (x < y) x :: xs else y :: insert(x, ys)
}