lgtk-0.5.1: src/Control/Monad/ExtRef.hs
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ExistentialQuantification #-}
module Control.Monad.ExtRef
( module Data.Lens.Common
-- * Restricted monads
, HasReadPart (..)
-- * Reference class
, Reference (..)
, ReadRefMonad
-- * Ref construction class
, ExtRef (..)
, ReadRef
, WriteRef
-- * Derived constructs
, modRef
, liftReadRef
, readRef'
, undoTr
, memoRead
, memoWrite
-- * References with equation
, EqRef
, eqRef
, toRef
, hasEffect
-- * Auxiliary definitions
, Morph
, MorphD (..)
, MonadIO' (..)
-- * Auxiliary lens definitions
, listLens
, maybeLens
, showLens
-- * Re-exported
, (.)
, 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
{- |
A reference @(r a)@ is isomorphic to @('Lens' s a)@ for some fixed state @s@.
@r@ === @Lens s@
-}
class (HasReadPart (RefMonad r)) => Reference r where
{- | @Refmonad r@ === @State s@
Property derived from the 'HasReadPart' instance:
@ReadRefMonad r@ = @ReadPart (Refmonad r)@ === @Reader s@
-}
type RefMonad r :: * -> *
{- | @readRef@ === @reader . getL@
Properties derived from the 'HasReadPart' instance:
@(readRef r >> return ())@ === @return ()@
-}
readRef :: r a -> ReadRefMonad r a
{- | @writeRef r@ === @modify . setL r@
Properties derived from the set-get, get-set and set-set laws for lenses:
* @(readRef r >>= writeRef r)@ === @return ()@
* @(writeRef r a >> readRef r)@ === @return a@
* @(writeRef r a >> writeRef r a')@ === @writeRef r a'@
-}
writeRef :: r a -> a -> RefMonad r ()
{- | Apply a lens on a reference.
@lensMap@ === @(.)@
-}
lensMap :: Lens a b -> r a -> r b
{- | @joinRef@ makes possible to define dynamic references, i.e. references which depends on
values of other references.
It is not possible to create new reference dynamically with @joinRef@; for that, see 'onChange'.
@joinRef@ === @Lens . join . (runLens .) . runReader@
-}
joinRef :: ReadRefMonad r (r a) -> r a
-- | @unitRef@ === @lens (const ()) (const id)@
unitRef :: r ()
type ReadRefMonad m = ReadPart (RefMonad m)
infixr 8 `lensMap`
-- | @modRef r f@ === @liftReadPart (readRef r) >>= writeRef r . f@
modRef :: Reference r => r a -> (a -> a) -> RefMonad r ()
r `modRef` f = liftReadPart (readRef r) >>= writeRef r . f
{- | Monad for reference creation. Reference creation is not a method
of the 'Reference' type class to make possible to
create the same type of references in multiple monads.
@(Extref m) === (StateT s m)@, where 's' is an extendible state.
For basic usage examples, look into the source of @Control.Monad.ExtRef.Pure.Test@.
-}
class (Monad m, Reference (Ref m)) => ExtRef m where
type Ref m :: * -> *
-- | @'WriteRef' m@ is a submonad of @m@.
liftWriteRef :: Morph (WriteRef m) m
{- | Reference creation by extending the state of an existing reference.
Suppose that @r@ is a reference and @k@ is a lens.
Law 1: @extRef@ applies @k@ on @r@ backwards, i.e.
the result of @(extRef r k a0)@ should behaves exactly as @(lensMap k r)@.
* @(liftM (k .) $ extRef r k a0)@ === @return r@
Law 2: @extRef@ does not change the value of @r@:
* @(extRef r k a0 >> readRef r)@ === @(readRef r)@
Law 3: Proper initialization of newly defined reference with @a0@:
* @(extRef r k a0 >>= readRef)@ === @(readRef r >>= setL k a0)@
-}
extRef :: Ref m b -> Lens a b -> a -> m (Ref m a)
{- | @newRef@ extends the state @s@ in an independent way.
@newRef@ === @extRef unitRef (lens (const ()) (const id))@
-}
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)
{- | @ReadRef@ lifted to the reference creation class.
Note that we do not lift @WriteRef@ to the reference creation class, which a crucial restriction
in the LGtk interface; this is a feature.
-}
liftReadRef :: ExtRef m => Morph (ReadRef m) m
liftReadRef = liftWriteRef . liftReadPart
{- | @readRef@ lifted to the reference creation class.
@readRef'@ === @liftReadRef . readRef@
-}
readRef' :: ExtRef m => Ref m a -> m a
readRef' = liftReadRef . readRef
{- | Lazy monadic evaluation.
In case of @y <- memoRead x@, invoking @y@ will invoke @x@ at most once.
Laws:
* @(memoRead x >> return ())@ === @return ()@
* @(memoRead x >>= id)@ === @x@
* @(memoRead x >>= \y -> liftM2 (,) y y)@ === @liftM (\a -> (a, a)) y@
* @(memoRead x >>= \y -> liftM3 (,) y y y)@ === @liftM (\a -> (a, a, a)) y@
* ...
-}
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
-- | This instance is used in the implementation, end users do not need it.
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 (ReaderT s m) where
type Ref (ReaderT s m) = Ref m
liftWriteRef = lift . liftWriteRef
extRef r k a = lift $ extRef r k a
-}
-- | This instance is used in the implementation, end users do not need it.
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
-- | This instance is used in the implementation, end users do not need it.
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
-- | Undo-redo state transformation.
undoTr
:: ExtRef m =>
(a -> a -> Bool) -- ^ equality on state
-> Ref m a -- ^ reference of state
-> m ( ReadRef m (Maybe (WriteRef m ()))
, ReadRef m (Maybe (WriteRef m ()))
) -- ^ undo and redo actions
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
data EqRef_ r a = forall b . Eq b => EqRef_ (r b) (Lens b a)
{- | References with inherent equivalence.
@EqRef r a@ === @ReadRefMonad r (forall b . Eq b => (Lens b a, r b))@
As a reference, @(m :: EqRef r a)@ behaves as
@joinRef $ liftM (uncurry lensMap) m@
@EqRef@ makes defining auto-sensitive buttons easier, see later.
-}
newtype EqRef r a = EqRef { runEqRef :: ReadRefMonad r (EqRef_ r a) }
{- | @EqRef@ construction.
@hasEffect@ is correct only if @eqRef@ is applied on a pure reference (a reference which is a pure lens on the hidden state).
-}
eqRef :: (Reference r, Eq a) => r a -> EqRef r a
eqRef r = EqRef $ return $ EqRef_ r id
{- | An @EqRef@ is a normal reference if we forget about the equality.
@toRef m@ === @joinRef $ liftM (uncurry lensMap) m@
-}
toRef :: Reference r => EqRef r a -> r a
toRef (EqRef m) = joinRef $ liftM (\(EqRef_ r k) -> k `lensMap` r) m
-- | @hasEffect r f@ returns @False@ iff @(modRef m f)@ === @(return ())@.
hasEffect
:: Reference r
=> EqRef r a
-> (a -> a)
-> ReadRefMonad r Bool
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)