- PA 6 is out, due Fri 12/7
- NO PA 7
- We will release it, but will not be graded ...
- ... but will be on the final.
- Decorators e.g. Profiling
- ScalaCheck e.g. BST Bug
- Case Classes e.g.
IntList
- When does one object behave like another?
- When can we substitute one widget with another?
- What kinds of
Pointobjects can we send toscreenSaver?
- What is a
Point?
- Maps from names to properties
- Properties = Data + Functions (+ Objects)
Short descriptions of Objects
Maps from names to types
The actions one can perform on object
Field and Methods and their respective types
Short descriptions of Objects
Maps from names to types
Example: ticker
{ def next(): Any
def hasNext: Boolean }
Example: Int
{ def .+(other: Int) : Int // add method
def .-(other: Int) : Int // subtract method
...//etc }
Short descriptions of Objects
Maps from names to types
Example: Point
class Point {
val name : String // immutable field
var x : Int // mutable field
var y : Int // mutable field
def render(g: Graphics): Unit // method
def move(d: Dimension, del: Int) // method
}
def screenSaver(p: Point) {
// Only uses properties described in Point
}
- Method (
ScreenSaver) promises to only use attributes ofPoint
- Caller (
main) promises to only sendPointvalues
- Compiler must verify that both sides stick to contract
- Key: what exactly is a
Pointvalue?
Point ?What objects can we really call ScreenSaver with?
- Only those created by
new Point(...)?
- That seems too restrictive. What about ?
DotColorDotBoxColorBox
- Why are those also allowed?
What objects can we really call ScreenSaver with?
- Intuitively, any object with
Pointattributes
- If object has more attributes, no problem!
- This notion is called subtyping
DotColorDotBoxColorBox- .. are subtypes of
Point
S <: T means type S is a subtype of type TS<:T then we can use S object in place of T objectS<:T then S object IS A T object
- If
S <: Tthen we can useSobject in place ofTobject
- If a function expects
Tobjects, it can be called withSobjects
If S <: T then we can use S objects in place of T objects
Dot <: PointColorDot <: Dot <: PointBox <: Dot <: PointColorBox <: Dot <: Pointdef screen(p: Point) {
// Only uses properties described in Point
}
So screen can be called with Dot, ColorDot, Box, ColorBox
S <: TS <: TWhen every object of type S "IS A" object of type T
- When
Shas all attributes ofT
- (... and maybe extra ones.)
S <: TS <: T if S has all attributes of Tclass ColorDot {
val color : Color // Extra Field
val name : String
var x : Int
var y : Int
def render(g: Graphics): Unit
def move(d: Dimension, delta: Int)
}
is a (subtype of)
class Dot {
val name : String
var x : Int
var y : Int
def render(g: Graphics): Unit
def move(d: Dimension, delta: Int)
}
S <: TWidth Subtyping: when S has more attributes than T
S={ val f0: S0; val f1: S1; ... ; val fn: Sn }
T={ val f1: S1; ... ; val fn: Sn }
S <: T
Dot <: Point(extra field:name)
ColorDot <: Dot(extra field:color)
ColorDot <: Point?
S <: TTransitive Subtyping: The Is-A relation is transitive
S <: Thing
Thing <: T
S <: T
S and T, how can we know if S <: T ?We know ColorDot <: Dot. What about ColorLine and Line?
class Line {
val p1: Dot
val p2: Dot
}
and
class ColorLine {
val p1: ColorDot
val p2: ColorDot
}
S <: T ?We know ColorDot <: Dot. What about ColorLine and Line?
class Line {
val p1: Dot
val p2: Dot
}
class ColorLine {
val p1: ColorDot
val p2: ColorDot
}
For any function def foo(l: Line), is it OK to call foo with a ColorLine ?
A. Hells, Yes!
B. OMG, NO!
S and T, how can we know if S <: T ?We know ColorDot <: Dot.
What about ColorLine and Line?
For any function
def foo(l: Line)
Is it OK to call foo with a ColorLine ? Yes
- Only thing
foocan do is usep1andp2asDot...
- A
ColorLinehas those fields with properties ofColorDot
- But a
ColorDotis aDot
- Hence, we can call
foowithColorLineobjects.
S <: TDepth Subtyping: when S fields are subtypes of T fields
S={ val f1: S1; ... ; val fn: Sn }
T={ val f1: T1; ... ; val fn: Tn }
S1 <: T1andS2 <: T2and ...Sn <: Tn
S <: T
S and T, how can we know if S <: T ?
- First, we look at outputs (with the same input type)
- When is
(In => S) <: (In => T)?
- Next, lets look at inputs (with the same output type)
- When is
(S => Out) <: (T => Out)?
- Finally, we can combine the two...
- When is
(S1 => S2) <: (T1 => T2)?
S and T, how can we know if S <: T ?(In => S) <: (In => T)?(S => Out) <: (T => Out)?(S1 => S2) <: (T1 => T2)?In=>S <: In=>T ?() => ColorDot <: () => Dot ?Suppose I have a method whose input has type () => Dot
def screenSaver(f:() => Dot): Unit
Also, suppose I have a function that returns ColorDot <: Dot
val getColorDot: () => ColorDot
screenSaver(getColorDot) ?A. Yes
B. No
In=>S <: In=>T ?() => ColorDot <: () => Dot ?Suppose I have a method whose input has type () => Dot
def screenSaver(f:() => Dot): Unit
Also, suppose I have a function that returns ColorDot <: Dot
val getColorDot: () => ColorDot
screenSaver(getColorDot) ? YES
- Only thing
screenSavercan do is callfto get aDot...- ... on which it operates using
Dots properties
- When
screenSavercallsgetColorDotit gets aColorDot...- ... which is a
Dot(has all ofDots properties) ...
In => S <: In => TCan substitute/use a function with another that produces fewer outputs ...
S <: T
In => S <: In => T
ColorDot <: Dot() => ColorDot <: () => DotS and T, how can we know if S <: T ?(In => S) <: (In => T)?S <: T(S => Out) <: (T => Out)?(S1 => S2) <: (T1 => T2)?S=>Out <: T=>Out ?ColorDot=>Graphic <: Dot=>Graphic ?If I have a function whose input must be Dot => Graphic
def plotGraphic(x: Dot => Graphic): Unit
and a function from ColorDot to Graphic
val cd2g: ColorDot => Graphic
plotGraphic(cd2g) ?A. Yes
B. No
S=>Out <: T=>Out ?ColorDot=>Graphic <: Dot=>Graphic ?def plotGraphic(f: Dot => Graphic) {
val d = new Dot(0, 0) // not ColorDot
plot(f(d))
}
def cd2g(cd: ColorDot) = {
val c = cd.color
setColor(cd.color)
fillColorOval()
}
What happens if we do ...
plotGraphic(cd2g)
?
S=>Out <: T=>Out ?ColorDot=>Graphic <: Dot=>Graphic ?def plotGraphic(f: Dot => Graphic) {
val d = new Dot(0, 0) // not ColorDot
plot(f(d)) // cd2g(d) crashes!
}
def cd2g(cd: ColorDot) = {
val c = cd.color
setColor(cd.color)
fillColorOval()
}
plotGraphic(cd2g)
f(d) in plotGraphic crashes!
- As
cd2g(new Dot(0, 0))tries to access.colorfield ...- ... which does not exist in
Dot!
S=>Out <: T=>Out ?ColorDot=>Graphic <: Dot=>Graphic ?
- B. No!
- Cannot use a
ColorDot=>Graphicinstead ofDot=>Graphic
- Cannot substitute a
ColorDot=>Graphicin place ofDot=>Graphic
- !!!
S=>Out <: T=>Out ?Dot=>Graphic <: ColorDot=>Graphic ?S=>Out <: T=>Out ?Dot=>Graphic <: ColorDot=>Graphic ?Suppose we have:
def plotColorGraphic(f: ColorDot => Graphic)
def d2g(d: Dot)
Would plotGraphic(cd2g) cause any problems?
- No! Non! Nein! Nada! Nahin!
plotColorGraphicwill only callfwithColorDotvalues ...
- ... all of which are
Dotvalues ...
- ... so
d2gwill have no problem handling them!
S=>Out <: T=>Out ?Dot=>Graphic <: ColorDot=>Graphic ?Suppose we have:
def plotColorGraphic(f: ColorDot => Graphic)
def d2g(d: Dot)
Would plotGraphic(cd2g) cause any problems?
Safe to use a function that accepts more inputs
T => Out <: T => OutCan substitute/use a function with another that accepts more inputs ...
T <: S
S => Out <: T => Out
As ColorDot <: Dot
We have Dot => Graphic <: ColorDot => Graphic
S and T, how can we know if S <: T ?(In => S) <: (In => T)?S <: T(S => Out) <: (T => Out)?T <: S(In1 => Out1) <: (In2 => Out2) ?You tell me!
In1 => Out1 <: In2 => Out2
If:
A. In1 <: In2 and Out1 <: Out2
B. In2 <: In1 and Out1 <: Out2
C. In1 <: In2 and Out2 <: Out1
D. In2 <: In1 and Out2 <: Out1
You tell me!
In1 => Out1 <: In2 => Out2
We can use f1: In1 => Out1 in place of f2: In2 => Out2
If:
f1accepts more inputs
In2 <: In1
f2produces fewer outputs
Out1 <: Out2
- Solution: B.
In2 <: In1andOut1 <: Out2
S and T, how can we know if S <: T ?If
In2 <: In1Out1 <: Out2Then
In1 => Out1 <: In2 => Out2S and T, how can we know if S <: T ?If
In2 <: In1Out1 <: Out2Then
In1 => Out1 <: In2 => Out2
- Contravariant on input type
- Covariant on output type
S <: T ?We know ColorDot <: Dot. How about these two (with getter methods)
class Line {
def getP1(): Dot
def getP2(): Dot
}
and
class ColorLine {
def getP1(): ColorDot
def getP2(): ColorDot
}
Is ColorLine <: Line ? (Is it OK to call foo with a ColorLine?)
A. Yes
B. No
S and T, how can we know if S <: T ?We know ColorDot <: Dot. How about these two (with getter methods)
class Line {
def getP1(): Dot
def getP2(): Dot
}
and
class ColorLine {
def getP1(): ColorDot
def getP2(): ColorDot
}
Yes we can call foo with a ColorLine
foocan only get and usep1andp2asDot...- A
ColorLinehas those fields with properties ofColorDot- But a
ColorDotis aDot- Hence, we can call
foowithColorLineobjects.
S and T, how can we know if S <: T ?We know ColorDot <: Dot. How about these two (with getter methods)
class Line {
def getP1: () => Dot
def getP2: () => Dot
}
class ColorLine {
def getP1: () => ColorDot
def getP2: () => ColorDot
}
Lets use the rules to derive ColorLine <: Line ?
- By Function Subtyping :
() => ColorDot <: () => Dot- Hence,
ColorLine.getP1 <: Line.getP1- And,
ColorLine.getP2 <: Line.getP2- So, by Depth Subtyping:
ColorLine <: Line
S and T, how can we know if S <: T ?We know ColorDot <: Dot. How about these two (with setter methods)
class Line {
// other fields from before ...
def setP1(p: Dot): Unit
def setP2(p: Dot): Unit
}
class ColorLine {
// other fields from before ...
def setP1(p: ColorDot) : Unit
def setP2(p: ColorDot) : Unit
}
ColorLine <: Line ?For any function foo, can we call foo(new ColorLine(...)) ?
def foo(l: Line) {...}
S and T, how can we know if S <: T ?OK to call foo with a ColorLine? (for any def foo(l: Line))
class Line { def setP1(p: Dot): Unit }
class ColorLine { def setP1(p: ColorDot) : Unit }
Suppose that ColorLine <: Line ?
Consider the following foo
def foo(l: Line) { l.setP1(new Dot(0, 0)) }
Now, all hell breaks loose:
val cl = new ColorLine
foo(cl) // Typechecks
val color1 = l.getP1.color // Runtime Error!
S and T, how can we know if S <: T ?If ColorDot <: Dot then is ColorLine <: Line ?
class Line {
// from before ...
def setP1: Dot => Unit
def setP2: Dot => Unit
}
class ColorLine {
// from before ...
def setP1: ColorDot => Unit
def setP2: ColorDot => Unit
}
ColorLine.setP1not a subtype ofLine.setP1...- ... accepts fewer inputs
Can one widget be safely substituted with another?
S <: TSays when a value of type S is a value of type T
- Width Subtyping: when
Shas more attributes thanT
- Depth Subtyping: when
Sfields are subtypes ofTfields
- Transitive Subtyping: The "Is-A" relation is transitive
- Function Subtyping when
Saccepts more inputs, produces fewer outputs
If S <: T then we can use S objects in place of T objects
Dot <: PointColorDot <: Dot <: PointBox <: Dot <: PointColorBox <: Dot <: Pointdef screen(p: Point) {
// Only uses properties described in Point
}
So screen can be called with Dot, ColorDot, Box, ColorBox