o
Satisfies a Type T
?T
describes a set of values that behave in a certain way.S => T
is a contract between caller and callee:S
.T
.Say we have a function of type T => U
val foo : T => U = x => ...
and an object of type S
.
val o : S = { ... }
o
to foo
?foo(o)
S
is a subtype of T
!When can S
-typed values can be substituted for T
-typed values?
e
has type S
, andS
is a subtype of T
(written S
<: T
)e
can be used in any context that requires a T
.S
<: T
?ticker
Type From Last Time
type t = { def next(): Any
; def hasNext: Boolean }
s
<: t
?type s = { def hasNext: Boolean
; def next(): Any }
type t = { def next(): Any
; def hasNext: Boolean }
s
<: t
?type s = { def next(): Any
; def hasNext: Boolean }
type t = { def hasNext: Boolean
; def next(): Any;
; def foreach(f: Any => Unit): Unit }
s
does not guarantee foreach
method, which t
requires.s
<: t
?type s = { def hasNext: String
; def next(): Any;
; def foreach(f: Any => Unit): Unit }
type t = { def next(): Any
; def hasNext: Boolean }
A: Yes B: No
s
<: t
?type s = { def hasNext: String
; def next(): Any;
; def foreach(f: Any => Unit): Unit }
type t = { def next(): Any
; def hasNext: Boolean }
s
provides an extra members which t
doesn't need. That's okay...s
has hasNext
, but it's String
, not Boolean
.When structural type S
has more attributes than T
S
= { def f1: S1; ... ; def fn: Sn; def f0:S0 }
T
= { def f1: S1; ... ; def fn: Sn }
S
<: T
s
<: t
?type s = { def next(): Int;
; def hasNext: Boolean
; def foreach(f: Any => Unit): Unit }
type t = { def next(): Any
; def hasNext: Boolean }
A: Yes B: No
s
<: t
?type s = { def next(): Int;
; def hasNext: Boolean
; def foreach(f: Any => Unit): Unit }
type t = { def next(): Any
; def hasNext: Boolean }
next
method in s
returns Int
rather than Any
...When S
fields are subtypes of T
fields
S
= { def f1: S1; ... ; def fn: Sn }
T
= { def f1: T1; ... ; def fn: Tn }
S1 <: T1
and ... and Sn <: Tn
S <: T
So far, three subtyping rules:
Permutation : order of members doesn't matter
Width : extra members are okay
Depth : member types can be "more specific" than required
The subtyping (or "is-a") relation is reflexive
T
T
<: T
The subtyping (or "is-a") relation is transitive
S
<: T
T
<: U
S
<: U
How can we ask Scala if an expression e
has a certain type T
?
How can we ask Scala if an expression e
has a certain type T
?
scala> val blah: T = e
e
will be analyzed and (possibly) assigned some type S
.S
<: T
.scala> val blah: Int = 1
blah: Int = 1
scala> val blah: Boolean = 1
<console>:7: error: type mismatch;
found : Int(1)
required: Boolean
val blah: Boolean = 1
^
scala> val blah: Any = 1
blah: Any = 1
T
<: Any
for all T
.Can make these "subtyping queries" more directly:
scala> (1:Int, 1:Any)
res20: (Int, Any) = (1, 1)
scala> 1:Boolean
... error ...
e:T
declares an expected type for e
.val
/var
/def
Declarations?type t1 = { val i : Int } ; type t3 = { def i : Int }
type t2 = { var i : Int } ; type t4 = { def i() : Int }
type t1 = { val i : Int } ; type t3 = { def i : Int }
type t2 = { var i : Int } ; type t4 = { def i() : Int }
object o1 { val i = 0 } ; object o3 { def i = 0 }
object o2 { var i = 0 } ; object o4 { def i() = 0 }
o1:t1
is well-typedo2:t2
is well-typedo3:t3
is well-typedo4:t4
is well-typedtype t1 = { val i : Int } ; type t3 = { def i : Int }
type t2 = { var i : Int } ; type t4 = { def i() : Int }
object o1 { val i = 0 } ; object o3 { def i = 0 }
object o2 { var i = 0 } ; object o4 { def i() = 0 }
scala> o1:t2 // Is this expression well-typed?
A: Yes B: No
var
means that the "context" (caller) may assign to i
...val
means immutable. So NO!type t1 = { val i : Int } ; type t3 = { def i : Int }
type t2 = { var i : Int } ; type t4 = { def i() : Int }
object o1 { val i = 0 } ; object o3 { def i = 0 }
object o2 { var i = 0 } ; object o4 { def i() = 0 }
scala> o1:t3 // Is this expression well-typed?
A: Yes B: No
def
with no parameter list must be called with no parens.type t1 = { val i : Int } ; type t3 = { def i : Int }
type t2 = { var i : Int } ; type t4 = { def i() : Int }
object o1 { val i = 0 } ; object o3 { def i = 0 }
object o2 { var i = 0 } ; object o4 { def i() = 0 }
scala> o1:t4 // Is this expression well-typed?
A: Yes B: No
def
with empty parameter list may be called with no parens.type t1 = { val i : Int } ; type t3 = { def i : Int }
type t2 = { var i : Int } ; type t4 = { def i() : Int }
object o1 { val i = 0 } ; object o3 { def i = 0 }
object o2 { var i = 0 } ; object o4 { def i() = 0 }
scala> o2:t1 // Is this expression well-typed?
val
is required, so the "context" (caller) will only read...var
should be okay.val
cannot be provided where var
requiredvar
cannot be provided where val
requiredval
can be provided where def
requiredvar
can be provided where def
requiredval
cannot be provided where def(...)
required (not surprising)var
cannot be provided where def(...)
required (not surprising)Two structurally identically classes:
class C1 { var i:Int = 0 } ; class C2 { var i:Int = 0 }
val x1 = new C1 ; val x2 = new C2
x1:C1
and x2:C2
.x1:C2
?Two structurally identically classes:
class C1 { val i:Int = 0 } ; class C2 { val i:Int = 0 }
val x1 = new C1 ; val x2 = new C2
Of course, x1:C1
and x2:C2
.
What about x1:C2
?
scala> x1:C2
<console>:11: error: type mismatch;
found : C1
required: C2
new C1
has type C1
When one class instance is an instance of another class
C
is a descendant of D
in the class hierarchyC
<: D
class C1 { val i:Int = 0 } ; class C2 { val i:Int = 0 }
C1
and C2
and have the same structure, but...C1
<: C2
does not holdC2
<: C1
does not holdAny ideas?
???
???
???
C
, just refer to the name C
!abstract class IntList
{ val data: Int; val next: Option[IntList] }
trait inti { val i : Int }
class D1 extends inti { val i = 0 }
class D2 extends inti { val i = 0 }
val y1 = new D1 ; val y2 = new D2
y1:D1
and y1:inti
and y2:D2
and y1:inti
y1:D2
and y2:D1
class C1 { val i = 0 } ; class C2 { val i = 0 }
trait inti { val i : Int }
val z1 = new C1 with inti ; val z2 = new C2 with inti
// z1 : C1 with inti // z2 : C2 with inti
Note: the types of z1
and z2
mention with
.
z1:C1
and z1:inti
and z2:C2
and z1:inti
z1:C2
and z2:C1
class C1 { val i:Int = 0 }
val x1 = new C1
scala> x1:{val i:Int}
res60: AnyRef{val i: Int} = C1@13c59de
scala> x1:{}
res61: AnyRef{} = C1@1b95cf5
def applyFunc(f: Double => Double) = 3.14 + (f (3.14))
def addOne(i: Int) = 1 + i
val res = applyFunc(addOne)
A: Type Error (in applyFunc
)
B: Type Error (in call to applyFunc
)
C: res: Int = 7
D: res: Double = 7.28
E: Run-Time Exception
def applyFunc(f: Double => Double) = 3.14 + (f (3.14))
def addOne(i: Int) = 1 + i
val res = applyFunc(addOne)
addOne
must be subtype of f
.(Int => Int)
<: (Double => Double)
?When is (S => T1)
<: (S => T2)
?
T2
.T1
.T1
<: T2
.When is (S1 => T)
<: (S2 => T)
?
S2
.S1
.S2
<: S1
. Whoa, weird!When is (In1 => Out1)
<: (In2 => Out2)
?
In2 <: In1
Out1 <: Out2
(In1 => Out1)
<: (In2 => Out2)
Courtesy: Ranjit Jhala
Courtesy: Ranjit Jhala
Courtesy: Ranjit Jhala
Courtesy: Ranjit Jhala
def applyFunc(f: Double => Double) = 3.14 + (f (3.14))
def addOne(i: Int) = 1 + i
val res = applyFunc(addOne)
For call to succeed, type of addOne
must be subtype of f
.
So, is (Int => Int)
<: (Double => Double)
?
Int
is a subtype of Double
).Double
is not a subtype of Int
).applyFunc
)class Parent { def foo(p:Parent) = () }
class Child extends Parent
{ override def foo(p:Parent) = () }
Child
class definition to be allowed,foo
method must be a subtype of Parent
's.(Parent => Unit)
<: (Parent => Unit)
to be true.class Parent { def foo(p:Parent) = () }
class ProblemChild extends Parent
{ override def foo(c:ProblemChild) = () }
ProblemChild
class definition to be allowed,foo
method must be a subtype of Parent
's.(ProblemChild => Unit)
<: (Parent => Unit)
to be true.Parent
<: ProblemChild
) by contravariance...ProblemChild
is not a well-typed subclass.