{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE LambdaCase #-}
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
newtype TList a = TList (TNode a)
data TNode a = TNode
{ nodeValue :: Maybe a
, nodePrev :: TVar (TNode a)
, nodeNext :: TVar (TNode a)
}
value :: TNode a -> a
value node = case nodeValue node of
Just v -> v
Nothing -> error "TList: empty node value"
empty :: STM (TList a)
empty = do
p <- newTVar undefined
n <- newTVar undefined
let node = TNode Nothing p n
writeTVar p node
writeTVar n node
return (TList node)
deleteAll :: TList a -> STM ()
deleteAll (TList m) = do
writeTVar (nodeNext m) m
writeTVar (nodePrev m) m
singleton :: e -> STM (TList e)
singleton e = do
m <- empty
void $ append e m
return m
null :: TList e -> STM Bool
null (TList m) = do
h <- readTVar (nodeNext m)
return (isNothing (nodeValue h))
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'
first :: TList e -> STM (Maybe (TNode e))
first (TList m) = next m
last :: TList e -> STM (Maybe (TNode e))
last (TList m) = prev m
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)
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 :: TNode a -> STM ()
delete n = do
left <- readTVar $ nodePrev n
right <- readTVar $ nodeNext n
writeTVar (nodeNext left) right
writeTVar (nodePrev right) left
writeTVar (nodePrev n) n
writeTVar (nodeNext n) n
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 :: a -> TList a -> STM (TNode a)
append v (TList m) = insertAfter v m
append_ :: a -> TList a -> STM ()
append_ a = void . append a
prepend :: a -> TList a -> STM (TNode a)
prepend v (TList m) = insertBefore v m
prepend_ :: a -> TList a -> STM ()
prepend_ a = void . prepend a
insertBefore :: a -> TNode a -> STM (TNode a)
insertBefore v n = do
right <- readTVar $ nodeNext n
insertBetween v n right
insertAfter :: a -> TNode a -> STM (TNode a)
insertAfter v n = do
left <- readTVar $ nodePrev n
insertBetween v left 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'
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'
fromList :: [e] -> STM (TList e)
fromList xs = do
s <- empty
forM_ xs (`append` s)
return s
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 :: (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'
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 :: Word -> TList e -> STM [e]
take n l = index n l >>= \case
Nothing -> do
r <- toList l
deleteAll l
return r
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