Dictionaries

A dictionary maps keys to values. This data structure typically supports at least a lookup operation that allows you to find the value with a corresponding key, an insert operation that lets you create a new dictionary with an extra key included. And there needs to be a way of creating an empty dictionary.

We might represent this in a Dictionary module type as follows:

module type Dictionary = sig
  type ('k, 'v) t

  (* The empty dictionary *)
  val empty  : ('k, 'v) t

  (* [insert k v d] produces a new dictionary [d'] with the same mappings 
   * as [d] and also a mapping from [k] to [v], even if [k] was already 
   * mapped in [d]. *)
  val insert : 'k -> 'v -> ('k,'v) t -> ('k,'v) t

  (* [lookup k d] returns the value associated with [k] in [d].  
   * raises:  [Not_found] if [k] is not mapped to any value in [d]. *)
  val lookup  : 'k -> ('k,'v) t -> 'v
end

Note how the type Dictionary.t is parameterized on two types, 'k and 'v, which are written in parentheses and separated by commas. Although ('k,'v) might look like a pair of values, it is not: it is a syntax for writing multiple type variables.

We have seen already in this class that an association list can be used as a simple implementation of a dictionary. For example, here is an association list that maps some well-known names to an approximation of their numeric value:

[("pi", 3.14); ("e", 2.718); ("phi", 1.618)]

Let's try implementing the Dictionary module type with a module called AssocListDict.

module AssocListDict = struct
  type ('k, 'v) t = ('k * 'v) list

  let empty = []

  let insert k v d = (k,v)::d

  let lookup k d = List.assoc k d
end

If we put that code in a file named dict.ml, launch utop, and type:

# #use "dict.ml";;
# open AssocListDict;;
# let d = insert 1 "one" empty;;

The response we get is:

val d : (int * string) list = [(1, "one")]

But if we change the first line of the implementation of AssocListDict in dict.ml to the following:

module AssocListDict : Dictionary = struct

And if we restart utop and repeat the three phrases above (use,open,let), we get a different response:

val d : (int, string) t = <abstr>

That's because by indicating that the module has type Dictionary, the type AssocListDict.t has become abstract. Clients of the module are no longer permitted to know that it is implemented with a list. That provides encapsulation, so that if we later wanted to change the representation, we could safely do so.

results matching ""

    No results matching ""