TCache-0.13.3: Data/Persistent/Collection.hs
{-# LANGUAGE DeriveDataTypeable, TypeSynonymInstances,
MultiParamTypeClasses, ExistentialQuantification,
OverloadedStrings, FlexibleInstances, UndecidableInstances #-}
{- |
A persistent, transactional collection with Queue interface as well as
indexed access by key.
Uses default persistence. See "Data.TCache.DefaultPersistence"
-}
{-
NOTES
TODO:
data.persistent collection
convertirlo en un tree
añadiendo elementos node Node (refQueue a)
implementar un query language
by key
by attribute (addAttibute)
by class
xpath
implementar un btree sobre el
-}
module Data.Persistent.Collection (
RefQueue, getQRef,
pop,popSTM,pick, flush, flushSTM,
pickAll, pickAllSTM, push,pushSTM,
pickElem, pickElemSTM, readAll, readAllSTM,
deleteElem, deleteElemSTM,updateElem,updateElemSTM,
unreadSTM,isEmpty,isEmptySTM
) where
import Data.Typeable
import Control.Concurrent.STM(STM,atomically, retry)
import Control.Monad
import Data.TCache.DefaultPersistence
import Data.TCache
import Data.RefSerialize
--import Debug.Trace
--(!>) :: a -> String -> a
--a !> b= trace b a
instance Indexable (Queue a) where
key (Queue k _ _)= queuePrefix ++ k
data Queue a= Queue String [a] [a] deriving (Typeable)
instance Serialize a => Serialize (Queue a) where
showp (Queue n i o)= showp n >> showp i >> showp o
readp = return Queue `ap` readp `ap` readp `ap` readp
-- do
-- n <- readp
-- i <- readp
-- o <- readp
-- return $ Queue n i o
queuePrefix :: String
queuePrefix= "Queue#"
lenQPrefix :: Int
lenQPrefix= Prelude.length queuePrefix
instance Serialize a => Serializable (Queue a ) where
serialize = runW . showp
deserialize = runR readp
-- | A queue reference
type RefQueue a= DBRef (Queue a)
-- | push an element at the top of the queue
unreadSTM :: (Typeable a, Serialize a) => RefQueue a -> a -> STM ()
unreadSTM queue x= do
r <- readQRef queue
writeDBRef queue $ doit r
where
doit (Queue n imp out) = Queue n imp ( x : out)
-- | Check if the queue is empty
isEmpty :: (Typeable a, Serialize a) => RefQueue a -> IO Bool
isEmpty = atomically . isEmptySTM
isEmptySTM :: (Typeable a, Serialize a) => RefQueue a -> STM Bool
isEmptySTM queue= do
r <- readDBRef queue
return $ case r of
Nothing -> True
Just (Queue _ [] []) -> True
_ -> False
-- | Get the reference to new or existing queue trough its name
getQRef :: (Typeable a, Serialize a) => String -> RefQueue a
getQRef n = getDBRef . key $ Queue n undefined undefined
-- | Empty the queue (factually, it is deleted)
flush :: (Typeable a, Serialize a) => RefQueue a -> IO ()
flush = atomically . flushSTM
-- | Version in the STM monad
flushSTM :: (Typeable a, Serialize a) => RefQueue a -> STM ()
flushSTM = delDBRef
-- | Read the first element in the queue and delete it (pop)
pop
:: (Typeable a, Serialize a) => RefQueue a -- ^ Queue name
-> IO a -- ^ the returned elems
pop tv = atomically $ popSTM tv
readQRef :: (Typeable a, Serialize a) => RefQueue a -> STM(Queue a)
readQRef tv= do
mdx <- readDBRef tv
case mdx of
Nothing -> do
let q= Queue ( Prelude.drop lenQPrefix $ keyObjDBRef tv) [] []
writeDBRef tv q
return q
Just dx ->
return dx
-- | Version in the STM monad
popSTM :: (Typeable a, Serialize a) => RefQueue a
-> STM a
popSTM tv=do
dx <- readQRef tv
doit dx
where
doit (Queue n [x] [])= do
writeDBRef tv (Queue n [] [])
return x
doit (Queue _ [] []) = retry
doit (Queue n imp []) = doit (Queue n [] $ Prelude.reverse imp)
doit (Queue n imp list ) = do
writeDBRef tv (Queue n imp (Prelude.tail list ))
return $ Prelude.head list
-- | Read the first element in the queue but it does not delete it
pick
:: (Typeable a, Serialize a) => RefQueue a -- ^ Queue name
-> IO a -- ^ the returned elems
pick tv = atomically $ do
dx <- readQRef tv
doit dx
where
doit (Queue _ [x] [])= return x
doit (Queue _ [] []) = retry
doit (Queue n imp []) = doit (Queue n [] $ Prelude.reverse imp)
doit (Queue _ _ list ) = return $ Prelude.head list
-- | Push an element in the queue
push :: (Typeable a, Serialize a) => RefQueue a -> a -> IO ()
push tv v = atomically $ pushSTM tv v
-- | Version in the STM monad
pushSTM :: (Typeable a, Serialize a) => RefQueue a -> a -> STM ()
pushSTM tv v=
readQRef tv >>= \ (Queue n imp out) -> writeDBRef tv $ Queue n (v : imp) out
-- | Return the list of all elements in the queue. The queue remains unchanged
pickAll :: (Typeable a, Serialize a) => RefQueue a -> IO [a]
pickAll= atomically . pickAllSTM
-- | Version in the STM monad
pickAllSTM :: (Typeable a, Serialize a) => RefQueue a -> STM [a]
pickAllSTM tv= do
(Queue _ imp out) <- readQRef tv
return $ out ++ Prelude.reverse imp
-- | Return the first element in the queue that has the given key
pickElem ::(Indexable a,Typeable a, Serialize a) => RefQueue a -> String -> IO(Maybe a)
pickElem tv k= atomically $ pickElemSTM tv k
-- | Version in the STM monad
pickElemSTM :: (Indexable a,Typeable a, Serialize a)
=> RefQueue a -> String -> STM(Maybe a)
pickElemSTM tv key1 = do
Queue name imp out <- readQRef tv
let xs = out ++ Prelude.reverse imp
unless (Prelude.null imp) $ writeDBRef tv $ Queue name [] xs
case Prelude.filter (\x -> key x == key1) xs of
[] -> return Nothing
(x:_) -> return $ Just x
-- | Update the first element of the queue with a new element with the same key
updateElem :: (Indexable a,Typeable a, Serialize a)
=> RefQueue a -> a -> IO()
updateElem tv x = atomically $ updateElemSTM tv x
-- | Version in the STM monad
updateElemSTM :: (Indexable a,Typeable a, Serialize a)
=> RefQueue a -> a -> STM()
updateElemSTM tv v= do
Queue name imp out <- readQRef tv
let xs= out ++ Prelude.reverse imp
let xs'= Prelude.map (\x -> if key x == n then v else x) xs
writeDBRef tv $ Queue name [] xs'
where
n= key v
-- | Return the list of all elements in the queue and empty it
readAll :: (Typeable a, Serialize a) => RefQueue a -> IO [a]
readAll= atomically . readAllSTM
-- | A version in the STM monad
readAllSTM :: (Typeable a, Serialize a) => RefQueue a -> STM [a]
readAllSTM tv= do
Queue name imp out <- readQRef tv
writeDBRef tv $ Queue name [] []
return $ out ++ Prelude.reverse imp
-- | Delete all the elements of the queue that has the key of the parameter passed
deleteElem :: (Indexable a,Typeable a, Serialize a) => RefQueue a-> a -> IO ()
deleteElem tv x= atomically $ deleteElemSTM tv x
-- | Version in the STM monad
deleteElemSTM :: (Typeable a, Serialize a,Indexable a) => RefQueue a-> a -> STM ()
deleteElemSTM tv x= do
Queue name imp out <- readQRef tv
let xs= out ++ Prelude.reverse imp
writeDBRef tv $ Queue name [] $ Prelude.filter (\x2-> key x2 /= k) xs
where
k=key x