{-# LANGUAGE RankNTypes #-} {-# LANGUAGE BlockArguments #-} {-# LANGUAGE DeriveFunctor #-} {- | Module: Control.Monad.Commander Description: A monad for stateful, backtracking computations Copyright: (c) Samuel Schlesinger 2020 License: MIT Maintainer: sgschlesinger@gmail.com Stability: experimental Portability: POSIX, Windows -} module Control.Monad.Commander ( -- ** The CommanderT Monad {- | The 'CommanderT' monad is stateful and has the ability to backtrack. -} CommanderT(Action, Defeat, Victory), runCommanderT, hoistToFunctor, hoistFromFunctor, ) where import Control.Arrow (first) import Control.Monad (ap, MonadPlus) import Control.Monad.Trans (MonadTrans, lift, liftIO, MonadIO) import Control.Applicative (Alternative(empty, (<|>))) -- | A 'CommanderT' action is a metaphor for a military commander. At each -- step, we have a new 'Action' to take, or we could have experienced -- 'Defeat', or we can see 'Victory'. While a real life commander -- worries about moving his troops around in order to achieve a victory in -- battle, a 'CommanderT' worries about iteratively transforming a state -- to find some value. -- -- In more practical terms, a term of type 'CommanderT' can be thought of -- as a backtracking, stateful computation which can either result in -- a result being produced, or nothing being produced. It is a -- 'Monad' for any base 'Functor' you want to use as the effect inside of -- the stateful computation, similarly to the free monad. data CommanderT state f a = Action (state -> f (CommanderT state f a, state)) | Defeat | Victory a deriving Functor -- | We can run a 'CommanderT' on some state and see if it has -- a successful campaign. runCommanderT :: Monad m => CommanderT state m a -> state -> m (Maybe a) runCommanderT (Action action) state = do (action', state') <- action state runCommanderT action' state' runCommanderT Defeat _ = return Nothing runCommanderT (Victory a) _ = return (Just a) -- | We can go from a non-'Functor' to a 'Functor' inside of a 'CommanderT' -- action. This does the transformation "top to bottom", as opposed to -- 'hoistFromFunctor', which does it "bottom to top". If your natural -- transformation is lessening, i.e. it trims branching structure, then you -- probably want to use this function. hoistToFunctor :: Functor g => (forall a. f a -> g a) -> CommanderT state f a -> CommanderT state g a hoistToFunctor phi (Action action) = Action (fmap (fmap (first (hoistToFunctor phi))) $ fmap phi action) -- | We can go from a 'Functor' to a non-'Functor' inside of a 'CommanderT' -- action. This does the transformation "bottom to top", as opposed to -- 'hoistToFunctor', which does it "top to bottom". If your natural -- transformation is increasing, i.e. it adds branching structure, then you -- probably want to use this function. hoistFromFunctor :: Functor f => (forall a. f a -> g a) -> CommanderT state f a -> CommanderT state g a hoistFromFunctor phi (Action action) = Action (fmap phi $ fmap (fmap (first (hoistFromFunctor phi))) action) instance Functor f => Applicative (CommanderT state f) where (<*>) = ap pure = Victory instance MonadTrans (CommanderT state) where lift ma = Action $ \state -> do a <- ma return (pure a, state) instance MonadIO m => MonadIO (CommanderT state m) where liftIO ma = Action $ \state -> do a <- liftIO ma return (pure a, state) instance Functor f => Monad (CommanderT state f) where Defeat >>= _ = Defeat Victory a >>= f = f a Action action >>= f = Action (fmap (\(a, s) -> (a >>= f, s)) . action) instance Functor f => Alternative (CommanderT state f) where empty = Defeat Defeat <|> a = a v@(Victory _) <|> _ = v Action action <|> p = Action (fmap (\(a, s) -> (a <|> p, s)) . action)