Representing cyclic data structures efficiently in Haskell

Andrew J Bromage ajb@spamcop.net
Tue, 8 Jul 2003 14:04:16 +1000


G'day all.

On Mon, Jul 07, 2003 at 04:04:17PM +0100, Sarah Thompson wrote:

> > I would also need to implement efficient indexes into the data structure 
> > -- flat searching will be too slow for non-trivial amounts of data. In 
> > C++, I'd implement one or more external (probably STL-based) indexes 
> > that point into the main data structure.

I replied:

> The direct Haskell equivalent is to use Refs; either IORefs or
> STRefs.  (I'd personally use IORefs, but that's because I like
> having IO around.)  Dereferencing an IORef is a constant-time
> operation, just like chasing a pointer in C.

I forgot to give an example. :-)

Suppose you want some kind of electronic circuit, as you suggested.
Abstractly, you want something like this:

	data Node
	  = Node NodeState [Component]

	data Component
	  = Resistor ResistorCharacteristics Node Node
	  | Transistor TransistorCharacteristics Node Node Node
	  | {- etc -}

You can make this indirect in introducing refs:

	type NodeRef = IORef Node
	type ComponentRef = IORef Component

	data Node
	  = Node NodeState [ComponentRef]

	data Component
	  = Resistor ResistorCharacteristics NodeRef NodeRef
	  | Transistor TransistorCharacteristics NodeRef NodeRef NodeRef

The data structures are mutable:

	getNodeState :: NodeRef -> IO NodeState
	getNodeState node
	  = do	Node state _ <- readIORef node
		return state

	setNodeState :: NodeRef -> NodeState -> IO ()
	setNodeState node state
	  = do	modifyIORef (\Node _ cs -> Node state cs) node

and it's straightforward to construct an index into the middle
somewhere:

	type NamedNodeIndex = FiniteMap String NodeRef

Cheers,
Andrew Bromage