module Control.Monad.ExtRef
( module Data.Lens.Common
, HasReadPart (..)
, Reference (..)
, ReadRefMonad
, ExtRef (..)
, ReadRef
, WriteRef
, modRef
, liftReadRef
, readRef'
, undoTr
, memoRead
, memoWrite
, EqReference (..)
, EqRef
, eqRef
, newEqRef
, toRef
, Morph
, MorphD (..)
, MonadIO' (..)
, listLens
, maybeLens
, showLens
, (.)
, id
) where
import Control.Monad
import Control.Monad.Reader
import Control.Monad.Writer
import Control.Monad.RWS
import Control.Monad.Trans.Identity
import Control.Category
import Data.Maybe
import Data.Lens.Common
import Prelude hiding ((.), id)
import Control.Monad.Restricted
class (HasReadPart (RefMonad r)) => Reference r where
type RefMonad r :: * -> *
readRef :: r a -> ReadRefMonad r a
writeRef :: r a -> a -> RefMonad r ()
lensMap :: Lens a b -> r a -> r b
joinRef :: ReadRefMonad r (r a) -> r a
unitRef :: r ()
type ReadRefMonad m = ReadPart (RefMonad m)
infixr 8 `lensMap`
modRef :: Reference r => r a -> (a -> a) -> RefMonad r ()
r `modRef` f = liftReadPart (readRef r) >>= writeRef r . f
class (Monad m, Reference (Ref m)) => ExtRef m where
type Ref m :: * -> *
liftWriteRef :: Morph (WriteRef m) m
extRef :: Ref m b -> Lens a b -> a -> m (Ref m a)
newRef :: a -> m (Ref m a)
newRef = extRef unitRef $ lens (const ()) (const id)
type WriteRef m = RefMonad (Ref m)
type ReadRef m = ReadRefMonad (Ref m)
liftReadRef :: ExtRef m => Morph (ReadRef m) m
liftReadRef = liftWriteRef . liftReadPart
readRef' :: ExtRef m => Ref m a -> m a
readRef' = liftReadRef . readRef
memoRead :: ExtRef m => m a -> m (m a)
memoRead g = do
s <- newRef Nothing
return $ readRef' s >>= \x -> case x of
Just a -> return a
_ -> g >>= \a -> do
liftWriteRef $ writeRef s $ Just a
return a
memoWrite :: (ExtRef m, Eq b) => (b -> m a) -> m (b -> m a)
memoWrite g = do
s <- newRef Nothing
return $ \b -> readRef' s >>= \x -> case x of
Just (b', a) | b' == b -> return a
_ -> g b >>= \a -> do
liftWriteRef $ writeRef s $ Just (b, a)
return a
instance (ExtRef m, Monoid w) => ExtRef (WriterT w m) where
type Ref (WriterT w m) = Ref m
liftWriteRef = lift . liftWriteRef
extRef x y a = lift $ extRef x y a
instance (ExtRef m) => ExtRef (IdentityT m) where
type Ref (IdentityT m) = Ref m
liftWriteRef = lift . liftWriteRef
extRef r k a = lift $ extRef r k a
instance (ExtRef m, Monoid w) => ExtRef (RWST r w s m) where
type Ref (RWST r w s m) = Ref m
liftWriteRef = lift . liftWriteRef
extRef r k a = lift $ extRef r k a
undoTr
:: ExtRef m =>
(a -> a -> Bool)
-> Ref m a
-> m ( ReadRef m (Maybe (WriteRef m ()))
, ReadRef m (Maybe (WriteRef m ()))
)
undoTr eq r = do
ku <- extRef r undoLens ([], [])
let try f = liftM (liftM (writeRef ku) . f) $ readRef ku
return (try undo, try redo)
where
undoLens = lens get set where
get = head . fst
set x (x' : xs, ys) | eq x x' = (x: xs, ys)
set x (xs, _) = (x : xs, [])
undo (x: xs@(_:_), ys) = Just (xs, x: ys)
undo _ = Nothing
redo (xs, y: ys) = Just (y: xs, ys)
redo _ = Nothing
class Reference r => EqReference r where
hasEffect
:: r a
-> (a -> a)
-> ReadRefMonad r Bool
data EqRef_ r a = forall b . Eq b => EqRef_ (r b) (Lens b a)
newtype EqRef r a = EqRef { runEqRef :: ReadRefMonad r (EqRef_ r a) }
eqRef :: (Reference r, Eq a) => r a -> EqRef r a
eqRef r = EqRef $ return $ EqRef_ r id
newEqRef :: (ExtRef m, Eq a) => a -> m (EqRef (Ref m) a)
newEqRef = liftM eqRef . newRef
toRef :: Reference r => EqRef r a -> r a
toRef (EqRef m) = joinRef $ liftM (\(EqRef_ r k) -> k `lensMap` r) m
instance Reference r => EqReference (EqRef r) where
hasEffect m f = runEqRef m >>= \(EqRef_ r k) -> liftM (\x -> modL k f x /= x) $ readRef r
instance Reference r => Reference (EqRef r) where
type (RefMonad (EqRef r)) = RefMonad r
readRef = readRef . toRef
writeRef = writeRef . toRef
lensMap l (EqRef m) = EqRef $ m >>= \(EqRef_ r k) -> return $ EqRef_ r $ l . k
joinRef = EqRef . join . liftM runEqRef
unitRef = eqRef unitRef
showLens :: (Show a, Read a) => Lens a String
showLens = lens show $ \s def -> maybe def fst $ listToMaybe $ reads s
listLens :: Lens (Bool, (a, [a])) [a]
listLens = lens get set where
get (False, _) = []
get (True, (l, r)) = l: r
set [] (_, x) = (False, x)
set (l: r) _ = (True, (l, r))
maybeLens :: Lens (Bool, a) (Maybe a)
maybeLens = lens (\(b,a) -> if b then Just a else Nothing)
(\x (_,a) -> maybe (False, a) (\a' -> (True, a')) x)