acid-state-0.6.3: src/Data/Acid/Common.hs
{-# LANGUAGE GeneralizedNewtypeDeriving, GADTs #-}
-----------------------------------------------------------------------------
-- |
-- Module : Data.Acid.Common
-- Copyright : PublicDomain
--
-- Maintainer : lemmih@gmail.com
-- Portability : non-portable (uses GHC extensions)
--
-- Common structures used by the various backends (local, memory).
--
module Data.Acid.Common where
import Data.Acid.Core
import Control.Monad.State
import Control.Monad.Reader
import Data.SafeCopy
import Data.Serialize (runGet, runGetLazy)
import Control.Applicative
import qualified Data.ByteString as Strict
-- Silly fix for bug in cereal-0.3.3.0's version of runGetLazy.
runGetLazyFix getter inp
= case runGet getter Strict.empty of
Left msg -> runGetLazy getter inp
Right val -> Right val
class (SafeCopy st) => IsAcidic st where
acidEvents :: [Event st]
-- ^ List of events capable of updating or querying the state.
-- | Context monad for Update events.
newtype Update st a = Update { unUpdate :: State st a }
deriving (Monad, Functor, MonadState st)
-- mtl pre-2.0 doesn't have these instances to newtype-derive, but they're
-- simple enough.
instance Applicative (Update st) where
pure = return
(<*>) = ap
-- | Context monad for Query events.
newtype Query st a = Query { unQuery :: Reader st a }
deriving (Monad, Functor, MonadReader st)
instance Applicative (Query st) where
pure = return
(<*>) = ap
-- | Run a query in the Update Monad.
runQuery :: Query st a -> Update st a
runQuery query
= do st <- get
return (runReader (unQuery query) st)
-- | Events return the same thing as Methods. The exact type of 'EventResult'
-- depends on the event.
type EventResult ev = MethodResult ev
type EventState ev = MethodState ev
-- | We distinguish between events that modify the state and those that do not.
--
-- UpdateEvents are executed in a MonadState context and have to be serialized
-- to disk before they are considered durable.
--
-- QueryEvents are executed in a MonadReader context and obviously do not have
-- to be serialized to disk.
data Event st where
UpdateEvent :: UpdateEvent ev => (ev -> Update (EventState ev) (EventResult ev)) -> Event (EventState ev)
QueryEvent :: QueryEvent ev => (ev -> Query (EventState ev) (EventResult ev)) -> Event (EventState ev)
-- | All UpdateEvents are also Methods.
class Method ev => UpdateEvent ev
-- | All QueryEvents are also Methods.
class Method ev => QueryEvent ev
eventsToMethods :: [Event st] -> [MethodContainer st]
eventsToMethods = map worker
where worker :: Event st -> MethodContainer st
worker (UpdateEvent fn) = Method (unUpdate . fn)
worker (QueryEvent fn) = Method (\ev -> do st <- get
return (runReader (unQuery $ fn ev) st)
)