packages feed

multistate 0.1.1 → 0.1.2

raw patch · 4 files changed

+206/−23 lines, 4 files

Files

multistate.cabal view
@@ -1,5 +1,5 @@ Name:          multistate-Version:       0.1.1+Version:       0.1.2 Cabal-Version: >= 1.8 Build-Type:    Simple license:       BSD3@@ -15,17 +15,46 @@  Synopsis: like mtl's ReaderT/StateT, but more than one contained value/type. Description:+  .+  == Introduction+  .   When using multiple ReaderT's or StateT's in the same monad stack, it becomes   necessary to lift the operations in order to affect a specific transformer.   Using heterogenous lists (type level functions), a GADT and a corresponding   type class, this package provides transformers that remove that necessity:   MultiReaderT/MultiStateT can contain a heterogenous list of values.+  .   The type inferred for the getter/setter determines which value is   read/written.-  See the Example application for some very basic usage.+  .+  == Example+  .+  >              -- an IO action wrapped by a MultiState+  >              -- containing both a Char and a [Char].+  > examplePrint :: MultiStateT (Cons [Char] (Cons Char Null)) IO ()+  > examplePrint = do+  >   c  <- mGet             -- inferred to be :: m Char+  >   cs <- mGet             -- inferred to be :: m [Char]+  >   lift $ putStrLn (c:cs)+  .+  This computation can be executed in the following way:+  .+  > main = evalMultiStateT+  >      $ withMultiState 'H'+  >      $ withMultiState "ello, World!"+  >      $ examplePrint+  .+  ( you can find this example as an executable in the package. )+  .+  == Known Deficits+  .   This package currently lacks a complete set of "lifting instances", i.e.-  instance definitions for classes such as mtl's MonadReader "over" the newly-  introduced monad transformers. These "lifting instances" would be necessary+  instance definitions for classes such as mtl's MonadWriter "over" the newly+  introduced monad transformers, as in+  .+  > instance (MonadWriter w m) => MonadWriter w (MultiStateT c m) where ..+  .+  These "lifting instances" would be necessary   to achieve full compatability with existing transformers. Ping me if you   find anything specific missing. @@ -39,14 +68,14 @@  flag build-example   description: Build the MultiState-example example program-  default: False  +  default: False  library {   exposed-modules:+    Data.HList.HList     Control.Monad.MultiState     Control.Monad.MultiReader   other-modules:-    Data.HList.HList   build-depends:     base >=4 && <6,     mtl >=2 && <3,
src/Control/Monad/MultiReader.hs view
@@ -4,17 +4,23 @@ {-# LANGUAGE OverlappingInstances #-} {-# LANGUAGE UndecidableInstances #-} +-- | The multi-valued version of mtl's Reader / ReaderT+-- / MonadReader module Control.Monad.MultiReader-  ( MultiReaderT(..)+  ( -- * MultiReaderT+    MultiReaderT(..)   , MultiReaderTNull   , MultiReader+  -- * MonadMultiReader class   , MonadMultiReader(..)+  -- * functions   , mAskRaw   , withMultiReader   , withMultiReaders   , evalMultiReaderT   , evalMultiReaderTWithInitial   , mapMultiReaderT+  -- * re-exports   , Cons -- re-export that stuff to allow writing type signatures.   , Null ) where@@ -46,20 +52,48 @@   +-- | A Reader transformer monad patameterized by:+--   +-- * x - The list of types constituting the environment / input (to be read),+-- * m - The inner monad.+-- +-- 'MultiReaderT' corresponds to mtl's 'ReaderT', but can contain+-- a heterogenous list of types.+-- +-- This heterogenous list is represented using Types.Data.List, i.e:+-- +--   * @'Null'@ - The empty list,+--   * @'Cons' a b@ - A list where @/a/@ is an arbitrary type+--     and @/b/@ is the rest list.+-- +-- For example,+-- +-- > MultiReaderT (Cons Int (Cons Bool Null)) :: (* -> *) -> (* -> *)+-- +-- is a Reader wrapper containing the types [Int,Bool]. newtype MultiReaderT x m a = MultiReaderT {   runMultiReaderTRaw :: StateT (HList x) m a } +-- | A MultiReader transformer carrying an empty state. type MultiReaderTNull = MultiReaderT Null -type MultiReader x a = MultiReaderT x Identity a+-- | A reader monad parameterized by the list of types x of the environment+-- / input to carry.+--+-- Similar to @Reader r = ReaderT r Identity@+type MultiReader x = MultiReaderT x Identity  class ContainsType a c where   setHListElem :: a -> HList c -> HList c   getHListElem :: HList c -> a +-- | All methods must be defined.+--+-- The idea is: Any monad stack is instance of @MonadMultiReader a@, iff+-- the stack contains a @MultiReaderT x@ with /a/ element of /x/. class (Monad m) => MonadMultiReader a m where-  mAsk :: m a+  mAsk :: m a -- ^ Access to a specific type in the environment.  {- it might make seem straightforward to define the following class that@@ -111,6 +145,10 @@ instance MonadTrans (MultiReaderT x) where   lift = MultiReaderT . lift +-- | Adds an element to the environment, thereby transforming a MultiReaderT+-- carrying an environment with types /(x:xs)/ to a a MultiReaderT with /xs/.+--+-- Think "Execute this computation with this additional value as environment". withMultiReader :: Monad m                 => x                 -> MultiReaderT (Cons x xs) m a@@ -121,6 +159,14 @@   put s'   return a +-- | Adds a heterogenous list of elements to the environment, thereby+-- transforming a MultiReaderT carrying an environment with values+-- over types /xs++ys/ to a MultiReaderT over /ys/.+--+-- Similar to recursively adding single values with 'withMultiReader'.+--+-- Note that /ys/ can be Null; in that case the return value can be+-- evaluated further using 'evalMultiReaderT'. withMultiReaders :: Monad m                  => HList xs                  -> MultiReaderT (Append xs ys) m a@@ -136,18 +182,37 @@       => MonadMultiReader a (t m) where   mAsk = lift $ mAsk +-- | A raw extractor of the contained HList (i.e. the complete environment).+--+-- For a possible usecase, see 'withMultiReaders'. mAskRaw :: Monad m => MultiReaderT a m (HList a) mAskRaw = MultiReaderT get +-- | Evaluate a computation over an empty environment.+--+-- Because the environment is empty, it does not need to be provided.+--+-- If you want to evaluate a computation over any non-Null environment, either+-- use+-- +-- * 'evalMultiReaderTWithInitial'+-- * simplify the computation using 'withMultiReader' / 'withMultiReaders',+--   then use 'evalMultiReaderT' on the result. evalMultiReaderT :: Monad m => MultiReaderT Null m a -> m a evalMultiReaderT k = evalStateT (runMultiReaderTRaw k) TNull +-- | Evaluate a reader computation with the given environment. evalMultiReaderTWithInitial :: Monad m-                            => HList a-                            -> MultiReaderT a m b+                            => HList a            -- ^ The initial state+                            -> MultiReaderT a m b -- ^ The computation to evaluate                             -> m b evalMultiReaderTWithInitial c k = evalStateT (runMultiReaderTRaw k) c +-- | Map both the return value and the environment of a computation+-- using the given function.+--+-- Note that there is a difference to mtl's ReaderT,+-- where it is /not/ possible to modify the environment. mapMultiReaderT :: (m (a, HList w)                 -> m' (a', HList w))                 -> MultiReaderT w m a
src/Control/Monad/MultiState.hs view
@@ -4,17 +4,24 @@ {-# LANGUAGE OverlappingInstances #-} {-# LANGUAGE UndecidableInstances #-} +-- | The multi-valued version of mtl's State / StateT+-- / MonadState module Control.Monad.MultiState-  ( MultiStateT(..)+  (+  -- * MultiStateT+    MultiStateT(..)   , MultiStateTNull   , MultiState+  -- * MonadMultiState class   , MonadMultiState(..)+  -- * functions   , mGetRaw   , withMultiState   , withMultiStates   , evalMultiStateT   , evalMultiStateTWithInitial   , mapMultiStateT+  -- * re-exports   , Cons -- re-export that stuff to allow writing type signatures.   , Null ) where@@ -46,20 +53,49 @@   +-- | A State transformer monad patameterized by:+--   +-- * x - The list of types constituting the state,+-- * m - The inner monad.+-- +-- 'MultiStateT' corresponds to mtl's 'StateT', but can contain+-- a heterogenous list of types.+-- +-- This heterogenous list is represented using Types.Data.List, i.e:+-- +--   * @'Null'@ - The empty list,+--   * @'Cons' a b@ - A list where @/a/@ is an arbitrary type+--     and @/b/@ is the rest list.+-- +-- For example,+-- +-- > MultiStateT (Cons Int (Cons Bool Null)) :: (* -> *) -> (* -> *)+-- +-- is a State wrapper containing the types [Int,Bool]. newtype MultiStateT x m a = MultiStateT {   runMultiStateTRaw :: StateT (HList x) m a } +-- | A MultiState transformer carrying an empty state. type MultiStateTNull = MultiStateT Null -type MultiState x a = MultiStateT x Identity a+-- | A state monad parameterized by the list of types x of the state to carry.+--+-- Similar to @State s = StateT s Identity@+type MultiState x = MultiStateT x Identity  class ContainsType a c where   setHListElem :: a -> HList c -> HList c   getHListElem :: HList c -> a +-- | All methods must be defined.+--+-- The idea is: Any monad stack is instance of @MonadMultiState a@, iff+-- the stack contains a @MultiStateT x@ with /a/ element of /x/. class (Monad m) => MonadMultiState a m where+  -- | state set function for values of type @a@.   mSet :: a -> m ()+  -- | state get function for values of type @a@.   mGet :: m a  instance ContainsType a (Cons a xs) where@@ -84,20 +120,36 @@ instance MonadTrans (MultiStateT x) where   lift = MultiStateT . lift +-- | Adds an element to the state, thereby transforming a MultiStateT over+-- values with types /(x:xs)/ to a MultiStateT over /xs/.+--+-- Think "Execute this computation with this additional value as state". withMultiState :: Monad m-               => x-               -> MultiStateT (Cons x xs) m a-               -> MultiStateT xs m a+               => x                           -- ^ The value to add+               -> MultiStateT (Cons x xs) m a -- ^ The computation using the+                                              -- enlarged state+               -> MultiStateT xs m a          -- ^ An computation using the+                                              -- smaller state withMultiState x k = MultiStateT $ do   s <- get   (a, TCons _ s') <- lift $ runStateT (runMultiStateTRaw k) (TCons x s)   put s'   return a +-- | Adds a heterogenous list of elements to the state, thereby+-- transforming a MultiStateT over values with types /xs++ys/ to a MultiStateT+-- over /ys/.+--+-- Similar to recursively adding single values with 'withMultiState'.+--+-- Note that /ys/ can be Null; in that case the return value can be+-- evaluated further using 'evalMultiStateT'. withMultiStates :: Monad m-                => HList xs-                -> MultiStateT (Append xs ys) m a-                -> MultiStateT ys m a+                => HList xs                       -- ^ The list of values to add+                -> MultiStateT (Append xs ys) m a -- ^ The computation using the+                                                  --   enlarged state+                -> MultiStateT ys m a             -- ^ A computation using the+                                                  -- smaller state withMultiStates TNull = id withMultiStates (TCons x xs) = withMultiStates xs . withMultiState x @@ -111,18 +163,38 @@   mSet = lift . mSet   mGet = lift $ mGet +-- | Evaluate an empty state computation.+--+-- Because the state is empty, no initial state must be provided.+--+-- Currently it is not directly possible to extract the final state of a+-- computation (similar to @execStateT@ and @runStateT@ for mtl's StateT),+-- but you can use 'mGetRaw' if you need such functionality.+--+-- If you want to evaluate a computation over any non-Null state, either+-- use+-- +-- * 'evalMultiStateTWithInitial'+-- * simplify the computation using 'withMultiState' / 'withMultiStates',+--   then use 'evalMultiStateT' on the result. evalMultiStateT :: Monad m => MultiStateT Null m a -> m a evalMultiStateT k = evalStateT (runMultiStateTRaw k) TNull +-- | Evaluate a state computation with the given initial state. evalMultiStateTWithInitial :: Monad m-                           => HList a-                           -> MultiStateT a m b+                           => HList a           -- ^ The initial state+                           -> MultiStateT a m b -- ^ The computation to evaluate                            -> m b evalMultiStateTWithInitial c k = evalStateT (runMultiStateTRaw k) c +-- | A raw extractor of the contained HList (i.e. the complete state).+--+-- For a possible usecase, see 'withMultiStates'. mGetRaw :: Monad m => MultiStateT a m (HList a) mGetRaw = MultiStateT get +-- | Map both the return value and the state of a computation+-- using the given function. mapMultiStateT :: (m (a, HList w) -> m' (a', HList w))                -> MultiStateT w m  a                -> MultiStateT w m' a'
src/Data/HList/HList.hs view
@@ -1,8 +1,12 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE TypeFamilies #-} -module Data.HList.HList(-  HList(..)+-- | A GADT HList implementation+--+-- Probably exists somewhere else already, but why add a dependency+-- for something so simple.+module Data.HList.HList+  ( HList(..) ) where  @@ -10,6 +14,7 @@ import Prelude hiding (reverse)  import Types.Data.List ( Cons, Null )+import Data.Monoid   @@ -22,3 +27,15 @@  instance (Show a, Show (HList b)) => Show (HList (Cons a b)) where   show (TCons x y) = "(" ++ show x ++ "," ++ show y ++ ")"++instance Monoid (HList Null) where+  mempty = TNull+  mappend _ _ = TNull++instance (Monoid x, Monoid (HList xs))+      => Monoid (HList (Cons x xs))+  where+    mempty = TCons mempty mempty+    mappend (TCons x1 xs1) (TCons x2 xs2) =+      TCons (x1 `mappend` x2)+            (xs1 `mappend` xs2)