content: 
  -- | a simple type that only lets an IO action happen once, caching its result.
  
  module Util.Once(
      Once,
      newOnce,
      runOnce,
      altOnce,
  
      OnceMap,
      newOnceMap,
      runOnceMap,
      altOnceMap,
      onceMapToList,
      onceMapKeys,
      onceMapElems
  
      ) where
  
  import qualified Data.Map as Map
  import Data.IORef
  import Data.Dynamic
  
  newtype Once a = Once (IORef (Maybe a))
      deriving(Typeable)
  
  
  -- | create a new Once object
  newOnce :: IO (Once a)
  newOnce = do
      ref <- newIORef Nothing
      return (Once ref)
  
  -- | execute the action at most once, always returning the same result
  runOnce :: Once a -> IO a -> IO a
  runOnce (Once ref) action = do
      b <- readIORef ref
      case b of
          Just x -> return x
          Nothing -> do
              r <- action
              writeIORef ref (Just r)
              return r
  
  -- | run first argument once, after which perform the second
  
  altOnce :: Once () -> IO b -> IO b -> IO b
  altOnce (Once ref) first second = do
      b <- readIORef ref
      case b of
          Just _ -> second
          Nothing -> do
              writeIORef ref (Just ())
              first
  
  
  -- | run an IO action at most once for each distinct argument
  
  newtype OnceMap a b = OnceMap (IORef (Map.Map a b))
      deriving(Typeable)
  
  
  newOnceMap :: Ord a => IO (OnceMap a b)
  newOnceMap = do
      r <- newIORef Map.empty
      return $ OnceMap r
  
  runOnceMap :: Ord a => OnceMap a b -> a -> IO b -> IO b
  runOnceMap (OnceMap r) x act = do
      m <- readIORef r
      case Map.lookup x m of
          Just y -> return y
          Nothing -> do
              y <- act
              modifyIORef r (Map.insert x y)
              return y
  
  altOnceMap :: Ord a => OnceMap a () -> a -> IO b -> IO b -> IO b
  altOnceMap (OnceMap ref) x first after = do
      m <- readIORef ref
      case Map.member x m of
          True -> after
          False -> do
              modifyIORef ref (Map.insert x ())
              first
  
  onceMapToList :: OnceMap a b -> IO [(a,b)]
  onceMapToList (OnceMap ref) = do
      m <- readIORef ref
      return $ Map.toList m
  
  onceMapKeys :: OnceMap a b -> IO [a]
  onceMapKeys (OnceMap ref) = do
      m <- readIORef ref
      return $ Map.keys m
  
  onceMapElems :: OnceMap a b -> IO [b]
  onceMapElems (OnceMap ref) = do
      m <- readIORef ref
      return $ Map.elems m
  
  