CSCI 2041

ADVANCED PROGRAMMING PRINCIPLES

Modules and Interfaces

Modular Programming

Most large programs are broken up into smaller components, to make the program easier to:

  • Understand

  • Maintain

  • Upgrade

  • Reuse

OCaml provides extremely powerful mechanisms for supporting modularity and code reuse.

Example: sets

Recall our implementation of sets as lists…

type 'a set = 'a list
let mem x s = List.mem x s
let add x s = if (mem x s) then s else x::s
let subset s1 s2 = List.for_all (fun x -> mem x s2) s1
let eq s1 s2 = (subset s1 s2) && (subset s2 s1)
let union s1 s2 = List.fold_left (fun acc x ->
    if (mem x acc) then acc else (x::acc)) s1 s2
let intersect s1 s2 = List.fold_left
  (fun acc x -> if (mem x s2) then (x::acc) else acc) [] s1

Putting in "lSet.ml" creates the LSet namespace.

To prevent other programmers from interfering with our representation we can specify an interface:

% ocamlc –i lSet.ml > lSet.mli
type 'a set = 'a list
val emptyset : 'a list
val is_empty : 'a list -> bool
val mem : 'a -> 'a list -> bool
val add : 'a -> 'a list -> 'a list
val subset : 'a list -> 'a list -> bool
val eq : 'a list -> 'a list -> bool
val union : 'a list -> 'a list -> 'a list
val intersect : 'a list -> 'a list -> 'a list
val check_set : 'a list -> bool

This tells other modules and programs what’s in LSet.

Anything left out of the interface is inaccessible:

(* lSet.mli *)
type 'a set
val emptyset : 'a set
val is_empty : 'a set -> bool
val mem : 'a -> 'a set -> bool
val add : 'a -> 'a set -> 'a set
val subset : 'a set -> 'a set -> bool
val eq : 'a set -> 'a set -> bool
val union : 'a set -> 'a set -> 'a set
val intersect : 'a set -> 'a set -> 'a set
% ocamlc –c lSet.mli lSet.ml
# #load "lSet.cmo";;
# LSet.add "b" (LSet.add "a" LSet.emptyset)

The type 'a LSet.set is abstract – it can only be accessed through functions in lSet.ml

An interface specifies the minimal requirements for a module:

File "unSet.ml", line 1:
Error: The implementation unSet.ml does not match the interface unSet.cmi:
       The field `fold' is required but not provided
       The field `intersect' is required but not provided

And the maximal external access to the module:

# #load "lSet.cmo";;
# LSet.check_set LSet.emptyset;;

A program can open a namespace to access the public names, but types remain abstract:

# open LSet;;
# emptyset;;

In order to make sure elements are accessible, many collections provide a fold function.

We might run into several kinds of errors when using interfaces:

  • Type errors occur when types are mismatched between the implementation and interface:

    Try changing let add x s = x::s to let add s x = x::s in lSet.ml

  • Missing definitions as in unSet.ml
  • Mismatched type definitions: tree.ml/tree.mli
  • Dependency errors: if we change an interface, recompile .mli first, then .ml

cs2041.org

// reveal.js plugins