-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | A Transactional cache with user-defined persistence -- -- Please see the README on GitHub at -- https://github.com/agocorona/TCache#readme @package TCache @version 0.13.3 -- | IDynamic is a indexable and serializable version of Dynamic. (See -- Data.Dynamic). It is used as containers of objects in the -- cache so any new datatype can be incrementally stored without -- recompilation. IDimamic provices methods for safe casting, besides -- serializaton, deserialirezation and retrieval by key. module Data.Persistent.IDynamic newtype IDynamic IDyn :: IORef IDynType -> IDynamic data IDynType DRight :: !a -> IDynType DLeft :: !(ByteString, (Context, ByteString)) -> IDynType newtype Save Save :: ByteString -> Save tosave :: IDynamic -> IDynamic errorfied :: String -> String -> a dynPrefix :: String dynPrefixSp :: ByteString notreified :: ByteString toIDyn :: (Typeable a, Serialize a) => a -> IDynamic -- | check if a (possibly polimorphic) value within a IDynamic value has -- the given serialization" serializedEqual :: IDynamic -> ByteString -> Bool fromIDyn :: (Typeable a, Serialize a) => IDynamic -> a safeFromIDyn :: (Typeable a, Serialize a) => IDynamic -> Either String a reifyM :: (Typeable a, Serialize a) => IDynamic -> a -> IO a instance Data.RefSerialize.Serialize Data.Persistent.IDynamic.Save instance Data.RefSerialize.Serialize Data.Persistent.IDynamic.IDynamic instance GHC.Show.Show Data.Persistent.IDynamic.IDynamic -- | some internal definitions. To use default persistence, import -- Data.TCache.DefaultPersistence instead module Data.TCache.Defs type AccessTime = Integer type ModifTime = Integer data Status a NotRead :: Status a DoNotExist :: Status a Exist :: a -> Status a data Elem a Elem :: !a -> !AccessTime -> !ModifTime -> Elem a type TPVar a = TVar (Status (Elem a)) data DBRef a DBRef :: !String -> !TPVar a -> DBRef a castErr :: (Typeable a1, Typeable a2) => a1 -> a2 -- | Indexable is an utility class used to derive instances of IResource -- -- Example: -- --
--   data Person= Person{ pname :: String, cars :: [DBRef Car]} deriving (Show, Read, Typeable)
--   data Car= Car{owner :: DBRef Person , cname:: String} deriving (Show, Read, Eq, Typeable)
--   
-- -- Since Person and Car are instances of Read ans Show, by -- defining the Indexable instance will implicitly define the -- IResource instance for file persistence: -- --
--   instance Indexable Person where  key Person{pname=n} = "Person " ++ n
--   instance Indexable Car where key Car{cname= n} = "Car " ++ n
--   
class Indexable a key :: Indexable a => a -> String defPath :: Indexable a => a -> String -- | Serialize is an alternative to the IResource class for defining -- persistence in TCache. The deserialization must be as lazy as -- possible. serialization/deserialization are not performance critical -- in TCache -- -- Read, Show, instances are implicit instances of Serializable -- --
--   serialize  = pack . show
--   deserialize= read . unpack
--   
-- -- Since write and read to disk of to/from the cache are not be very -- frequent The performance of serialization is not critical. class Serializable a serialize :: Serializable a => a -> ByteString deserialize :: Serializable a => ByteString -> a deserialKey :: Serializable a => String -> ByteString -> a setPersist :: Serializable a => a -> Maybe Persist -- | Used by IndexQuery for index persistence(see -- Data.TCache.IndexQuery. class PersistIndex a persistIndex :: PersistIndex a => a -> Maybe Persist type Key = String -- | a persist mechanism has to implement these three primitives -- filePersist is the default file persistence data Persist Persist :: (Key -> IO (Maybe ByteString)) -> (Key -> ByteString -> IO ()) -> (Key -> IO ()) -> Persist -- | read by key. It must be strict [readByKey] :: Persist -> Key -> IO (Maybe ByteString) -- | write. It must be strict [write] :: Persist -> Key -> ByteString -> IO () -- | delete [delete] :: Persist -> Key -> IO () -- | Implements default default-persistence of objects in files with their -- keys as filenames filePersist :: Persist defaultPersistIORef :: IORef Persist -- | Set the default persistence mechanism of all serializable -- objects that have setPersist= const Nothing. By default it is -- filePersist -- -- this statement must be the first one before any other TCache call setDefaultPersist :: Persist -> IO () getDefaultPersist :: Persist getPersist :: (Serializable a, Typeable a) => a -> Persist defaultReadByKey :: String -> IO (Maybe ByteString) defaultWrite :: String -> ByteString -> IO () safeWrite :: FilePath -> ByteString -> IO () defaultDelete :: String -> IO () defReadResourceByKey :: (Indexable a, Serializable a, Typeable a) => String -> IO (Maybe a) defWriteResource :: (Indexable a, Serializable a, Typeable a) => a -> IO () defDelResource :: (Indexable a, Serializable a, Typeable a) => a -> IO () -- | Strict read from file, needed for default file persistence readFileStrict :: FilePath -> IO ByteString instance Data.TCache.Defs.Indexable GHC.Base.String instance Data.TCache.Defs.Indexable GHC.Types.Int instance Data.TCache.Defs.Indexable GHC.Num.Integer.Integer instance Data.TCache.Defs.Indexable () instance GHC.Show.Show (Data.TCache.Defs.DBRef a) instance GHC.Classes.Eq (Data.TCache.Defs.DBRef a) instance GHC.Classes.Ord (Data.TCache.Defs.DBRef a) module Data.TCache.IResource -- | Must be defined for every object to be cached. class IResource a keyResource :: IResource a => a -> String -- | Implements the database access and marshalling of the object. while -- the database access must be strict, the marshaling must be lazy if, as -- is often the case, some parts of the object are not really accesed. If -- the object contains DBRefs, this avoids unnecesary cache lookups. This -- method is called within atomically blocks. Since STM -- transactions retry, readResourceByKey may be called twice in strange -- situations. So it must be idempotent, not only in the result but also -- in the effect in the database . However, because it is executed by -- safeIOToSTM it is guaranteed that the execution is not -- interrupted. readResourceByKey :: IResource a => String -> IO (Maybe a) -- | hopefully optimized read of many objects by key. readResourcesByKey :: IResource a => [String] -> IO [Maybe a] readResource :: IResource a => a -> IO (Maybe a) -- | To write into persistent storage. It must be strict. Since STM -- transactions may retry, writeResource must be idempotent, not -- only in the result but also in the effect in the database. . However, -- because it is executed by safeIOToSTM it is guaranteed that -- the execution is not interrupted. All the new obbects are writeen to -- the database on synchromization, so writeResource must not autocommit. -- Commit code must be located in the postcondition. (see -- setConditions) Since there is no provision for rollback from -- failure in writing to persistent storage, writeResource must -- retry until success. writeResource :: IResource a => a -> IO () -- | multiple write (hopefully) in a single request. That is up to you and -- your backend . Defined by default as 'mapM_ writeResource' writeResources :: IResource a => [a] -> IO () -- | Delete the resource. It is called syncronously. So it must commit delResource :: IResource a => a -> IO () delResources :: IResource a => [a] -> IO () -- | Resources data definition used by withSTMResources data Resources a b -- | forces a retry Retry :: Resources a b Resources :: [a] -> [a] -> b -> Resources a b -- | resources to be inserted back in the cache [toAdd] :: Resources a b -> [a] -- | resources to be deleted from the cache and from permanent storage [toDelete] :: Resources a b -> [a] -- | result to be returned [toReturn] :: Resources a b -> b -- | Empty resources: resources= Resources [] [] () resources :: Resources a () module Data.TCache.Triggers data DBRef a DBRef :: !String -> !TPVar a -> DBRef a data Elem a Elem :: !a -> !AccessTime -> !ModifTime -> Elem a data Status a NotRead :: Status a DoNotExist :: Status a Exist :: a -> Status a -- | Add an user defined trigger to the list of triggers Trriggers are -- called just before an object of the given type is created, modified or -- deleted. The DBRef to the object and the new value is passed to the -- trigger. The called trigger function has two parameters: the DBRef -- being accesed (which still contains the old value), and the new value. -- If the DBRef is being deleted, the second parameter is Nothing. -- if the DBRef contains Nothing, then the object is being created addTrigger :: (IResource a, Typeable a) => (DBRef a -> Maybe a -> STM ()) -> IO () -- | internally called when a DBRef is modifieddeletedcreated applyTriggers :: (IResource a, Typeable a) => [DBRef a] -> [Maybe a] -> STM () -- | TCache is a transactional cache with configurable persistence that -- permits STM transactions with objects that synchronize synchronously -- or asynchronously with their user defined storages. Persistence in -- files is provided by default. -- -- TCache implements DBRefs . They are persistent STM references -- with a typical Haskell interface. similar to TVars (newDBRef, -- readDBRef, writeDBRef etc) but with added persistence. -- DBRefs are serializable, so they can be stored and retrieved. Because -- they are references, they point to other serializable registers. This -- permits persistent mutable inter-object relations. -- -- For simple transactions of lists of objects of the same type TCache -- implements inversion of control primitives withSTMResources and -- variants, that call pure user-defined code for registers update. -- Examples below. -- -- Triggers in Data.TCache.Triggers are user-defined hooks that -- are called on register updates. They are used internally for indexing. -- -- Data.TCache.IndexQuery implements a straightforward pure -- Haskell, type-safe query language based on register field relations. -- This module must be imported separately. -- -- Data.TCache.IndexText add full text search and content search -- to the query language. -- -- Data.TCache.DefaultPersistence has instances for key -- indexation, serialization and default file persistence. The file -- persistence is more reliable, and the embedded IO reads inside STM -- transactions are safe. -- -- Data.Persistent.Collection implements a persistent, -- transactional collection with Queue interface as well as indexed -- access by key. module Data.TCache -- | Perform a series of STM actions atomically. -- -- Using atomically inside an unsafePerformIO or -- unsafeInterleaveIO subverts some of guarantees that STM -- provides. It makes it possible to run a transaction inside of another -- transaction, depending on when the thunk is evaluated. If a nested -- transaction is attempted, an exception is thrown by the runtime. It is -- possible to safely use atomically inside unsafePerformIO -- or unsafeInterleaveIO, but the typechecker does not rule out -- programs that may attempt nested transactions, meaning that the -- programmer must take special care to prevent these. -- -- However, there are functions for creating transactional variables that -- can always be safely called in unsafePerformIO. See: -- newTVarIO, newTChanIO, newBroadcastTChanIO, -- newTQueueIO, newTBQueueIO, and newTMVarIO. -- -- Using unsafePerformIO inside of atomically is also -- dangerous but for different reasons. See unsafeIOToSTM for more -- on this. atomically :: STM a -> IO a -- | Perform a synchronization of the cache with permanent storage once -- executed the STM transaction when syncWrite policy is -- Synchronous atomicallySync :: STM a -> IO a -- | A monad supporting atomic memory transactions. data STM a -- | Unsafely performs IO in the STM monad. Beware: this is a highly -- dangerous thing to do. -- -- unsafeIOToSTM :: IO a -> STM a -- | Assures that the IO computation finalizes no matter if the STM -- transaction is aborted or retried. The IO computation run in a -- different thread. The STM transaction wait until the completion of the -- IO procedure (or retry as usual). -- -- It can be retried if the embedding STM computation is retried so the -- IO computation must be idempotent. Exceptions are bubbled up to the -- STM transaction safeIOToSTM :: IO a -> STM a data DBRef a -- | Get the reference to the object in the cache. If it does not exist, -- the reference is created empty. Every execution of getDBRef -- returns the same unique reference to this key, so it can be safely -- considered pure. This property is useful because deserialization of -- objects with unused embedded DBRefs do not need to marshall -- them eagerly. This also avoids unnecessary cache lookups of the -- referenced objects. getDBRef :: (Typeable a, IResource a) => String -> DBRef a -- | Return the key of the object referenced by the DBRef keyObjDBRef :: DBRef a -> String -- | Create the object passed as parameter (if it does not exist) and -- -- return its reference in the IO monad. -- If an object with the same -- key already exists, it is returned as is -- If not, the reference is -- created with the new value. -- If you like to update in any case, use -- getDBRef and writeDBRef combined newDBRefIO :: -- (IResource a,Typeable a) => a -> IO (DBRef a) newDBRefIO x= do -- let key = keyResource x mdbref <- mDBRefIO key case mdbref of Right -- dbref -> return dbref -- -- Left cache -> do tv<- newTVarIO DoNotExist let dbref= DBRef key -- tv w <- mkWeakPtr dbref . Just $ fixToCache dbref H.insert cache -- key (CacheElem Nothing w) t <- timeInteger atomically $ do -- applyTriggers [dbref] [Just x] --debug ("before "++key) -- writeTVar tv . Exist $ Elem x t t return dbref -- -- Create the object passed as parameter (if it does not exist) and -- return its reference in the STM monad. If an object with the same key -- already exists, it is returned as is If not, the reference is created -- with the new value. If you like to update in any case, use -- getDBRef and writeDBRef combined if you need to create -- the reference and the reference content, use newDBRef newDBRef :: (IResource a, Typeable a) => a -> STM (DBRef a) -- | Return the reference value. If it is not in the cache, it is fetched -- from the database. readDBRef :: (IResource a, Typeable a) => DBRef a -> STM (Maybe a) -- | Read multiple DBRefs in a single request using the new -- readResourcesByKey readDBRefs :: (IResource a, Typeable a) => [DBRef a] -> STM [Maybe a] -- | Write in the reference a value The new key must be the same than the -- old key of the previous object stored otherwise, an error "law of key -- conservation broken" will be raised -- -- WARNING: the value to be written in the DBRef must be fully evaluated. -- Delayed evaluations at serialization time can cause inconsistencies in -- the database. In future releases this will be enforced. writeDBRef :: (IResource a, Typeable a) => DBRef a -> a -> STM () -- | Delete the content of the DBRef form the cache and from permanent -- storage delDBRef :: (IResource a, Typeable a) => DBRef a -> STM () -- | Must be defined for every object to be cached. class IResource a keyResource :: IResource a => a -> String -- | Implements the database access and marshalling of the object. while -- the database access must be strict, the marshaling must be lazy if, as -- is often the case, some parts of the object are not really accesed. If -- the object contains DBRefs, this avoids unnecesary cache lookups. This -- method is called within atomically blocks. Since STM -- transactions retry, readResourceByKey may be called twice in strange -- situations. So it must be idempotent, not only in the result but also -- in the effect in the database . However, because it is executed by -- safeIOToSTM it is guaranteed that the execution is not -- interrupted. readResourceByKey :: IResource a => String -> IO (Maybe a) -- | hopefully optimized read of many objects by key. readResourcesByKey :: IResource a => [String] -> IO [Maybe a] readResource :: IResource a => a -> IO (Maybe a) -- | To write into persistent storage. It must be strict. Since STM -- transactions may retry, writeResource must be idempotent, not -- only in the result but also in the effect in the database. . However, -- because it is executed by safeIOToSTM it is guaranteed that -- the execution is not interrupted. All the new obbects are writeen to -- the database on synchromization, so writeResource must not autocommit. -- Commit code must be located in the postcondition. (see -- setConditions) Since there is no provision for rollback from -- failure in writing to persistent storage, writeResource must -- retry until success. writeResource :: IResource a => a -> IO () -- | multiple write (hopefully) in a single request. That is up to you and -- your backend . Defined by default as 'mapM_ writeResource' writeResources :: IResource a => [a] -> IO () -- | Delete the resource. It is called syncronously. So it must commit delResource :: IResource a => a -> IO () delResources :: IResource a => [a] -> IO () -- | Resources data definition used by withSTMResources data Resources a b -- | forces a retry Retry :: Resources a b Resources :: [a] -> [a] -> b -> Resources a b -- | resources to be inserted back in the cache [toAdd] :: Resources a b -> [a] -- | resources to be deleted from the cache and from permanent storage [toDelete] :: Resources a b -> [a] -- | result to be returned [toReturn] :: Resources a b -> b -- | Empty resources: resources= Resources [] [] () resources :: Resources a () -- | This is the main function for the *Resource(s) calls. All the rest -- derive from it. The results are kept in the STM monad so it can be -- part of a larger STM transaction involving other DBRefs. The -- Resources register returned by the user-defined function is -- interpreted as such: -- -- -- -- WARNING: To catch evaluations errors at the right place, the values to -- be written must be fully evaluated. Errors in delayed evaluations at -- serialization time can cause inconsistencies in the database. withSTMResources :: (IResource a, Typeable a) => [a] -> ([Maybe a] -> Resources a x) -> STM x -- | To atomically add/modify many objects in the cache -- --
--   withResources rs f=  atomically $ withSTMResources rs f1 >> return() where   f1 mrs= let as= f mrs in  Resources  as [] ()
--   
withResources :: (IResource a, Typeable a) => [a] -> ([Maybe a] -> [a]) -> IO () -- | Update of a single object in the cache -- --
--   withResource r f= withResources [r] ([mr]-> [f mr])
--   
withResource :: (IResource a, Typeable a) => a -> (Maybe a -> a) -> IO () -- | To read a list of resources from the cache if they exist -- -- | getResources rs= atomically $ withSTMResources rs f1 -- where f1 mrs= Resources [] [] mrs getResources :: (IResource a, Typeable a) => [a] -> IO [Maybe a] -- | To read a resource from the cache. -- --
--   getResource r= do{mr<- getResources [r];return $! head mr}
--   
getResource :: (IResource a, Typeable a) => a -> IO (Maybe a) -- | Delete the list of resources from cache and from persistent storage. -- --
--   deleteResources rs= atomically $ withSTMResources rs f1 where  f1 mrs = Resources  [] (catMaybes mrs) ()
--   
deleteResources :: (IResource a, Typeable a) => [a] -> IO () -- | Delete the resource from cache and from persistent storage. -- --
--   deleteResource r= deleteResources [r]
--   
deleteResource :: (IResource a, Typeable a) => a -> IO () -- | Add an user defined trigger to the list of triggers Trriggers are -- called just before an object of the given type is created, modified or -- deleted. The DBRef to the object and the new value is passed to the -- trigger. The called trigger function has two parameters: the DBRef -- being accesed (which still contains the old value), and the new value. -- If the DBRef is being deleted, the second parameter is Nothing. -- if the DBRef contains Nothing, then the object is being created addTrigger :: (IResource a, Typeable a) => (DBRef a -> Maybe a -> STM ()) -> IO () -- | Deletes the referenced object from the cache, not the database (see -- delDBRef) useful for cache invalidation when the database is -- modified by other processes. flushDBRef :: (IResource a, Typeable a) => DBRef a -> STM () -- | flush the element with the given key flushKey :: String -> STM () -- | label the object as not existent in database invalidateKey :: String -> STM () -- | drops the entire cache. flushAll :: STM () type Cache = IORef (Ht, Integer) -- | Set the cache. this is useful for hot loaded modules that will update -- an existing cache. Experimental setCache :: Cache -> IO () -- | Creates a new cache. Experimental newCache :: IO (Ht, Integer) -- | Force the atomic write of all cached objects modified since the last -- save into permanent storage. Cache writes allways save a coherent -- state. As always, only the modified objects are written. syncCache :: IO () -- | stablishes the procedures to call before and after saving with -- syncCache, clearSyncCache or clearSyncCacheProc. -- The postcondition of database persistence should be a commit. setConditions :: IO () -> IO () -> IO () -- | Saves the unsaved elems of the cache. Cache writes allways save a -- coherent state. Unlike syncCache this call deletes some elems -- from the cache when the number of elems > sizeObjects. The -- deletion depends on the check criteria, expressed by the first -- parameter. defaultCheck is the one implemented to be passed by -- default. Look at it to understand the clearing criteria. clearSyncCache :: (Integer -> Integer -> Integer -> Bool) -> Int -> IO () -- | Return the total number of DBRefs in the cache. For debug purposes. -- This does not count the number of objects in the cache since many of -- the DBRefs may not have the referenced object loaded. It's -- O(n). numElems :: IO Int -- | Retuns some statistical information for the DBRefs in the cache (for -- debugging) This returns a tuple containing: total : count of the total -- elements in cache dirty : the elements which need to be written to the -- persistent storage loaded : the elements which are currently hold in -- memory statElems :: IO (Int, Int, Int) -- | Specify the cache synchronization policy with permanent storage. See -- SyncMode for details syncWrite :: SyncMode -> IO () data SyncMode -- | sync state to permanent storage when atomicallySync is invoked Synchronous :: SyncMode Asynchronous :: Int -> (Integer -> Integer -> Integer -> Bool) -> Int -> SyncMode -- | number of seconds between saves when asynchronous [frequency] :: SyncMode -> Int -- | The user-defined check-for-cleanup-from-cache for each object. -- defaultCheck is an example [check] :: SyncMode -> Integer -> Integer -> Integer -> Bool -- | size of the cache when async [cacheSize] :: SyncMode -> Int -- | use syncCache to write the state SyncManual :: SyncMode -- | Start the thread that periodically call clearSyncCache to clean -- and writes on the persistent storage. it is indirectly set by means of -- syncWrite, since it is more higuer level. I recommend to use -- the latter Otherwise, syncCache or clearSyncCache or -- atomicallySync must be invoked explicitly or no persistence -- will exist. Cache writes allways save a coherent state clearSyncCacheProc :: Int -> (Integer -> Integer -> Integer -> Bool) -> Int -> IO ThreadId -- | This is a default cache clearance check. It forces to drop from the -- cache all the elems not accesed since half the time between now and -- the last sync if it returns True, the object will be discarded from -- the cache it is invoked when the cache size exceeds the number of -- objects configured in clearSyncCacheProc or -- clearSyncCache defaultCheck :: Integer -> Integer -> Integer -> Bool -- | Handles Nothing cases in a simpler way than runMaybeT. it is used in -- infix notation. for example: -- --
--   result <- readDBRef ref `onNothing` error ("Not found "++ keyObjDBRef ref)
--   
-- -- or -- --
--   result <- readDBRef ref `onNothing` return someDefaultValue
--   
onNothing :: Monad m => m (Maybe b) -> m b -> m b instance (Data.TCache.IResource.IResource a, Data.Typeable.Internal.Typeable a) => GHC.Read.Read (Data.TCache.Defs.DBRef a) module Data.TCache.Memoization -- | memoize the result of a computation for a certain time. This is useful -- for caching costly data such web pages composed on the fly. -- -- time == 0 means infinite writeCached :: (Typeable b, Typeable a, Indexable a, Executable m) => a -> (a -> m b) -> b -> Integer -> STM () -- | Memoize the result of a computation for a certain time. A string -- key is used to index the result -- -- The Int parameter is the timeout, in second after the last evaluation, -- after which the cached value will be discarded and the expression will -- be evaluated again if demanded . Time == 0 means no timeout cachedByKey :: (Typeable a, Executable m, MonadIO m) => String -> Int -> m a -> m a cachedByKeySTM :: (Typeable a, Executable m) => String -> Int -> m a -> STM a flushCached :: String -> IO () -- | a pure version of cached cachedp :: (Indexable a, Typeable a, Typeable b) => (a -> b) -> a -> b -- | given a string, return a key that can be used in Indexable instances -- Of non persistent objects, such are cached objects (it changes fron -- execution to execution) . It uses addrHash addrStr :: a -> String -- | to execute a monad for the purpose of memoizing its result class Executable m execute :: Executable m => m a -> a instance Data.TCache.Defs.Indexable a => Data.TCache.IResource.IResource (Data.TCache.Memoization.Cached a b) instance Data.TCache.Memoization.Executable GHC.Types.IO instance Data.TCache.Memoization.Executable Data.Functor.Identity.Identity instance Control.Monad.IO.Class.MonadIO Data.Functor.Identity.Identity -- | This module implements an experimental typed query language for TCache -- build on pure haskell. It is minimally intrusive (no special data -- definitions, no special syntax, no template haskell). It uses the same -- register fields from the data definitions. Both for query conditions -- and selections. It is executed in haskell, no external database -- support is needed. -- -- it includes -- -- -- -- An example that register the owner and name fields fo the Car register -- and the name of the Person register, create the Bruce register, return -- the Bruce DBRef, create two Car registers with bruce as owner and -- query for the registers with bruce as owner and its name alpabeticaly -- higuer than "Bat mobile" -- --
--   import Data.TCache
--   import Data.TCache.IndexQuery
--   import Data.TCache.DefaultPersistence
--   import Data.Typeable
--   
--   data Person= Person {pname :: String} deriving  (Show, Read, Eq, Typeable)
--   data Car= Car{owner :: DBRef Person , cname:: String} deriving (Show, Read, Eq, Typeable)
--   
--   instance Indexable Person where key Person{pname= n} = "Person " ++ n
--   instance Indexable Car where key Car{cname= n} = "Car " ++ n
--   
--   main =  do
--      index owner
--      index pname
--      index cname
--      bruce <- atomically $ newDBRef $ Person "bruce"
--      atomically $  mapM_ newDBRef [Car bruce "Bat Mobile", Car bruce "Porsche"]
--      r <- atomically $ cname .==. "Porsche"
--      print r
--      r <- atomically $ select (cname, owner) $  owner .==. bruce .&&. cname .>=. "Bat Mobile"
--      print r
--   
-- -- Will produce: -- --
--   [DBRef "Car Porsche"]
--   [("Porsche",DBRef "Person bruce")]
--   
-- -- NOTES: -- -- -- --
--   data Person = Person {name , surname :: String}
--   
-- -- then a query for name .==. Bruce is -- indistinguishable from surname .==. Bruce -- -- Will return indexOf the registers with surname Bruce as well. -- So if two or more fields in a registers are to be indexed, they must -- have different types. module Data.TCache.IndexQuery -- | Register a trigger for indexing the values of the field passed as -- parameter. the indexed field can be used to perform relational-like -- searches index :: Queriable reg a => (reg -> a) -> IO () (.==.) :: RelationOps field1 field2 res => field1 -> field2 -> STM res infixr 5 .==. (.<.) :: RelationOps field1 field2 res => field1 -> field2 -> STM res infixr 5 .<. (.<=.) :: RelationOps field1 field2 res => field1 -> field2 -> STM res infixr 5 .<=. (.>=.) :: RelationOps field1 field2 res => field1 -> field2 -> STM res infixr 5 .>=. (.>.) :: RelationOps field1 field2 res => field1 -> field2 -> STM res infixr 5 .>. -- | return all the (indexed) values which this field has and a DBRef -- pointer to the register indexOf :: Queriable reg a => (reg -> a) -> STM [(a, [DBRef reg])] recordsWith :: (IResource a, Typeable a) => STM [DBRef a] -> STM [a] (.&&.) :: SetOperations set set' setResult => STM set -> STM set' -> STM setResult infixr 4 .&&. (.||.) :: SetOperations set set' setResult => STM set -> STM set' -> STM setResult infixr 3 .||. select :: Select selector a res => selector -> a -> res class (Read a, Show a, IResource reg, Typeable reg, Typeable a, Ord a, PersistIndex reg) => Queriable reg a instance GHC.Show.Show a => GHC.Show.Show (Data.TCache.IndexQuery.Index reg a) instance (Data.Typeable.Internal.Typeable reg, Data.TCache.IResource.IResource reg) => Data.TCache.IndexQuery.Select (reg -> a) (GHC.Conc.Sync.STM [Data.TCache.Defs.DBRef reg]) (GHC.Conc.Sync.STM [a]) instance (Data.Typeable.Internal.Typeable reg, Data.TCache.IResource.IResource reg, Data.TCache.IndexQuery.Select (reg -> a) (GHC.Conc.Sync.STM [Data.TCache.Defs.DBRef reg]) (GHC.Conc.Sync.STM [a]), Data.TCache.IndexQuery.Select (reg -> b) (GHC.Conc.Sync.STM [Data.TCache.Defs.DBRef reg]) (GHC.Conc.Sync.STM [b])) => Data.TCache.IndexQuery.Select (reg -> a, reg -> b) (GHC.Conc.Sync.STM [Data.TCache.Defs.DBRef reg]) (GHC.Conc.Sync.STM [(a, b)]) instance (Data.Typeable.Internal.Typeable reg, Data.TCache.IResource.IResource reg, Data.TCache.IndexQuery.Select (reg -> a) (GHC.Conc.Sync.STM [Data.TCache.Defs.DBRef reg]) (GHC.Conc.Sync.STM [a]), Data.TCache.IndexQuery.Select (reg -> b) (GHC.Conc.Sync.STM [Data.TCache.Defs.DBRef reg]) (GHC.Conc.Sync.STM [b]), Data.TCache.IndexQuery.Select (reg -> c) (GHC.Conc.Sync.STM [Data.TCache.Defs.DBRef reg]) (GHC.Conc.Sync.STM [c])) => Data.TCache.IndexQuery.Select (reg -> a, reg -> b, reg -> c) (GHC.Conc.Sync.STM [Data.TCache.Defs.DBRef reg]) (GHC.Conc.Sync.STM [(a, b, c)]) instance (Data.Typeable.Internal.Typeable reg, Data.TCache.IResource.IResource reg, Data.TCache.IndexQuery.Select (reg -> a) (GHC.Conc.Sync.STM [Data.TCache.Defs.DBRef reg]) (GHC.Conc.Sync.STM [a]), Data.TCache.IndexQuery.Select (reg -> b) (GHC.Conc.Sync.STM [Data.TCache.Defs.DBRef reg]) (GHC.Conc.Sync.STM [b]), Data.TCache.IndexQuery.Select (reg -> c) (GHC.Conc.Sync.STM [Data.TCache.Defs.DBRef reg]) (GHC.Conc.Sync.STM [c]), Data.TCache.IndexQuery.Select (reg -> d) (GHC.Conc.Sync.STM [Data.TCache.Defs.DBRef reg]) (GHC.Conc.Sync.STM [d])) => Data.TCache.IndexQuery.Select (reg -> a, reg -> b, reg -> c, reg -> d) (GHC.Conc.Sync.STM [Data.TCache.Defs.DBRef reg]) (GHC.Conc.Sync.STM [(a, b, c, d)]) instance (Data.Typeable.Internal.Typeable reg, Data.TCache.IResource.IResource reg, Data.Typeable.Internal.Typeable reg', Data.TCache.IResource.IResource reg', Data.TCache.IndexQuery.Select (reg -> a) (GHC.Conc.Sync.STM [Data.TCache.Defs.DBRef reg]) (GHC.Conc.Sync.STM [a]), Data.TCache.IndexQuery.Select (reg' -> b) (GHC.Conc.Sync.STM [Data.TCache.Defs.DBRef reg']) (GHC.Conc.Sync.STM [b])) => Data.TCache.IndexQuery.Select (reg -> a, reg' -> b) (GHC.Conc.Sync.STM (Data.TCache.IndexQuery.JoinData reg reg')) (GHC.Conc.Sync.STM [([a], [b])]) instance Data.TCache.IndexQuery.SetOperations [Data.TCache.Defs.DBRef a] [Data.TCache.Defs.DBRef a] [Data.TCache.Defs.DBRef a] instance Data.TCache.IndexQuery.SetOperations (Data.TCache.IndexQuery.JoinData a a') [Data.TCache.Defs.DBRef a] (Data.TCache.IndexQuery.JoinData a a') instance Data.TCache.IndexQuery.SetOperations [Data.TCache.Defs.DBRef a] (Data.TCache.IndexQuery.JoinData a a') (Data.TCache.IndexQuery.JoinData a a') instance Data.TCache.IndexQuery.SetOperations (Data.TCache.IndexQuery.JoinData a a') [Data.TCache.Defs.DBRef a'] (Data.TCache.IndexQuery.JoinData a a') instance (Data.TCache.IndexQuery.Queriable reg a, Data.TCache.IndexQuery.Queriable reg' a) => Data.TCache.IndexQuery.RelationOps (reg -> a) (reg' -> a) (Data.TCache.IndexQuery.JoinData reg reg') instance Data.TCache.IndexQuery.Queriable reg a => Data.TCache.IndexQuery.RelationOps (reg -> a) a [Data.TCache.Defs.DBRef reg] instance Data.TCache.IndexQuery.Queriable reg a => Data.TCache.IResource.IResource (Data.TCache.IndexQuery.Index reg a) instance (Data.TCache.IResource.IResource reg, Data.Typeable.Internal.Typeable reg, GHC.Classes.Ord a, GHC.Read.Read a) => GHC.Read.Read (Data.TCache.IndexQuery.Index reg a) instance Data.TCache.IndexQuery.Queriable reg a => Data.TCache.Defs.Serializable (Data.TCache.IndexQuery.Index reg a) instance (Data.Typeable.Internal.Typeable reg, Data.Typeable.Internal.Typeable a) => Data.TCache.Defs.Indexable (Data.TCache.IndexQuery.Index reg a) instance (GHC.Read.Read a, GHC.Show.Show a, Data.TCache.IResource.IResource reg, Data.Typeable.Internal.Typeable reg, Data.Typeable.Internal.Typeable a, GHC.Classes.Ord a, Data.TCache.Defs.PersistIndex reg) => Data.TCache.IndexQuery.Queriable reg a -- | Implements full text indexation (indexText) and text -- search(contains), as an addition to the query language -- implemented in IndexQuery it also can index the lists of -- elements in a field (with indexList) so that it is possible to -- ask for the registers that contains a given element in the given field -- (with containsElem) -- -- An example of full text search and element search in a list in -- combination using the .&&. operator defined in -- "indexQuery". before and after the update of the register -- --
--   data Doc= Doc{title :: String , authors :: [String], body :: String} deriving (Read,Show, Typeable)
--   instance Indexable Doc where
--     key Doc{title=t}= t
--   
--   instance Serializable Doc  where
--     serialize= pack . show
--     deserialize= read . unpack
--   
--   main= do
--     indexText  body T.pack
--     indexList authors  (map T.pack)
--   
--     let doc= Doc{title=  "title", authors=["john","Lewis"], body=  "Hi, how are you"}
--     rdoc <- atomically $ newDBRef doc
--   
--     r0 <- atomically $ select title $ authors `containsElem` "Lewis"
--     print r0
--   
--     r1 <- atomically $ select title $ body `contains` "how are you"
--     print r1
--   
--     r2 <- atomically $ select body $ body `contains` "how are you" .&&. authors containsElem "john"
--     print r2
--   
--     atomically $ writeDBRef rdoc  doc{ body=  "what's up"}
--   
--     r3 <- atomically $ select title $ body  `contains` "how are you"
--     print r3
--   
--     if  r0== r1 && r1== [title doc] then print "OK" else print "FAIL"
--     if  r3== [] then print "OK" else print "FAIL"
--   
module Data.TCache.IndexText -- | start a trigger to index the contents of a register field indexText :: (IResource a, Typeable a, Typeable b) => (a -> b) -> (b -> Text) -> IO () -- | trigger the indexation of list fields with elements convertible to -- Text indexList :: (IResource a, Typeable a, Typeable b) => (a -> b) -> (b -> [Text]) -> IO () -- | return the DBRefs whose fields include all the words in the requested -- text contents.Except the words with less than three characters that -- are not digits or uppercase, that are filtered out before making the -- query contains :: (IResource a, Typeable a, Typeable b) => (a -> b) -> String -> STM [DBRef a] -- | return the DBRefs of the registers whose field (first parameter, -- usually a container) contains the requested value. containsElem :: (IResource a, Typeable a, Typeable b) => (a -> b) -> String -> STM [DBRef a] -- | return all the values of a given field (if it has been indexed with -- index) allElemsOf :: (IResource a, Typeable a, Typeable b) => (a -> b) -> STM [Text] instance GHC.Show.Show Data.TCache.IndexText.IndexText instance GHC.Read.Read Data.TCache.IndexText.IndexText instance Data.TCache.Defs.Serializable Data.TCache.IndexText.IndexText instance Data.TCache.Defs.Indexable Data.TCache.IndexText.IndexText instance Data.TCache.IResource.IResource Data.TCache.IndexText.IndexText -- | This module decouples the 'IResource" class in two classes one for key -- extraction Indexable and other ('Serializable" for -- serlalization and persistence .The last one defines persistence in -- files as default, but it can be changed to persistence in databases, -- for example. -- -- The definitions of these classes are in Defs.hs module Data.TCache.DefaultPersistence -- | Indexable is an utility class used to derive instances of IResource -- -- Example: -- --
--   data Person= Person{ pname :: String, cars :: [DBRef Car]} deriving (Show, Read, Typeable)
--   data Car= Car{owner :: DBRef Person , cname:: String} deriving (Show, Read, Eq, Typeable)
--   
-- -- Since Person and Car are instances of Read ans Show, by -- defining the Indexable instance will implicitly define the -- IResource instance for file persistence: -- --
--   instance Indexable Person where  key Person{pname=n} = "Person " ++ n
--   instance Indexable Car where key Car{cname= n} = "Car " ++ n
--   
class Indexable a key :: Indexable a => a -> String defPath :: Indexable a => a -> String -- | Serialize is an alternative to the IResource class for defining -- persistence in TCache. The deserialization must be as lazy as -- possible. serialization/deserialization are not performance critical -- in TCache -- -- Read, Show, instances are implicit instances of Serializable -- --
--   serialize  = pack . show
--   deserialize= read . unpack
--   
-- -- Since write and read to disk of to/from the cache are not be very -- frequent The performance of serialization is not critical. class Serializable a serialize :: Serializable a => a -> ByteString deserialize :: Serializable a => ByteString -> a deserialKey :: Serializable a => String -> ByteString -> a setPersist :: Serializable a => a -> Maybe Persist -- | Set the default persistence mechanism of all serializable -- objects that have setPersist= const Nothing. By default it is -- filePersist -- -- this statement must be the first one before any other TCache call setDefaultPersist :: Persist -> IO () getDefaultPersist :: Persist -- | Implements default default-persistence of objects in files with their -- keys as filenames filePersist :: Persist -- | a persist mechanism has to implement these three primitives -- filePersist is the default file persistence data Persist Persist :: (Key -> IO (Maybe ByteString)) -> (Key -> ByteString -> IO ()) -> (Key -> IO ()) -> Persist -- | read by key. It must be strict [readByKey] :: Persist -> Key -> IO (Maybe ByteString) -- | write. It must be strict [write] :: Persist -> Key -> ByteString -> IO () -- | delete [delete] :: Persist -> Key -> IO () instance (Data.Typeable.Internal.Typeable a, Data.TCache.Defs.Indexable a, Data.TCache.Defs.Serializable a) => Data.TCache.IResource.IResource a instance Data.TCache.Defs.Serializable a => Data.TCache.Defs.PersistIndex a -- | A persistent, transactional collection with Queue interface as well as -- indexed access by key. -- -- Uses default persistence. See Data.TCache.DefaultPersistence module Data.Persistent.Collection -- | A queue reference type RefQueue a = DBRef (Queue a) -- | Get the reference to new or existing queue trough its name getQRef :: (Typeable a, Serialize a) => String -> RefQueue a -- | Read the first element in the queue and delete it (pop) pop :: (Typeable a, Serialize a) => RefQueue a -> IO a -- | Version in the STM monad popSTM :: (Typeable a, Serialize a) => RefQueue a -> STM a pick :: (Typeable a, Serialize a) => RefQueue a -> IO a -- | Empty the queue (factually, it is deleted) flush :: (Typeable a, Serialize a) => RefQueue a -> IO () -- | Version in the STM monad flushSTM :: (Typeable a, Serialize a) => RefQueue a -> STM () -- | Return the list of all elements in the queue. The queue remains -- unchanged pickAll :: (Typeable a, Serialize a) => RefQueue a -> IO [a] -- | Version in the STM monad pickAllSTM :: (Typeable a, Serialize a) => RefQueue a -> STM [a] -- | Push an element in the queue push :: (Typeable a, Serialize a) => RefQueue a -> a -> IO () -- | Version in the STM monad pushSTM :: (Typeable a, Serialize a) => RefQueue a -> a -> STM () -- | 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) -- | Version in the STM monad pickElemSTM :: (Indexable a, Typeable a, Serialize a) => RefQueue a -> String -> STM (Maybe a) -- | Return the list of all elements in the queue and empty it readAll :: (Typeable a, Serialize a) => RefQueue a -> IO [a] -- | A version in the STM monad readAllSTM :: (Typeable a, Serialize a) => RefQueue a -> STM [a] -- | 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 () -- | Version in the STM monad deleteElemSTM :: (Typeable a, Serialize a, Indexable a) => RefQueue a -> a -> STM () -- | 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 () -- | Version in the STM monad updateElemSTM :: (Indexable a, Typeable a, Serialize a) => RefQueue a -> a -> STM () -- | push an element at the top of the queue unreadSTM :: (Typeable a, Serialize a) => RefQueue a -> a -> STM () -- | Check if the queue is empty isEmpty :: (Typeable a, Serialize a) => RefQueue a -> IO Bool isEmptySTM :: (Typeable a, Serialize a) => RefQueue a -> STM Bool instance Data.TCache.Defs.Indexable (Data.Persistent.Collection.Queue a) instance Data.RefSerialize.Serialize a => Data.RefSerialize.Serialize (Data.Persistent.Collection.Queue a) instance Data.RefSerialize.Serialize a => Data.TCache.Defs.Serializable (Data.Persistent.Collection.Queue a)