module UniqueLogic.ST.System (
Variable,
globalVariable,
C, doUpdate,
simpleUpdate,
updateIfNew,
updateAndCheck,
Fragile(break),
T,
localVariable,
constant,
assignment2,
assignment3,
Apply, arg, runApply,
solve,
query,
queryForbid,
queryIgnore,
queryVerify,
) where
import qualified Control.Monad.Exception.Synchronous as E
import qualified Control.Monad.Trans.Writer as MW
import qualified Control.Monad.Trans.Class as MT
import qualified UniqueLogic.ST.MonadTrans as UMT
import qualified UniqueLogic.ST.Duplicate as Duplicate
import qualified Data.Foldable as Fold
import Control.Monad.Trans.Writer (WriterT, )
import Control.Monad.Trans.Maybe (MaybeT(MaybeT), runMaybeT, mapMaybeT, )
import Control.Monad.Trans.Identity (IdentityT, )
import Control.Monad.ST (ST, )
import Control.Monad.HT (void, (<=<), )
import Control.Monad (when, liftM2, ap, )
import Control.Applicative (Applicative, pure, (<*>), )
import Data.Functor.Compose (Compose(Compose))
import Data.STRef (STRef, newSTRef, modifySTRef, readSTRef, writeSTRef, )
import Data.Maybe (isNothing, )
import Data.Monoid (Monoid, )
import Prelude hiding (break)
data Variable w s a =
Variable {
varUpdate :: MaybeT (ST s) a -> Update w s,
dependsRef :: STRef s [Update w s],
valueRef :: STRef s (Maybe a)
}
type Update w s = UMT.Wrap w (ST s) ()
type Updater w s a =
STRef s [Update w s] -> STRef s (Maybe a) ->
MaybeT (UMT.Wrap w (ST s)) a -> Update w s
type SimpleUpdater w s a =
STRef s [Update w s] -> STRef s (Maybe a) ->
MaybeT (ST s) a -> Update w s
newtype T w s a =
Cons {run :: WriterT [STRef s [Update w s]] (ST s) a}
instance Functor (T w s) where
fmap f (Cons x) = Cons (fmap f x)
instance Applicative (T w s) where
pure = Cons . return
(<*>) = ap
instance Monad (T w s) where
return = Cons . return
Cons x >>= k = Cons $ run . k =<< x
lift :: ST s a -> T w s a
lift = Cons . MT.lift
globalVariable ::
(UMT.C w, Duplicate.C a) =>
SimpleUpdater w s a -> ST s (Variable w s a)
globalVariable update = object update Nothing
localVariable :: (C w, Duplicate.C a) => T w s (Variable w s a)
localVariable = lift $ globalVariable simpleUpdate
constant ::
(C w, Duplicate.C a) =>
a -> T w s (Variable w s a)
constant a =
do v <- lift $ object simpleUpdate $ Just a
Cons $ MW.tell [dependsRef v]
return v
object ::
(STRef s [Update w s] -> STRef s (Maybe a) ->
MaybeT (ST s) a -> Update w s) ->
Maybe a -> ST s (Variable w s a)
object updater ma = do
al <- newSTRef []
av <- newSTRef ma
return $ Variable (updater al av) al av
resolve ::
UMT.C w =>
STRef s [Update w s] -> Update w s
resolve =
sequence_ <=< UMT.lift . readSTRef
solve ::
UMT.C w =>
T w s a -> w (ST s) a
solve (Cons m) = UMT.unwrap $ do
(a,w) <- UMT.lift $ MW.runWriterT m
mapM_ resolve w
return a
query :: Variable w s a -> ST s (Maybe a)
query = readSTRef . valueRef
queryForbid :: Variable w s (Duplicate.Forbid a) -> ST s (Maybe a)
queryForbid = fmap (fmap (\(Duplicate.Forbid a) -> a)) . query
queryIgnore :: Variable w s (Duplicate.Ignore a) -> ST s (Maybe a)
queryIgnore = fmap (fmap (\(Duplicate.Ignore a) -> a)) . query
queryVerify :: Variable w s (Duplicate.Verify a) -> ST s (Maybe a)
queryVerify = fmap (fmap (\(Duplicate.Verify a) -> a)) . query
updateIfNew :: (C w, Duplicate.C a) => Updater w s a
updateIfNew al av act = do
as <- UMT.lift $ readSTRef av
when (isNothing as) $ void $ runMaybeT $ do
MT.lift . UMT.lift . writeSTRef av . Just =<< act
MT.lift $ resolve al
class Inconsistency e where
inconsistency :: e
instance
Inconsistency e =>
Fragile (E.ExceptionalT e) where
break =
UMT.wrap $ E.throwT inconsistency
class C t => Fragile t where
break :: Monad m => UMT.Wrap t m a
updateAndCheck ::
(UMT.C w, Duplicate.C a) =>
(a -> a -> UMT.Wrap w (ST s) ()) ->
Updater w s a
updateAndCheck customBreak al av act = do
maold <- UMT.lift $ readSTRef av
manew <- runMaybeT act
Fold.forM_ manew $ \anew -> do
UMT.lift . writeSTRef av . Just $ anew
case maold of
Just aold ->
when (not $ Duplicate.accept aold anew) $
customBreak aold anew
Nothing -> resolve al
class UMT.C w => C w where
doUpdate :: (Duplicate.C a) => Updater w s a
instance C IdentityT where
doUpdate = updateIfNew
instance (Monoid w) => C (MW.WriterT w) where
doUpdate = updateIfNew
instance (Inconsistency e) => C (E.ExceptionalT e) where
doUpdate = updateAndCheck $ \_ _ -> break
simpleUpdate :: (C w, Duplicate.C a) => SimpleUpdater w s a
simpleUpdate al av = doUpdate al av . mapMaybeT UMT.lift
readSTRefM :: STRef s (Maybe a) -> MaybeT (ST s) a
readSTRefM = MaybeT . readSTRef
assignment2 ::
UMT.C w =>
(a -> b) ->
Variable w s a -> Variable w s b ->
T w s ()
assignment2 f (Variable _ al av) b =
let update =
varUpdate b $ fmap f $ readSTRefM av
in lift $
modifySTRef al (update :)
assignment3 ::
UMT.C w =>
(a -> b -> c) ->
Variable w s a -> Variable w s b -> Variable w s c ->
T w s ()
assignment3 f (Variable _ al av) (Variable _ bl bv) c =
let update =
varUpdate c $
liftM2 f (readSTRefM av) (readSTRefM bv)
in lift $
modifySTRef al (update :) >>
modifySTRef bl (update :)
newtype Apply w s a =
Apply (Compose (MW.Writer [STRef s [Update w s]]) (MaybeT (ST s)) a)
arg :: Variable w s a -> Apply w s a
arg (Variable _update al av) =
Apply $ Compose $ MW.writer (MaybeT $ readSTRef av, [al])
instance Functor (Apply w s) where
fmap f (Apply a) = Apply $ fmap f a
instance Applicative (Apply w s) where
pure a = Apply $ pure a
Apply f <*> Apply a = Apply $ f <*> a
runApply ::
UMT.C w =>
Apply w s a -> Variable w s a -> T w s ()
runApply (Apply (Compose w)) a =
case MW.runWriter w of
(f, refs) ->
lift $ Fold.forM_ refs $ flip modifySTRef (varUpdate a f :)