Slides16 Functors PDF
Slides16 Functors PDF
Stephen Brookes
announcements
Midterm exams: pick up in lab A 87.5
B
80.0
Average score on exam: 72.0
C 69.5
D 60.0
Median score: 73.0
Midterm grade based on HW + MIDTERM
Labs will count toward final grade
last time
!
Modular programming
Signatures and structures
Information hiding
our example
The signature for ARITH specifies an
abstract data type
A type integer
Equipped with basic operations
initialize
rep
:
int
->
integer
fun rep 0 = [ ] !
| rep n = (n mod 10) :: rep(n div 10)!
(abbreviation)
fun rep 0 = [ ] !
| rep n = (n mod 10) :: rep(n div 10)!
fun mult ([ ], _) = [ ]
| mult(_, [ ]) = [ ]
| mult (p::ps, qs) = add (times(p, qs), 0 :: mult (ps, qs))
!
correctness
Dec implements non-negative integers
invariant
To prove correctness we introduce a
representation invariant
inv(L) :: L is a list of decimal digits
This is a property guaranteed to hold,
for all values of type integer constructible from
rep(n) with n0, using add, mult
abstraction function
And we (re-)introduce an abstraction function
eval : integer -> int
to explain how
integer values represent int values
eval L = the int represented by L
(only needs to make sense on values that satisfy inv)
inv
(* inv : int list -> bool *)!
fun inv [ ] = true!
| inv (d::L) = (0 <= d andalso d <= 9) andalso inv L
eval
(* eval : int list -> int *)!
fun eval [ ] = 0!
| eval (d::L) = d + 10 * eval(L)
termination
For all L:int list,
For all L:int list,
For all n0,
inv(L) terminates
eval(L) terminates
rep(n) terminates
invariance
Proofs?
invariance
!
LEMMA
correctness
For all ps, qs : int list,
then
correctness
If ps and qs satisfy inv
then
eval(add(ps, qs)) = (eval ps) + (eval qs)
LEMMA
If ps satisfies inv
and 0 c 9,
If ps satisfies inv
and 0 c 9,
By induction on ps
an alternative
structure Dec2 : ARITH =!
struct!
type digit = int !
type integer = digit list!
What changes?
Anything significant?
Dec.rep 0 = [ ]
Dec2.rep 0 = [0]
But users ascribing opaquely to ARITH
cannot distinguish between Dec and Dec2
Dec.display (Dec.rep 0) = "0"
Dec2.display (Dec2.rep 0) = "0"
questions
Why decimal?
Could have used binary
binary digits
structure Bin : ARITH =!
struct!
type digit = int !
type integer = digit list!
just replace 10 by 2
in the code for Dec
fun rep 0 = [ ] !
| rep n = (n mod 2) :: rep(n div 2)!
!
...
end
correctness
Bin implements non-negative integers
invariant
To prove correctness we introduce a
representation invariant
inv2 : int list -> bool
inv2(L) = true
iff
every item in L is a binary digit
01
abstraction
And we define an abstraction function
eval2 : integer -> int
For all L : int list such that inv2(L) = true,
eval2 L = the int value represented
in binary by L
correctness
and eval2(rep n) = n
meta-proof
A correctness proof for Dec,
with 10 replaced by 2,
yields a correctness proof for Bin
functors
An ML functor is a
end
BASE
signature BASE =!
sig!
val base : int!
end
Digits
functor Digits(B : BASE) : ARITH =!
struct!
val b = B.base!
type digit = int (* use 0 through b-1 *)!
type integer = digit list!
!
fun rep 0 = [ ]!
| rep n = (n mod b) :: rep(n div b)!
!
end
fun mult ([ ], _) = [ ]
| mult (_, [ ]) = [ ]
| mult (p::ps, qs) = add (times p qs, 0 :: mult (ps,qs))
fun display L = foldl (fn (d, s) => Int.toString d ^ s) "" L
end
using Digits
functor applied to argument
an
anonymous
structure
whats visible?
functor Digits(B : BASE) : ARITH =!
struct!
val b = B.base;!
type digit = int (* use 0 through b-1 *)!
type integer = digit list!
...
signature ARITH =!
sig!
type integer!
...!
end;
- it : int list!
val it = [0,1,0,1,0,1] : int list
oops!
- Bin.add(Dec.rep 42, Dec.rep 42);!
!
solution (1)
In the functor body, make integer a
fun rep 0 = D [ ] !
| rep n = let val (D L) = rep(n div b) in D((n mod b)::L) end!
...
solution (1)
!
!
fun rep 0 = D [ ]
| rep n = let val (D L) = rep(n div b) in D ((n mod b) :: L) end
(* carry : digit * digit list -> digit list *)
fun carry (0, ps) = ps
| carry (c, [ ]) = [c]
| carry (c, p::ps) = ((p+c) mod b) :: carry ((p+c) div b, ps)
!
!
- Dec.rep 42;
val it = D [2,4] : Dec.integer
!
- Bin.rep 42;
val it = D [0,1,0,1,0,1] : Bin.integer
!
- D [1+1] = D [2];
Error: unbound variable or constructor: D
solution (2)
Leave the functor body as is,
fun rep 0 = [ ] !
| rep n = (n mod b) :: rep (n div b)!
...
problem solved
With either of these solutions,
the code fragment
is not well-typed,
so cannot be evaluated.
transparency
- Bin.rep 42;!
val it = [0,1,0,1,0,1] : Bin.integer!
!
- Dec.rep 42;!
val it = [2,4] : Dec.integer
opacity
- Bin.rep 42;!
val it = - : Bin.integer!
!
- Dec.rep 42;!
val it = - : Dec.integer
testing
With opaque ascription
- Dec.rep 42;!
val it = - : Dec.integer
type error
correctness
what happens if
B.base = 1?
unary
structure Unary : ARITH = Digits(struct val b = 1 end);
open Unary;
display(add(rep 3, rep 2));
eval1(rep n) n
Figure out where the
correctness proof breaks!