Given the existence of a priority queue data structure, the heap sort algorithm is trivially implemented by loading the unsorted sequence into a queue then successively pulling of the minimum element from the queue until the queue is exhausted.
There are many ways to implement a priority queue and so we seek an expression for a function for heap sorting that is polymorphic over those choices.
To begin, a module type for priority queues.
(**Priority queues over ordered types*) module type PRIORITY_QUEUE = sig (**Output signature of the functor [Make]*) module type S = sig exception Empty type element (*Abstract type of elements of the queue*) type t (*Abstract type of a queue*) val empty : t (*The empty queue*) val is_empty : t -> bool (*Check if queue is empty*) val insert : t -> element -> t (*Insert item into queue*) val delete_min : t -> t (*Delete the minimum element*) val find_min : t -> element (*Return the minimum element*) val of_list : element list -> t end (**Input signature of the functor [Make]*) module type Ordered_type = sig type t val compare : t -> t -> int end (**Functor building an implementation of the priority queue structure given a totally ordered type*) module Make : functor (Ord : Ordered_type) -> S with type element = Ord.t end
An implementation of this signature using "leftist heaps" is described for the interested in this Caltech lab but such details are omitted here.
module Priority_queue : PRIORITY_QUEUE = struct module type S = sig .. end module type Ordered_type = sig .. end module Make (Elt : Ordered_type) : (S with type element = Elt.t) = struct .. end end
What I really want to show you is this. We start with the following module type abbreviation.
type 'a queue_impl = (module Priority_queue.S with type element = 'a)Then, the
heap_sort
function can be written such that it takes a module as a first class value and uses a locally abstract type to connect it with the element type of the list to be sorted.
let heap_sort (type a) (queue : a queue_impl) (l : a list) : a list = let module Queue = (val queue : Priority_queue.S with type element = a) in let rec loop acc h = if Queue.is_empty h then acc else let p = Queue.find_min h in loop (p :: acc) (Queue.delete_min h) in List.rev (loop [] (Queue.of_list l))There we have it. The objective has been achieved : we have written a heap sorting function that is polymorphic in the implementation of the priority queue with which it is implemented.
Usage (testing) proceeds as in this example.
(*Prepare an [Priority_queue.Ordered_type] module to pass as argument to [Priority_queue.Make]*) module Int : Priority_queue.Ordered_type with type t = int = struct type t = int let compare = Pervasives.compare end (*Make a priority queue module*) module Int_prioqueue : (Priority_queue.S with type element = int) = Priority_queue.Make (Int) (*Make a first class value of the module by packing it*) let queue = (module Int_prioqueue : Priority_queue.S with type element = int) (*Now, pass the module to [heap_sort]*) let sorted = heap_sort queue [-1; -2; 2] (*Produces the list [-2; -1; 2]*)
Addendum :
These ideas can be pushed a little further yielding a simpler syntax for the parametric heapsort algorithm.(*Type abbreviations*) type 'a order_impl = (module Priority_queue.Ordered_type with type t = 'a) type 'a queue_impl = (module Priority_queue.S with type element = 'a) (*Module factory functions*) let mk_ord : 'a. unit -> 'a order_impl = fun (type s) () -> (module struct type t = s let compare = Pervasives.compare end : Priority_queue.Ordered_type with type t = s ) let mk_queue : 'a. unit -> 'a queue_impl = fun (type s) ord -> let module Ord = (val mk_ord () : Priority_queue.Ordered_type with type t = s) in (module Priority_queue.Make (Ord) : Priority_queue.S with type element = s)For example, now we can write
# heap_sort (mk_queue ()) [-3; 1; 5] ;; - : int list = [-3; 1; 5]