haggle-0.1.0.0: src/Data/Graph/Haggle/VertexMap.hs
{-# LANGUAGE PatternGuards, FlexibleContexts #-}
-- | This is a simple module to handle a common pattern: constructing graphs
-- where vertex labels map uniquely to vertices.
--
-- The primary functions in this module are 'vertexForLabel' and
-- 'vertexForLabelRef', which take a vertex label and return the 'Vertex' for
-- that label (allocating a new 'Vertex') if necessary. The first of those
-- functions explicitly threads the mapping as inputs and outputs. The second
-- manages a mutable ref side-by-side with the underlying graph.
--
-- After the graph is fully constructed, this mapping is often still useful.
module Data.Graph.Haggle.VertexMap (
-- * Pure interface
VertexMap,
emptyVertexMap,
vertexForLabel,
lookupVertexForLabel,
vertexMapFromGraph,
-- * Ref interface
VertexMapRef,
newVertexMapRef,
vertexForLabelRef,
vertexMapFromRef ) where
import qualified Control.DeepSeq as DS
import Control.Monad ( liftM )
import qualified Control.Monad.Primitive as P
import qualified Control.Monad.Ref as R
import Data.Map ( Map )
import qualified Data.Map as M
import Data.Tuple ( swap )
import Data.Graph.Haggle.Classes
-- | A simple mapping from labels to their 'Vertex'
newtype VertexMap nl = VM (Map nl Vertex)
instance (DS.NFData nl) => DS.NFData (VertexMap nl) where
rnf (VM m) = m `DS.deepseq` ()
emptyVertexMap :: VertexMap nl
emptyVertexMap = VM M.empty
-- | > (v, m') <- vertexForLabel g m lbl
--
-- Looks up the 'Vertex' for @lbl@ in @g@. If no 'Vertex' in @g@ has that
-- label, a new 'Vertex' is allocated and returned. The updated vertex
-- mapping @m'@ is returned, too.
vertexForLabel :: (MLabeledVertex g, Ord (MVertexLabel g), P.PrimMonad m, R.MonadRef m)
=> g m
-> VertexMap (MVertexLabel g)
-> MVertexLabel g
-> m (Vertex, VertexMap (MVertexLabel g))
vertexForLabel g vm@(VM m) lbl
| Just v <- M.lookup lbl m = return (v, vm)
| otherwise = do
v <- addLabeledVertex g lbl
let m' = M.insert lbl v m
return (v, VM m')
-- | A pure lookup to convert a 'Vertex' label into a 'Vertex'. If the
-- label is not in the graph, returns 'Nothing'.
lookupVertexForLabel :: (Ord nl) => nl -> VertexMap nl -> Maybe Vertex
lookupVertexForLabel lbl (VM m) = M.lookup lbl m
-- | Build a 'VertexMap' from a 'Graph' with 'Vertex' labels.
vertexMapFromGraph :: (HasVertexLabel g, Ord (VertexLabel g))
=> g -> VertexMap (VertexLabel g)
vertexMapFromGraph = VM . M.fromList . map swap . labeledVertices
-- | A 'VertexMap' wrapped up in a mutable ref for possibly
-- easier access in 'vertexMapFromRef'.
newtype VertexMapRef nl m = VMR (R.Ref m (VertexMap nl))
-- | Extract the pure 'VertexMap' from the mutable ref. This is useful
-- to retain the mapping after the graph is fully constructed.
vertexMapFromRef :: (P.PrimMonad m, R.MonadRef m) => VertexMapRef nl m -> m (VertexMap nl)
vertexMapFromRef (VMR ref) = R.readRef ref
-- | Allocate a new 'VertexMap' buried in a mutable ref.
newVertexMapRef :: (P.PrimMonad m, R.MonadRef m) => m (VertexMapRef nl m)
newVertexMapRef = liftM VMR $ R.newRef emptyVertexMap
-- | Just like 'vertexForLabel', but holding the mapping in a ref instead
-- of threading it. Usage is simpler:
--
-- > v <- vertexForLabelRef g m lbl
vertexForLabelRef :: (MLabeledVertex g, Ord (MVertexLabel g), P.PrimMonad m, R.MonadRef m)
=> g m
-> VertexMapRef (MVertexLabel g) m
-> MVertexLabel g
-> m Vertex
vertexForLabelRef g (VMR ref) lbl = do
vm <- R.readRef ref
(v, vm') <- vertexForLabel g vm lbl
R.writeRef ref vm'
return v