monad-lgbt 0.0.1 → 0.0.2
raw patch · 4 files changed
+179/−30 lines, 4 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
+ Control.Monad.Backtrack: data BacktrackT r m a
+ Control.Monad.Backtrack: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Control.Monad.Backtrack.BacktrackT r m)
+ Control.Monad.Backtrack: instance Control.Monad.Trans.Class.MonadTrans (Control.Monad.Backtrack.BacktrackT r)
+ Control.Monad.Backtrack: instance GHC.Base.Alternative (Control.Monad.Backtrack.BacktrackT r m)
+ Control.Monad.Backtrack: instance GHC.Base.Applicative (Control.Monad.Backtrack.BacktrackT r m)
+ Control.Monad.Backtrack: instance GHC.Base.Functor (Control.Monad.Backtrack.BacktrackT r m)
+ Control.Monad.Backtrack: instance GHC.Base.Monad (Control.Monad.Backtrack.BacktrackT r m)
+ Control.Monad.Backtrack: instance GHC.Base.MonadPlus (Control.Monad.Backtrack.BacktrackT r m)
+ Control.Monad.Backtrack: runBacktrackT :: BacktrackT r m a -> (String -> m r) -> (a -> m r) -> m r
+ Control.Monad.State.LGBT: data LGBT localState globalState m a
+ Control.Monad.State.LGBT: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Control.Monad.State.LGBT.LGBT localState globalState m)
+ Control.Monad.State.LGBT: instance Control.Monad.Trans.Class.MonadTrans (Control.Monad.State.LGBT.LGBT localState globalState)
+ Control.Monad.State.LGBT: instance GHC.Base.Applicative (Control.Monad.State.LGBT.LGBT localState globalState m)
+ Control.Monad.State.LGBT: instance GHC.Base.Applicative m => GHC.Base.Alternative (Control.Monad.State.LGBT.LGBT localState globalState m)
+ Control.Monad.State.LGBT: instance GHC.Base.Functor (Control.Monad.State.LGBT.LGBT localState globalState m)
+ Control.Monad.State.LGBT: instance GHC.Base.Monad m => Control.Monad.State.LGBT.MonadLGBT (Control.Monad.State.LGBT.LGBT localState globalState m) localState globalState
+ Control.Monad.State.LGBT: instance GHC.Base.Monad m => GHC.Base.Monad (Control.Monad.State.LGBT.LGBT localState globalState m)
+ Control.Monad.State.LGBT: instance GHC.Base.MonadPlus m => GHC.Base.MonadPlus (Control.Monad.State.LGBT.LGBT localState globalState m)
+ Control.Monad.State.LGBT: runLGBT :: Monad m => LGBT localState globalState m result -> localState -> globalState -> m (Either String (result, localState), globalState)
- Control.Monad.State.LGBT: withGlobal :: Monad m => (globalState -> m globalState) -> LGLT globalState globalState m ()
+ Control.Monad.State.LGBT: withGlobal :: (Monad m, MonadTrans t, MonadLGBT (t m) localState globalState) => (globalState -> m globalState) -> t m ()
- Control.Monad.State.LGBT: withLocal :: Monad m => (localState -> m localState) -> LGLT localState globalState m ()
+ Control.Monad.State.LGBT: withLocal :: (Monad m, MonadTrans t, MonadLGBT (t m) localState globalState) => (localState -> m localState) -> t m ()
Files
- monad-lgbt.cabal +5/−3
- src/Control/Monad/Backtrack.hs +69/−0
- src/Control/Monad/State/LGBT.hs +92/−16
- test/Main.hs +13/−11
monad-lgbt.cabal view
@@ -3,19 +3,19 @@ -- see: https://github.com/sol/hpack name: monad-lgbt-version: 0.0.1+version: 0.0.2 synopsis: Monad transformers for combining local and global state. description: This is library providing a nice typeclass interface for monads with two different states: local and global. Local state is backtraced whenever intervening monad transformer backtracks. Global state is preserved across all backtracing. It provides nice, classy interface for monads with backtracking/backjumping/continuations. category: Control stability: beta homepage: https://github.com/mgajda/monad-lgbt#readme bug-reports: https://github.com/mgajda/monad-lgbt/issues-build-type: Simple-cabal-version: >= 1.10 author: Michal J. Gajda <mjgajda@gmail.com> maintainer: Michal J. Gajda <mjgajda@gmail.com> license: BSD2 license-file: LICENSE+build-type: Simple+cabal-version: >= 1.10 source-repository head type: git@@ -34,6 +34,7 @@ , mtl exposed-modules: Control.Monad.State.LGBT+ Control.Monad.Backtrack other-modules: Paths_monad_lgbt default-language: Haskell2010@@ -53,5 +54,6 @@ , containers , mtl other-modules:+ Control.Monad.Backtrack Control.Monad.State.LGBT default-language: Haskell2010
+ src/Control/Monad/Backtrack.hs view
@@ -0,0 +1,69 @@+{-# LANGUAGE RankNTypes #-}+-- | Simple monad that allows _depth-first_ backtracking+-- instead of fair conjunction/disjunction behaviour+-- as in @LogicT@.+module Control.Monad.Backtrack(+ BacktrackT+ , runBacktrackT+ ) where++import Control.Applicative+import Control.Monad+import Control.Monad.Trans++newtype BacktrackT r m a = BacktrackT { runBacktrackT :: (String -> m r) -- ^ failure+ -> (a -> m r) -- ^ success+ -> m r -- ^ result+ }++instance Functor (BacktrackT r m) where+ fmap f m = BacktrackT $ \cf cs -> runBacktrackT m cf $ cs . f+ {-# INLINE fmap #-}++instance Applicative (BacktrackT r m) where+ pure x = BacktrackT (\_cf cs -> cs x)+ {-# INLINE pure #-}+ f <*> v = BacktrackT $ \cf cs -> runBacktrackT f cf+ $ \r -> runBacktrackT v cf (cs . r)+ {-# INLINE (<*>) #-}++instance Monad (BacktrackT r m) where+ m >>= k = BacktrackT $ \cf cs -> runBacktrackT m cf (\v -> runBacktrackT (k v) cf cs)+ fail s = BacktrackT $ \cf _cs -> cf s++instance MonadTrans (BacktrackT r) where+ lift m = BacktrackT $ \_cf cs -> m >>= cs+ {-# INLINE lift #-}++instance (MonadIO m) => MonadIO (BacktrackT r m) where+ liftIO = lift . liftIO+ {-# INLINE liftIO #-}++instance Alternative (BacktrackT r m) where+ empty = BacktrackT $ \cf _cs -> cf "<empty alternative>"+ {-# INLINE empty #-}+ a <|> b = BacktrackT $ \cf cs -> runBacktrackT a (\_s -> runBacktrackT b cf cs) cs+ {-# INLINE (<|>) #-}+ many = munch []+ {-# INLINE many #-}+ some p = p >>= (\a -> munch [a] p)+ {-# INLINE some #-}++-- | Munch as many as possible, depth-first.+-- Note that it always succeeds - possibly with empty result.+-- That allows it to backjump efficiently, instead of using @Alternative@.+munch :: [a] -> BacktrackT r m a -> BacktrackT r m [a]+munch initialAcc p = BacktrackT $ \_cf cs -> go cs initialAcc+ where+ go cs acc = runBacktrackT p onFailure onSuccess + where+ onSuccess a = go cs $ a:acc+ onFailure _ = cs $ reverse acc+{-# INLINE munch #-}++instance MonadPlus (BacktrackT r m) where+ mzero = empty+ {-# INLINE mzero #-}+ mplus = (<|>)+ {-# INLINE mplus #-}+
src/Control/Monad/State/LGBT.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE PartialTypeSignatures #-}@@ -8,9 +9,11 @@ {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE UndecidableInstances #-}-module Control.Monad.State.LGBT( LGLT+module Control.Monad.State.LGBT( LGBT+ , LGLT , LGCT , MonadLGBT (..)+ , runLGBT , runLGLT , runLGCT , withGlobal, withLocal@@ -19,12 +22,47 @@ import Control.Applicative import Control.Monad.Cont---import Control.Monad.Except import Control.Monad.Logic---import Control.Monad.Trans(lift) --import Control.Monad.Reader import Control.Monad.State.Strict+import Control.Monad.Backtrack +-- | LGBT monad is sandwiching state with eager @BacktrackT@ transformer.+newtype LGBT localState globalState m a = LGBT { _unLGBT :: forall result.+ StateT localState+ (BacktrackT (Either String (result, localState))+ (StateT globalState m)) a }++instance Functor (LGBT localState globalState m) where+ fmap f (LGBT act) = LGBT $ fmap f act++instance Applicative (LGBT localState globalState m) where+ LGBT f <*> LGBT a = LGBT $ f <*> a+ pure v = LGBT $ pure v++instance Applicative m+ => Alternative (LGBT localState globalState m) where+ empty = LGBT empty+ LGBT a <|> LGBT b = LGBT $ a <|> b+ some (LGBT act) = LGBT $ some act+ many (LGBT act) = LGBT $ many act++instance Monad m+ => Monad (LGBT localState globalState m) where+ return v = LGBT $ return v+ LGBT a >>= b = LGBT $ a >>= (_unLGBT . b)+ fail s = LGBT $ fail s++instance MonadPlus m+ => MonadPlus (LGBT localState globalState m) where+ mzero = LGBT mzero+ LGBT a `mplus` LGBT b = LGBT $ a `mplus` b++instance MonadIO m+ => MonadIO (LGBT localState globalState m) where+ liftIO act = LGBT $ liftIO act++-- | LGBT monad is sandwiching state with fair @LogicT@ transformer. newtype LGLT localState globalState m a = LGLT { _unLGLT :: StateT localState (LogicT (StateT globalState m)) a }@@ -51,6 +89,9 @@ StateT localState (ContT (result, localState) (StateT globalState m)) a } deriving (Functor, Applicative, Monad, MonadIO, MonadCont) +instance MonadTrans (LGBT localState globalState) where+ lift act = LGBT $ lift $ lift $ lift act+ instance MonadTrans (LGLT localState globalState) where lift = LGLT . lift . lift . lift @@ -90,6 +131,16 @@ getsGlobal f = f <$> getGlobal instance Monad m+ => MonadLGBT (LGBT localState globalState m)+ localState globalState where+ getLocal = LGBT get+ getGlobal = LGBT $ lift $ lift get+ putLocal l = LGBT $ put l+ putGlobal g = LGBT $ lift . lift $ put g+ modifyLocal m = LGBT $ modify m+ modifyGlobal m = LGBT $ lift . lift $ modify m++instance Monad m => MonadLGBT (LGLT localState globalState m) localState globalState where getLocal = LGLT get@@ -99,24 +150,49 @@ modifyLocal = LGLT . modify modifyGlobal = LGLT . lift . lift . modify --- * These are not instance methods, since liftings need to be explicitly determined.-withLocal :: Monad m- => (localState -> m localState)- -> LGLT localState globalState m ()+-- * These are not instance methods, since choice of transformer needs+-- to be explicitly determined.+withLocal :: forall (t :: (* -> *) -> * -> *) -- ^ Any transformer that makes MonadLGBT+ (m :: * -> *) -- ^ Any underlying monad, below the @t@+ localState+ globalState.+ (Monad m,+ MonadTrans t ,+ MonadLGBT (t m) localState globalState)+ => (localState -> m localState )+ -> t m () withLocal f = getLocal >>= (lift . f) >>= putLocal -withGlobal :: Monad m- => (globalState -> m globalState)- -> LGLT globalState globalState m ()+withGlobal :: forall (t :: (* -> *) -> * -> *) -- ^ Any transformer that makes MonadLGBT+ (m :: * -> * ) -- ^ Any underlying monad, below the @t@+ localState+ globalState.+ (Monad m,+ MonadTrans t ,+ MonadLGBT (t m) localState globalState)+ => (globalState -> m globalState)+ -> t m () withGlobal f = getGlobal >>= (lift . f) >>= putGlobal -runLGLT :: forall m localState globalState success result.+runLGBT :: forall m localState globalState result. Monad m- => LGLT localState globalState m success- -> localState- -> globalState- -> (success -> localState -> globalState -> m result -> m result)- -> ( globalState -> m result)+ => LGBT localState globalState m result+ -> localState+ -> globalState+ -> m (Either String (result, localState), globalState)+runLGBT (LGBT act) localState globalState =+ runStateT (runBacktrackT (runStateT act localState) onFailure onSuccess) globalState+ where+ onFailure = pure . Left+ onSuccess = pure . Right++runLGLT :: forall m result success localState globalState.+ Monad m+ => LGLT localState globalState m success+ -> localState+ -> globalState+ -> (success -> localState -> globalState -> m result -> m result)+ -> ( globalState -> m result) -> m result runLGLT (LGLT act) localState globalState onSuccess onFailure = evalStateT (runLogicT (runStateT act localState) onSuccess' onFailure') globalState
test/Main.hs view
@@ -6,7 +6,9 @@ {-# LANGUAGE NamedFieldPuns #-} module Main(main) where +import Control.Applicative import Control.Monad.Identity+import Data.Foldable(asum) import Data.Tree import Control.Monad.State.LGBT@@ -20,7 +22,7 @@ -- | We define type Path = [String] -type MeerkatM a = LGLT Local Global Identity a+type MeerkatM a = LGBT Local Global Identity a -- | First maze is relatively easy, but since Merryssa is very liberal, -- she will probably choose the leftist path until proven that it goes nowhere.@@ -54,7 +56,7 @@ withRaisins :: (Raisins -> Raisins) -> Global -> Global withRaisins f (Global x) = Global $ f x -type MazeM = LGLT Local Global Identity [String]+type MazeM = LGBT Local Global Identity [String] -- | Merryssa the Meerkat tries to find her way inside the forest maze... -- but she has limited amount of food.@@ -63,10 +65,10 @@ current <- getsLocal $ rootLabel . subMaze modifyLocal $ \Local{..} -> Local { path=current:path, .. } remainingFood <- getsGlobal food- when (remainingFood == 0) mzero+ when (remainingFood == 0) $ fail "No more raisins!" if current == "FINISH"- then reverse <$> getsLocal path -- return the path to finish- else (msum . map stepTo) =<< getsLocal (subForest . subMaze)+ then reverse <$> getsLocal path -- ^ Return the path to finish+ else (asum . map stepTo) =<< getsLocal (subForest . subMaze) where stepTo :: Maze -> MazeM stepTo new = do@@ -88,14 +90,14 @@ experiment :: Int -> Maze -> Result experiment givenFood theMaze =+ extract $ runIdentity $- runLGLT meerkat (Local { subMaze = theMaze, path = [] })- (Global { food = givenFood })- onSuccess onFailure+ runLGBT meerkat Local { subMaze = theMaze, path = [] }+ Global { food = givenFood } where- onFailure Global { food=0 } = return Bored- onFailure Global { food } = return $ Asleep food- onSuccess path _ Global { food } _next = return $ Escaped food path+ extract (Left _ , Global { food=0 }) = Bored+ extract (Left _ , Global { food }) = Asleep food+ extract (Right (path, _), Global { food }) = Escaped food path test :: Int -> Maze -> IO () test someFood aMaze = do