module UniqueLogic.ST.TF.System (
Variable,
globalVariable,
C, update,
simpleUpdate,
updateIfNew,
updateAndCheck,
Fragile(break),
Value, ValueConstraint, valueConstraint,
T,
localVariable,
constant,
assignment2,
assignment3,
Apply, arg, runApply, runApplyMaybe,
solve, solveDepthFirst, solveBreadthFirst,
query,
) where
import qualified Control.Monad.Trans.Except as ME
import qualified Control.Monad.Trans.Writer as MW
import qualified Control.Monad.Trans.Class as MT
import qualified UniqueLogic.ST.TF.MonadTrans as UMT
import qualified Data.Sequence as Seq
import qualified Data.Foldable as Fold
import qualified Data.Ref as Ref
import Control.Monad.Trans.Writer (WriterT, )
import Control.Monad.Trans.Maybe (MaybeT(MaybeT), runMaybeT, mapMaybeT, )
import Control.Monad.Trans.Identity (IdentityT, )
import Control.Monad (when, liftM, liftM2, ap, guard, join, )
import Control.Applicative (Applicative, liftA2, pure, (<*>), )
import Data.Sequence (Seq, (|>), ViewL((:<)), )
import Data.Functor.Compose (Compose(Compose))
import Data.Maybe (isNothing, )
import Data.Monoid (Monoid, mempty, mappend, mconcat, )
import Data.Semigroup (Semigroup, (<>), )
import Prelude hiding (break)
data Variable w s a =
Variable {
varUpdate :: MaybeT s a -> Update w s,
dependsRef :: Ref.T s (Updates w s),
valueRef :: Ref.T s (Maybe a)
}
type Update w s = UMT.Wrap w s (Updates w s)
newtype Updates w s = Updates {unpackUpdates :: Seq (Update w s)}
instance Semigroup (Updates w s) where
Updates x <> Updates y = Updates $ x <> y
instance Monoid (Updates w s) where
mempty = Updates Seq.empty
mappend (Updates x) (Updates y) = Updates $ mappend x y
addUpdate :: Update w s -> Updates w s -> Updates w s
addUpdate x (Updates xs) = Updates $ xs |> x
type Updater w s a =
Ref.T s (Updates w s) -> Ref.T s (Maybe a) ->
MaybeT (UMT.Wrap w s) a -> Update w s
type SimpleUpdater w s a =
Ref.T s (Updates w s) -> Ref.T s (Maybe a) ->
MaybeT s a -> Update w s
newtype T w s a =
Cons {run :: WriterT [Ref.T s (Updates w s)] s a}
instance (Ref.C s) => Functor (T w s) where
fmap f (Cons x) = Cons (liftM f x)
instance (Ref.C s) => Applicative (T w s) where
pure = Cons . return
(<*>) = ap
instance (Ref.C s) => Monad (T w s) where
return = Cons . return
Cons x >>= k = Cons $ run . k =<< x
lift :: (Monad s) => s a -> T w s a
lift = Cons . MT.lift
globalVariable ::
(UMT.C w, Value w a, Ref.C s) =>
SimpleUpdater w s a -> s (Variable w s a)
globalVariable triggerUpdate = object triggerUpdate Nothing
localVariable :: (C w, Value w a, Ref.C s) => T w s (Variable w s a)
localVariable = lift $ globalVariable simpleUpdate
constant ::
(C w, Value w a, Ref.C s) =>
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 ::
(Ref.C s) =>
SimpleUpdater w s a ->
Maybe a -> s (Variable w s a)
object updater ma = do
al <- Ref.new mempty
av <- Ref.new ma
return $ Variable (updater al av) al av
solve, solveDepthFirst, solveBreadthFirst ::
(UMT.C w, Ref.C s) =>
T w s a -> w s a
solve = solveDepthFirst
data Order = DepthFirst | BreadthFirst
deriving (Eq, Enum)
solveDepthFirst = solveOrder DepthFirst
solveBreadthFirst = solveOrder BreadthFirst
solveOrder ::
(UMT.C w, Ref.C s) =>
Order -> T w s a -> w s a
solveOrder order (Cons m) = UMT.unwrap $ do
let resolve updates =
case Seq.viewl updates of
Seq.EmptyL -> return ()
currentUpdate :< remUpdates -> do
Updates newUpdates <- currentUpdate
resolve $
case order of
DepthFirst -> mappend newUpdates remUpdates
BreadthFirst -> mappend remUpdates newUpdates
(a, w) <- UMT.lift $ MW.runWriterT m
resolve . unpackUpdates . mconcat =<< mapM (UMT.lift . Ref.read) w
return a
query :: Variable w s a -> s (Maybe a)
query = Ref.read . valueRef
updateIfNew :: (C w, Ref.C s) => Updater w s a
updateIfNew al av act = do
as <- UMT.lift $ Ref.read av
fmap Fold.fold $ runMaybeT $ do
guard $ isNothing as
MT.lift . UMT.lift . Ref.write av . Just =<< act
MT.lift $ UMT.lift $ Ref.read al
class Inconsistency e where
inconsistency :: e
instance Inconsistency e => Fragile (ME.ExceptT e) where
break = UMT.wrap $ ME.throwE inconsistency
class C t => Fragile t where
break :: Monad m => UMT.Wrap t m a
updateAndCheck ::
(UMT.C w, Ref.C s) =>
(a -> a -> UMT.Wrap w s ()) ->
Updater w s a
updateAndCheck customBreak al av act = do
maold <- UMT.lift $ Ref.read av
manew <- runMaybeT act
case manew of
Nothing -> return mempty
Just anew -> do
UMT.lift . Ref.write av . Just $ anew
case maold of
Just aold -> customBreak aold anew >> return mempty
Nothing -> UMT.lift $ Ref.read al
class C w => Value w a where
data ValueConstraint w a :: *
valueConstraint ::
Ref.T s (Updates w s) -> Ref.T s (Maybe a) -> ValueConstraint w a
class UMT.C w => C w where
update :: (Value w a, Ref.C s) => Updater w s a
instance Value IdentityT a where
data ValueConstraint IdentityT a = IdentityConstraint
valueConstraint _ _ = IdentityConstraint
instance C IdentityT where
update = updateIfNew
instance (Monoid w) => Value (MW.WriterT w) a where
data ValueConstraint (MW.WriterT w) a = WriterConstraint
valueConstraint _ _ = WriterConstraint
instance (Monoid w) => C (MW.WriterT w) where
update = updateIfNew
instance (Inconsistency e, Eq a) => Value (ME.ExceptT e) a where
data ValueConstraint (ME.ExceptT e) a =
Eq a => ExceptionConstraint
valueConstraint _ _ = ExceptionConstraint
instance (Inconsistency e) => C (ME.ExceptT e) where
update al av act =
case valueConstraint al av of
ExceptionConstraint ->
updateAndCheck (\aold anew -> when (aold /= anew) break) al av act
simpleUpdate :: (C w, Value w a, Ref.C s) => SimpleUpdater w s a
simpleUpdate al av = update al av . mapMaybeT UMT.lift
readSTRefM :: Ref.T s (Maybe a) -> MaybeT s a
readSTRefM = MaybeT . Ref.read
assignment2 ::
(UMT.C w, Ref.C s) =>
(a -> b) ->
Variable w s a -> Variable w s b ->
T w s ()
assignment2 f (Variable _ al av) b =
let triggerUpdate =
varUpdate b $ liftM f $ readSTRefM av
in lift $
Ref.modify al (addUpdate triggerUpdate)
assignment3 ::
(UMT.C w, Ref.C s) =>
(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 triggerUpdate =
varUpdate c $
liftM2 f (readSTRefM av) (readSTRefM bv)
in lift $
Ref.modify al (addUpdate triggerUpdate) >>
Ref.modify bl (addUpdate triggerUpdate)
newtype Apply w s a =
Apply (Compose (MW.Writer [Ref.T s (Updates w s)]) (MaybeT s) a)
arg :: Variable w s a -> Apply w s a
arg (Variable _update al av) =
Apply $ Compose $ MW.writer (readSTRefM av, [al])
instance (Ref.C s) => Functor (Apply w s) where
fmap f (Apply (Compose a)) = Apply $ Compose $ fmap (liftM f) a
instance (Ref.C s) => Applicative (Apply w s) where
pure a = Apply $ Compose $ pure $ return a
Apply (Compose f) <*> Apply (Compose a) = Apply $ Compose $ liftA2 ap f a
runApply ::
(UMT.C w, Ref.C s) =>
Apply w s a -> Variable w s a -> T w s ()
runApply (Apply (Compose w)) a =
uncurry (runUpdate a) $ MW.runWriter w
runApplyMaybe ::
(UMT.C w, Ref.C s) =>
Apply w s (Maybe a) -> Variable w s a -> T w s ()
runApplyMaybe (Apply (Compose w)) a =
case MW.runWriter w of
(mf, refs) ->
runUpdate a (MaybeT $ liftM join $ runMaybeT mf) refs
runUpdate ::
(Ref.C s) =>
Variable w s a -> MaybeT s a ->
[Ref.T s (Updates w s)] -> T w s ()
runUpdate a f refs =
lift $ Fold.forM_ refs $ flip Ref.modify (addUpdate $ varUpdate a f)