Lecture 015

Modules

propose:

structure: interface(signature) vs. implementation

Interface

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

Implementation = structure(code) + abstraction function + representation invariants

implement queues as list Abstruction function: list gives elements of the queue in arrival order

Opaque ascription (ascribing signature to structure)

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.

Transparent ascription (for debugging)

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

Improved Implementation

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

Dictionary

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

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