A data type representing directed graphs, backed by Data.Graph.
It is strict in the node type.

This is an alternative interface to Data.Graph. In this interface,
nodes (identified by the IsNode type class) are associated with a
key and record the keys of their neighbors. This interface is more
convenient than Graph, which requires vertices to be
explicitly handled by integer indexes.

The current implementation has somewhat peculiar performance
characteristics. The asymptotics of all map-like operations mirror
their counterparts in Data.Map. However, to perform a graph
operation, we first must build the Data.Graph representation, an
operation that takes O(V + E log V). However, this operation can
be amortized across all queries on that particular graph.

Some nodes may be broken, i.e., refer to neighbors which are not
stored in the graph. In our graph algorithms, we transparently
ignore such edges; however, you can easily query for the broken
vertices of a graph using broken (and should, e.g., to ensure that
a closure of a graph is well-formed.) It's possible to take a closed
subset of a broken graph and get a well-formed graph.

The IsNode class is used for datatypes which represent directed
graph nodes. A node of type a is associated with some unique key of
type Key a; given a node we can determine its key (nodeKey)
and the keys of its neighbors (nodeNeighbors).