Copyright (C) 2017, H. Conrad Cunningham

Advisory: The HTML version of this document may require use of a browser that supports the display of MathML. A good choice as of October 2017 is a recent version of Firefox from Mozilla.

TODO:

Labeled Digraph ADT in Haskell

Introduction

In this case study, we seek to develop a family of graph data structures that can support the implementation of Adventure games similar to the Wizard's Adventure game given in Chapter 5 of the book Land of Lisp: Learn to Program in Lisp, One Game at a Time [Barski 2011]. We develop this family as an abstract data type and implement it as modules in Haskell.

This abstract data type represents doubly labeled directed graphs. A directed graph (digraph) includes vertices (nodes) and edges directed from one node to another. The graph allows arbitrary data (i.e., labels) to be attached to both vertices and edges in the directed graph.

Note: In this case study we use the acronym ADT to refer to an abstract data type. We also use an algebraic data type to denote the graph type provided by a Haskell module and perhaps use other algebraic data types in implementing the module.

The objectives of this case study are to:

  1. specify a Labeled Digraph abstract data type using a constructive model (sets and interface invariant for the, preconditions/postconditions for each operation)

  2. implement the abstract data type as a module in Haskell with at least two distinct implementations, giving appropriate implementation invariants for each

One source of information on digraphs and their specification we referenced is Chapter 10 of the book Abstract Data Types: Specifications, Implementations, and Applications [Dale-Walker 1996].

Specification

Notation

We use the following notation and terminology to describe the abstract data type's model and its semantics. (We choose notation that can readily be used in comments in the Haskell program.)

Abstract model

We parameterize the abstract model for the Labeled Digraph ADT with the following types.

We model the state of the instance of the Labeled Digraph ADT with an abstract value G such that G = (V,E,VL,EL) with G's components satisfying the following Labeled Digraph Properties.

Interface invariant

We define the following interface invariant for the Labeled Digraph ADT:

Any valid labeled digraph instance G, appearing in either the arguments or return value of a public ADT operation, must satisfy the Labeled Digraph Properties.

Constructive semantics

We specify the various ADT operations below using their type signatures, preconditions, and postconditions. Along with the interface invariant, these comprise the (implementation-independent) specification of the ADT.

In these assertions, for a digraph g that satisfies the invariants, G(g) denotes its abstract model(V,E,VL,EL) as described above. The value Result denotes the return value of function.

Haskell module abstract interface

Below we state the header for a Haskell module Digraph_XXX that implements the Labeled Digraph ADT. The module name suffix XXX denotes the particular implementation for a data representation, but the signatures and semantics of the operations are the same regardless of representation.

The module exports data type Digraph, but its constructors are not exported. This allows modules that import Digraph_XXX to use the data type without revealing how the data type is implemented. (If we had Digraph(..) in the export list, then the data type and all its constructors would be exported.)

    module DigraphADT_XXX 
      ( Digraph             -- constrain ops (Eq a, Show a, Show b, Show c) 
      , new_graph           -- Digraph a b c 
      , is_empty            -- Digraph a b c -> Bool 
      , add_vertex          -- Digraph a b c -> a -> b -> Digraph a b c 
      , remove_vertex       -- Digraph a b c -> a -> Digraph a b c 
      , update_vertex       -- Digraph a b c -> a -> b -> Digraph a b c 
      , get_vertex          -- Digraph a b c -> a -> b 
      , has_vertex          -- Digraph a b c -> a -> Bool 
      , add_edge            -- Digraph a b c -> a -> a -> c -> Digraph a b c 
      , remove_edge         -- Digraph a b c -> a -> a -> Digraph a b c 
      , update_edge         -- Digraph a b c -> a -> a -> c -> Digraph a b c 
      , get_edge            -- Digraph a b c -> a -> a -> c 
      , has_edge            -- Digraph a b c -> a -> a -> Bool 
      , all_vertices        -- Digraph a b c -> [a]
      , from_edges          -- Digraph a b c -> a -> [a]
      , all_vertices_labels -- Digraph a b c -> [(a,b)]
      , from_edges_labels   -- Digraph a b c -> a -> [(a,c)]
      )
    where  -- definitions for the types and functions

Note: The Glasgow Haskell Compiler (GHC) release 8.2 (July 2017) and the Cabal-Install package manager release 2.0 (August 2017) support a new mixin package system called Backpack. This extension would enable us to define an abstract module "DigraphADT" as a signature file with the above interface. Other modules can then implement this abstract interface thus giving a more explicit and flexible definition of this abstract data type.

List Implementation

Type parameters

The Haskell List representation uses the following values for the type parameters:

That is, vertices can be compared for equality. Vertices and both the vertex and edge labels can be displayed as strings.

Note: It may be desirable to require VertexType to be from class Ord (totally ordered) and VertexLabelType and EdgeLabelType to be from class Eq. These were not necessary for the List implementation, but were necessary for the Map implementation.

Labeled digraph representation

The List implementation represents a labeled digraph as an instance of the Haskell algebraic data type Digraph, in particular data constructor (Graph vs es).

In an instance (Graph vs es):

In terms of the abstract model, vs encodes VL directly and, because VL is a total function on V, it encodes V indirectly. Similarly, es encodes EL directly and E indirectly.

Implementation invariant

Given the above description, we then define the following implementation (representation) invariant for the list-based version of the Labeled Digraph ADT:

Any Haskell Digraph value (Graph vs es) with abstract model G = (V,E,VL,EL), appearing in either the arguments or return value of an operation, must also satisfy the following:

    (ForAll v, l :: (v,l) IN vs  <=> (v,l) IN VL ) &&
    (ForAll v1, v2, m :: (v1,v2,m) IN es  <=> ((v1,v2),m) IN EL )

Haskell module

The Haskell module for the list representation of the labeled digraph graph ADT is in file DigraphADT_List.hs. Its test driver module is in file DigraphADT_TestList.hs.

Map Implementation

Type parameters

The Haskell Map representation uses the following values for the type parameters:

Note: In the List version of this ADT, VertexType is required to be in classes Show and Eq (instead of Ord). The two label types did not require Eq. However, the use of the Map module for implementation in this version requires the new type constraints.

Labeled digraph representation

This implementation represents a labeled digraph as an instance of the Haskell algebraic data type Digraph, in particular data constructor (Graph m), where m is from the Data.Map.Strict collection. (This collection is implemented as a balanced tree.)

An instance of (Graph m) corresponds to the abstract model as follows:

Implementation invariant

Given the above description, we then define the following implementation (representation) invariant for the list-based version of the Labeled Digraph ADT:

Any Haskell Digraph value (Graph m) with abstract model G = (V,E,VL,EL), appearing in either the arguments or return value of an operation, must also satisfy the following:

    (ForAll v1, l, es :: 
        ( m(v1) defined && m(v1) == (l,es) ) <=> 
        ( VL(v1) == l && 
            (ForAll v2, el :: (v2,el) IN es <=> 
                              EL((v1,v2)) == el) ) )

Haskell module

The Haskell module for the Map representation of the labeled digraph graph ADT is in file DigraphADT_Map.hs. Its test driver module is in file DigraphADT_TestMap.hs.

Acknowledgements

In Spring 2017, I adapted and revised this document from comments in my Spring 2015 Haskell implementations of the Labeled Digraph abstract data type. (In addition to the list- and map-based Haskell implementations, I also developed a list-based implementation in Elixir in Spring 2015 and two Scala-based implementations in Spring 2016.)

In Summer and Fall 2017, I continue to revise this document. I plan eventually to integrate it into evolving textbook "Introduction to Functional Programming Using Haskell."

I maintain these notes as text in Pandoc's dialect of Markdown using embedded LaTeX markup for the mathematical formulas and then translate the notes to HTML, PDF, and other forms as needed. The HTML version of this document requires use of a browser that supports the display of MathML.

References

[Barski 2011]
Conrad Barski. "Building a Text Game Engine," Land of Lisp: Learn to Program in Lisp, One Game at a Time, pp. 69-84, No Starch Press, 2011. (The Common Lisp example in this chapter is similar to the classic Adventure game; the underlying data structure is a labeled digraph.)
[Bird-Wadler 1998]
Richard Bird and Philip Wadler. Introduction to Functional Programming, Second Edition, Addison Wesley, 1998. [First Edition, 1988]
[Cunningham 2017]
H. Conrad Cunningham. Notes on Data Abstraction, 1996-2017.
[Dale-Walker 1996]
Nell Dale and Henry M. Walker. "Directed Graphs or Digraphs," Chapter 10, In Abstract Data Types: Specifications, Implementations, and Applications, pp. 439-469, D. C. Heath & Co, 1996.

Terms and Concepts

Use of Haskell module hiding features to implement the abstract data type's interface, applying specification concepts, using mathematical concepts to model the data abstraction (graphs, sets, sequences, bags, functions, relations), graph data structure.

The specification model for the abstract data type uses a constructive semantics -- an abstract model, invariants, preconditions, and postconditions.