Slides8 Polymorphism PDF
Slides8 Polymorphism PDF
Lecture 8
Stephen Brookes
Parallelism
Span of Ins
fun Ins (x, Empty) = Node(Empty, x, Empty)
| Ins (x, Node(t1, y, t2)) =
case compare(x, y) of
GREATER => Node(t1, y, Ins(x, t2))
|
_
=> Node(Ins(x, t1), y, t2)
(no parallelism!)
Span of SplitAt
fun SplitAt(y, Empty) = (Empty, Empty)
| SplitAt(y, Node(t1, x, t2)) =
case compare(x, y) of
GREATER => let val (l1, r1) = SplitAt(y, t1) in (l1, Node(r1, x, t2)) end
| _
=> let val (l2, r2) = SplitAt(y, t2) in (Node(t1, x, l2), r2) end;
(no parallelism!)
Span of Merge
independent
Span of Msort
fun Msort Empty = Empty!
independent
| Msort (Node(t1, x, t2)) = !
Ins (x, Merge(Msort t1, Msort t2))
2
O(d )
SMsort(d) is O(d3)
losing balance
Msort can produce badly balanced trees
42
42
42
42 42
Msort
42
42
42
42
42
42
42
42
42
Really?
The balance assumptions are not realistic!
But we could design a rebalancing function...
fun Msort Empty = Empty!
| Msort (Node(t1, x, t2)) = !
Rebalance(Ins (x, Merge(Msort t1, Msort t2)))
Type checking
Polymorphism
Type inference
type benefits
... a static check provides a runtime guarantee
If e has type t,
e evaluates to a value of type t
assuming
termination
If d declares x of type t,
d binds x to a value of type t
static property
runtime guarantee
e has type t
if e =>* v then v : t
d declares x : t
if d =>* [x : v] then v : t
advantages
Type analysis is easy, static, cheap
and yields a runtime guarantee
would be
expensive
to keep checking
at runtime
Referential transparency
for types
type analysis
can be done statically, at parse time
possibly with
assumptions like
x:int and y:int
Typing rules
There are static (syntax-directed) rules for
e has type t
d declares x1 : t1 xk : tk
p fits type t and binds x1 : t1 xk : tk
under appropriate assumptions
about the free variables of e and d
Arithmetic rules
numerals have type int
e + e has type int
1
Similarly for e
* e2 and e1 - e2
static property
runtime behavior
21 + 21 =>* 42 : int
Boolean rules
true and false have type bool
e andalso e has type bool
1
similarly for
e1 orelse e2
similarly for
e1 = e2
e1 > e 2
static property
runtime behavior
Conditional rules
(one for each type t)
if e then e
runtime behavior
if x<y then x else =>* 4:int
if x:4 and y:5
Tuple rules
(for all types t1 and t2)
(e , e ) has type t
1
1 * t2
static property
runtime behavior
List rules
[e , ..., e ] has type t list
1
e ::e
e @e
Functions
fn x => e has type t
-> t2
if, assuming x : t1, e has type t2
1
the type of a
function expression
ensures type-safe
applicative behavior
when applied
to an argument of type t1
the result (if it terminates)
will be a value of type t2
Application
e
e2 has type t2
if e1 has type t1 -> t2 and e2 has type t1
1
evaluates to
46 : int
if f : fn y => y+42
Declarations
val x = e declares x : t
if e has type t
+ scope rules
for combining
declarations
Declarations
fun f x = e declares f : t
-> t2
if, assuming x : t1 and f : t1 -> t2, e has type t2
1
assuming that
and
f is applied to
recursive calls to f in e
an argument of type t1
have type t1 -> t2
the result of
application
will have type t2
!
let expressions
let d in e end has type t
let
fun f x = if x=0 then 1 else f(x-1)
in
f 42
end
Patterns
when p fits type t
p ::p
what bindings?
none
none
x:t
combined
combined
fits t iff
t is t1 list, p1 fits t1, p2 fits t1 list
1
clausal function
expressions
fn p
fun f p
Polymorphic types
ML has type variables
a, b, c
A type with type variables is polymorphic
a list -> a list
substitute
a type
for each type variable
split
fun split [ ] = ([ ], [ ])
| split [x] = ([x], [ ])
| split (x::y::L) =
let val (A,B) = split L in (x::A, y::B) end
declares
split : int list -> int list * int list
also (more generally!) declares
split : a list -> a list * a list
(the most general type is polymorphic)
sorting
Assuming split : int list -> int list * int list
merge : int list * int list -> int list
fun msort [ ] = [ ]
| msort [x] = [x]
| msort L = let
val (A,B) = split L
in
merge (msort A, msort B)
end
sorting
Assuming split : a list -> a list * a list
merge : int list * int list -> int list
fun msort [ ] = [ ]
| msort L = let
val (A,B) = split L
in
merge(msort A, msort B)
end