{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE LambdaCase #-} -- | Transactional list module Haskus.Utils.STM.TList ( TList , TNode , empty , singleton , null , length , first , last , prev , next , value , deleteAll , delete , filter , find , append , append_ , prepend , prepend_ , insertBefore , insertAfter , toList , toReverseList , fromList , index , take ) where import Prelude hiding (null,length,last,filter,take) import Haskus.Utils.STM import Haskus.Utils.Flow import Haskus.Utils.Maybe -- | A double linked-list newtype TList a = TList (TNode a) -- | A node in the list -- -- Every list has a marker node whose value is Nothing. Its nodePrev links to -- the last node and its nodeNext links to the first node. data TNode a = TNode { nodeValue :: Maybe a -- value (Nothing if list marker) , nodePrev :: TVar (TNode a) -- previous node , nodeNext :: TVar (TNode a) -- next node } -- | Get value associated with a node value :: TNode a -> a value node = case nodeValue node of Just v -> v Nothing -> error "TList: empty node value" -- | Empty node singleton empty :: STM (TList a) empty = do p <- newTVar undefined n <- newTVar undefined let node = TNode Nothing p n -- initially the marker node refers to itself writeTVar p node writeTVar n node return (TList node) -- | Remove all the elements of the list (O(1)) deleteAll :: TList a -> STM () deleteAll (TList m) = do -- make the marker node refer to itself writeTVar (nodeNext m) m writeTVar (nodePrev m) m -- | Create a singleton list singleton :: e -> STM (TList e) singleton e = do m <- empty void $ append e m return m -- | Indicate if the list is empty null :: TList e -> STM Bool null (TList m) = do h <- readTVar (nodeNext m) -- if the list is empty, the marker node refers to itself, hence the value of -- the nodeNext node is Nothing return (isNothing (nodeValue h)) -- | Count the number of elements in the list (0(n)) length :: TList e -> STM Word length (TList m) = go 0 m where go !n node = do node' <- readTVar (nodeNext node) case nodeValue node' of Nothing -> return n Just _ -> go (n+1) node' -- | Get the first element if any first :: TList e -> STM (Maybe (TNode e)) first (TList m) = next m -- | Get the last element if any last :: TList e -> STM (Maybe (TNode e)) last (TList m) = prev m -- | Get the previous element if any prev :: TNode a -> STM (Maybe (TNode a)) prev n = do h <- readTVar (nodePrev n) case nodeValue h of Nothing -> return Nothing Just _ -> return (Just h) -- | Get the next element if any next :: TNode a -> STM (Maybe (TNode a)) next n = do h <- readTVar (nodeNext n) case nodeValue h of Nothing -> return Nothing Just _ -> return (Just h) -- | Delete a element of the list delete :: TNode a -> STM () delete n = do -- if somehow we delete the marker node, we get a ring-list left <- readTVar $ nodePrev n right <- readTVar $ nodeNext n writeTVar (nodeNext left) right writeTVar (nodePrev right) left -- Link list node to itself so subsequent 'delete' calls will be harmless. writeTVar (nodePrev n) n writeTVar (nodeNext n) n -- | Insert a node between two adjacent nodes. insertBetween :: a -> TNode a -> TNode a -> STM (TNode a) insertBetween v left right = do n <- TNode (Just v) <$> newTVar left <*> newTVar right writeTVar (nodeNext left) n writeTVar (nodePrev right) n return n -- | Append an element to the list append :: a -> TList a -> STM (TNode a) append v (TList m) = insertAfter v m -- | Append an element to the list append_ :: a -> TList a -> STM () append_ a = void . append a -- | Prepend an element to the list prepend :: a -> TList a -> STM (TNode a) prepend v (TList m) = insertBefore v m -- | Prepend an element to the list prepend_ :: a -> TList a -> STM () prepend_ a = void . prepend a -- | Insert an element before another insertBefore :: a -> TNode a -> STM (TNode a) insertBefore v n = do right <- readTVar $ nodeNext n insertBetween v n right -- | Insert an element after another insertAfter :: a -> TNode a -> STM (TNode a) insertAfter v n = do left <- readTVar $ nodePrev n insertBetween v left n -- | Convert into a list (O(n)) toList :: TList a -> STM [a] toList (TList m) = go [] m where go !xs node = do node' <- readTVar (nodePrev node) case nodeValue node' of Nothing -> return xs Just x -> go (x:xs) node' -- | Convert into a reversed list (O(n)) toReverseList :: TList a -> STM [a] toReverseList (TList m) = go [] m where go !xs node = do node' <- readTVar (nodeNext node) case nodeValue node' of Nothing -> return xs Just x -> go (x:xs) node' -- | Create from a list fromList :: [e] -> STM (TList e) fromList xs = do s <- empty forM_ xs (`append` s) return s -- | Only keep element matching the criterium filter :: (e -> STM Bool) -> TList e -> STM () filter f (TList m) = go m where go node = do node' <- readTVar (nodeNext node) case nodeValue node' of Nothing -> return () Just v -> do p <- f v if not p then delete node' >> go node else go node' -- | Find the first node matching the predicate (if any) find :: (e -> STM Bool) -> TList e -> STM (Maybe (TNode e)) find f (TList m) = go m where go node = do node' <- readTVar (nodeNext node) case nodeValue node' of Nothing -> return Nothing Just v -> do p <- f v if p then return (Just node') else go node' -- | Get the node from its index index :: Word -> TList e -> STM (Maybe (TNode e)) index n (TList m) = go n m where go !i node = do node' <- readTVar (nodeNext node) case nodeValue node' of Nothing -> return Nothing Just _ | i == 0 -> return (Just node') | otherwise -> go (i-1) node' -- | Take (and remove) up to n elements in the list (O(n)) take :: Word -> TList e -> STM [e] take n l = index n l >>= \case -- return the whole list Nothing -> do r <- toList l deleteAll l return r -- build the list and remove elements at the same time Just node -> go [] node where go !xs node' = do case nodeValue node' of Nothing -> return xs Just x -> do p <- readTVar (nodePrev node') delete node' go (x:xs) p