propose:
protect code from fool, build a wall.
abstraction
structure: interface(signature) vs. implementation
Signature QUEUE =
sig
type 'a q (*abstract*)
val empty: 'a q
val enq: 'a q * 'a -> 'a q
val null: 'a q -> bool
exception Empty
val deq: 'a q -> 'a * 'a q (*raises Empty if the queue is empty*)
end
Implementation = structure(code) + abstraction function + representation invariants
implement queues as list Abstruction function: list gives elements of the queue in arrival order
You can implement type
using datatype
, but not vise versa
structure Queue1(Implementation name) :> QUEUE(signature name) =
struct
type 'a q = 'a list
val empty = []
fun enq(q, x) = q @ [x]
val null = List.null
exception Empty
fun deq [] = raise Empty
| deq (x::q) = (x, q)
end
structure Q = Queue1
val myq = Q.enq(Q.enq(Q.empty, 1), 2)
myq: int Q.q = int Queue1.q
val (1, b) = Q.deq my q
val (1, _) = Q.deq my q
val (2, _) = Q.deq b
- b;
val it = -:int Q.q
Opaque ascription hides implementation detail, leaving only the interface known to client.
structure Queue1'(Implementation name) : QUEUE(signature name) =
struct
type 'a q = 'a list
val empty = []
fun enq(q, x) = q @ [x]
val null = List.null
exception Empty
fun deq [] = raise Empty
| deq (x::q) = (x, q)
end
structure Q' = Queue1'
val myq = Q'.enq(Q'.enq(Q'.empty, 1), 2)
myq: int Q'.q = int Queue1'.q = int list
val (1, b) = Q'.deq my q
val (1, _) = Q'.deq my q
val (2, _) = Q'.deq b
- b;
val it = [2]:int Q'.q
abstraction function: front @ rev back
structure Queue2 :> QUEUE =
struct
type 'a q = 'a list * 'a list
val empty = ([], [])
fun enq ((f, b), x) = (f, x::b)
fun null([], []) = true
| null _ = false_
exception Empty
fun deq ([], []) = raise Empty (*O(1) amortized*)
| deq (x::f, b) = (x, (f, b))
| deq ([], b) = deq (rev b, [])
end
Signature DICT =
sig
type key = string (*concrete type*)
type 'a entry = key * 'a (*concrete*)
type 'a dict (*abstract*)
val lookup: 'a dict -> key -> 'a option
val insert: 'a dict * 'a entry -> 'a dict (*replace the value if key present*)
end
implement dict using trees abstraction function: the (key, value) pairs represents the value in dict representation invariant: tree is sorted
requires sorted tree
ensure sorted tree
structure BST :> DICT =
struct
type key = string
type 'a entry = key * 'a
datatype 'a tree = Empty | Node of ('a tree * 'a tree)
type 'a dict = 'a entry tree
val empty = Empty
fun insert (Empty, e) = Node (Empty, e, Empty)
| insert (Node (lt, (e' as (k', _)), rt), (e as (k, __))) =
String.equal (k, k') of
EQUAL => Node(lt, e, rt)
| LESS => Node(insert(lt, e), e', rt)
| _ => Node(lt, e', insert(rt, e))_
fun lookup (Empty) _ = NONE
| lookup (Node(left, (k, v), right)) key =
case String.compare (key, k) of
EQUAL => SOMe v
| LESS => lookup left key
| _ => lookup right key
end
(if you make transparent, there still can be invisible like ?:tree, but occasionly REPEL knows how to print out data)
Table of Content