case classesval)- case class C (x:Int, y:String)
- val c:C = C (5, "hello")
- val a:Int = c.x
- c.x = 6 // error: reassignment to val
toString implementationapply method
unapply method / extractors in textbookenum lists disjoint alternatives- enum Color:
- case Blue
- case White
- enum Expr:
- case Number(x: Int)
- case Plus(l: Expr, r: Expr)
- ...
Create instances
- val b = Color.Blue
- import Expr.*
- val three = Number(3)
- val plus = Plus(three, Number(5))
- def eval(e: Expr) : Int = e match
- case Number(x) => x
- case Plus(l, r) => eval(l) + eval(r)
- ...
- end eval
eval- // (3+5)*2
- val v = Times(Plus(Number(3), Number(5)), Number(2))
- val i: Int = eval(v) // i==16
- enum Expr:
- // Arithmetic expressions
- case Number(n:Int)
- case Plus(l:Expr, r:Expr)
- case Minus(l:Expr, r:Expr)
- case Times(l:Expr, r:Expr)
- // Identifiers a,...,x,y,z
- case Ident(c:Char)
- // Comparisons l>=r
- case GEq(l:Expr, r:Expr)
-
- // Parenthesized expressions (e)
- case Par(e:Expr)
- // Programs
- // Assignment x := v
- case Assign(x:Ident, v:Expr)
-
- // Conditional if p then t else e
- case If(p:Expr, t:Expr, e:Expr)
-
- // Loop while p do b
- case While(p:Expr, b:Expr)
-
- // Sequential composition p ; r
- case Seq(p:Expr, r:Expr)
- end Expr
- val abs = (n: Expr) =>
- Seq(
- Assign(Ident('i'), n), // i := <n>
- If( // if
- GEq(Ident('i'), Number(0)), // i >= 0
- Assign(Ident('i'), Ident('i')), // then i := i
- Assign(Ident('i'), Minus(Number(0), Ident('i'))) // else i := 0-i
- ) // fi
- )
- type Store = Map[Char, Int]
- def eval(e: Expr, s: Store): (Int, Store)
- def eval(e: Expr, s: Store): (Int, Store) = e match
- case Number(n) => (n, s)
- case Plus(e1, e2) =>
- val (v1, s1) = eval(e1, s)
- val (v2, s2) = eval(e2, s1)
- (v1+v2, s2)
-
- ...
-
- case Par(e) => eval(e, s)
- def eval(e: Expr, s: Store): (Int, Store) = e match
- ...
- case Ident(c) => (s(c), s)
-
- case Assign(Ident(x), e) =>
- val (v,s1) = eval(e, s)
- (v, s1.updated(x,v))
-
- case Seq(e1, e2) =>
- val (v1, s1) = eval(e1, s)
- val (v2, s2) = eval(e2, s1)
- (v2, s2)
Reduction rules for conditionals and loops
- def eval(e: Expr, s: Store): (Int, Store) = e match
- ...
- case GEq(e1, e2) =>
- val (v1, s1) = eval(e1, s)
- val (v2, s2) = eval(e2, s1)
- (math.max(0,v1-v2+1), s2)
-
- case If(e1, e2, e3) =>
- val (v1, s1) = eval(e1, s)
- if v1!=0
- then eval(e2, s1)
- else eval(e3, s1)
- case w@While(e1, e2) =>
- val (v1, s1) = eval(e1, s)
- if v1==0
- then (0, s1)
- else eval(Seq(e2, w), s1)
Algebraic data types: sum of products
Product types
- val product = (1, "hello", List(1, 2, 3))
Sum types
- class A; class B extends A; class C extends A
enum and case classes- enum Sum:
- case Product1(i: Int, s: String)
- case Product2(i: Int, j: Int, k: Int)
- ...
PeanoNat- enum PeanoNat:
- case Zero
- case Succ(n: PeanoNat)
PeanoNat to Int- import PeanoNat.*
- def peano2int (p: PeanoNat, result: Int = 0): Int = p match
- case Zero => result
- case Succ(n) => peano2int (n, result+1) // tail-recursive
- val q = Succ(Succ(Succ(Zero))) // val q: Peano = ...
- peano2int(q) // : Int = 3
Which case classes and case objects?
Empty list and a Cons cell of at least one element- enum MyList:
- case Empty
- case Cons (head:Int, tail:MyList)
- enum MyList:
- case Empty
- case Cons (head:Int, tail:MyList)
Create an empty list?
Simply use Empty
- import MyList.*
- val xs = Empty
Create an instance of a list?
Nest Cons and terminate with Empty
- import MyList.*
- val xs = Cons (1, Cons(2, Cons(3, Empty)))
- enum MyList:
- case Empty
- case Cons (head:Int, tail:MyList)
- object MyList:
- def create(elems: Int*) =
- elems.foldRight(Empty)((e, l) => Cons(e, l))
Create an instance of a list?
Use method create
- import MyList.*
- // val xs = Cons (1, Cons(2, Cons(3, Empty)))
- val xs = MyList.create(1, 2, 3)
Generalize to any element type?
- enum MyList[+X]:
- case Empty
- case Cons (head:X, tail:MyList[X])
Compute the length of such a list?
- def length [X] (xs:MyList[X]): Int = xs match
- case MyList.Empty => 0
- case MyList.Cons(a,as) => 1 + length(as)
- def length [X] (xs:MyList[X], result:Int = 0): Int = xs match
- case MyList.Empty => result
- case MyList.Cons(a,as) => length(as, result+1)
- enum Tree[X]:
- case Leaf (data:X)
- case Node (l:Tree[X], f:(X,X)=>X, r:Tree[X])
- def fold [X] (t: Tree[X]) : X = t match
- case Leaf(x) => x
- case Node(l, f, r) => f(fold(l), fold(r))
# <span class="fa-stack"><i class="fa-solid fa-circle fa-stack-2x"></i><i class="fa-solid fa-dumbbell fa-stack-1x fa-inverse"></i></span> Pattern Matching with Complex Datatypes <div class="grid grid-cols-2 gap-4"> <div> - Recursively traverse a data structure (recall [Formal Semantics](https://reed.cs.depaul.edu/efredericks/csc447/slides/html/07-formalsemantics.html#8)) ```scala abstract class Expr class Number(val x: Int) extends Expr class Plus(val l: Expr, val r: Expr) extends Expr ... ``` * Evaluate an expression ```scala def eval(e: Expr) : Int = e match case n: Number => n.x case n: Plus => eval(n.l) + eval(n.r) ... end eval ``` </div> <div> * :fa fa-circle-question: What if `Number(...)` and `Plus(...)` were l-values? ```scala def eval(e: Expr) : Int = e match case Number(x) => x case Plus(l, r) => eval(l) + eval(r) ... end eval ``` * Scala `enum` to express algebraic data types </div> </div> ---