documentation for Control.Monad.State
Isaac Jones
ijones@syntaxpolice.org
11 Mar 2003 10:19:36 -0500
--=-=-=
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Here's a patch which adds some Haddock documentation to the
Control.Monad.State module. I collected examples together that I had
found useful when trying to understand this module, and haddock-ized
some of the comments that were already in the code. I also got
permission from the original authors to include these examples.
The resulting haddock output can be seen here:
http://www.syntaxpolice.org/~ijones/tmp/Control.Monad.State.html
Any comments before I commit this patch are welcome.
peace,
isaac
--=-=-=
Content-Type: application/octet-stream
Content-Disposition: attachment; filename=patch
Index: State.hs
===================================================================
RCS file: /home/cvs/root/fptools/libraries/base/Control/Monad/State.hs,v
retrieving revision 1.6
diff -u -r1.6 State.hs
--- State.hs 3 Oct 2002 13:41:35 -0000 1.6
+++ State.hs 7 Mar 2003 15:48:17 -0000
@@ -11,11 +11,16 @@
--
-- State monads.
--
--- Inspired by the paper
+-- This module is inspired by the paper
-- /Functional Programming with Overloading and
-- Higher-Order Polymorphism/,
-- Mark P Jones (<http://www.cse.ogi.edu/~mpj>)
-- Advanced School of Functional Programming, 1995.
+--
+-- See below for examples.
+
+
+
-----------------------------------------------------------------------------
module Control.Monad.State (
@@ -48,42 +53,117 @@
-- ---------------------------------------------------------------------------
-- MonadState class
--
--- get: returns the state from the internals of the monad.
--- put: changes (replaces) the state inside the monad.
+
+-- | /get/ returns the state from the internals of the monad.
+--
+-- /put/ replaces the state inside the monad.
class (Monad m) => MonadState s m | m -> s where
get :: m s
put :: s -> m ()
--- Monadic state transformer.
+-- | Monadic state transformer.
--
-- Maps an old state to a new state inside a state monad.
--- The old state is thrown away.}
+-- The old state is thrown away.
--
--- Main> :t modify ((+1) :: Int -> Int)
--- modify (...) :: (MonadState Int a) => a ()
+-- > Main> :t modify ((+1) :: Int -> Int)
+-- > modify (...) :: (MonadState Int a) => a ()
--
--- This says that modify (+1) acts over any
--- Monad that is a member of the MonadState class,
--- with an Int state.
+-- This says that @modify (+1)@ acts over any
+-- Monad that is a member of the @MonadState@ class,
+-- with an @Int@ state.
modify :: (MonadState s m) => (s -> s) -> m ()
modify f = do
s <- get
put (f s)
--- Get part of the state
---
--- gets specific component of the state,
--- using a projection function supplied.
-
+-- | Gets specific component of the state, using a projection function
+-- supplied.
+
gets :: (MonadState s m) => (s -> a) -> m a
gets f = do
s <- get
return (f s)
-- ---------------------------------------------------------------------------
--- Our parameterizable state monad
+-- | A parameterizable state monad where /s/ is the type of the state
+-- to carry and /a/ is the type of the /return value/.
+--
+-- /Examples:/
+--
+-- A function to increment a counter. Taken from the paper
+-- /Generalising Monads to Arrows/, John
+-- Hughes (<http://www.math.chalmers.se/~rjmh/>), November 1998:
+--
+-- > tick :: State Int Int
+-- > tick = do n <- get
+-- > put (n+1)
+-- > return n
+--
+-- Add one to the given number using the state monad:
+--
+-- > plusOne :: Int -> Int
+-- > plusOne n = execState tick n
+--
+-- A contrived addition example. Works only with positive numbers:
+--
+-- > plus :: Int -> Int -> Int
+-- > plus n x = execState (sequence $ replicate n tick) x
+--
+-- An example from /The Craft of Functional Programming/, Simon
+-- Thompson (<http://www.cs.ukc.ac.uk/people/staff/sjt/>),
+-- Addison-wesley 1999: \"Given an arbitrary tree, transform it to a
+-- tree of integers in which the original elements are replaced by
+-- natural numbers, starting from 0. The same element has to be
+-- replaced by the same number at every occurrence, and when we meet
+-- an as-yet-unvisited element we have to find a 'new' number to match
+-- it with:\"
+--
+-- > data Tree a = Nil | Node a (Tree a) (Tree a) deriving (Show, Eq)
+-- > type Table a = [a]
+--
+-- > numberTree :: Eq a => Tree a -> State (Table a) (Tree Int)
+-- > numberTree Nil = return Nil
+-- > numberTree (Node x t1 t2)
+-- > = do num <- numberNode x
+-- > nt1 <- numberTree t1
+-- > nt2 <- numberTree t2
+-- > return (Node num nt1 nt2)
+-- > where
+-- > numberNode :: Eq a => a -> State (Table a) Int
+-- > numberNode x
+-- > = do table <- get
+-- > (newTable, newPos) <- return (nNode x table)
+-- > put newTable
+-- > return newPos
+-- > nNode:: (Eq a) => a -> Table a -> (Table a, Int)
+-- > nNode x table
+-- > = case (findIndexInList (== x) table) of
+-- > Nothing -> (table ++ [x], length table)
+-- > Just i -> (table, i)
+-- > findIndexInList :: (a -> Bool) -> [a] -> Maybe Int
+-- > findIndexInList = findIndexInListHelp 0
+-- > findIndexInListHelp _ _ [] = Nothing
+-- > findIndexInListHelp count f (h:t)
+-- > = if (f h)
+-- > then Just count
+-- > else findIndexInListHelp (count+1) f t
+--
+-- numTree applies numberTree with an initial state:
+--
+-- > numTree :: (Eq a) => Tree a -> Tree Int
+-- > numTree t = evalState (numberTree t) []
+--
+-- > testTree = Node "Zero" (Node "One" (Node "Two" Nil Nil) (Node "One" (Node "Zero" Nil Nil) Nil)) Nil
+-- > numTree testTree => Node 0 (Node 1 (Node 2 Nil Nil) (Node 1 (Node 0 Nil Nil) Nil)) Nil
+--
+-- sumTree is a little helper function that does not use the State monad:
+--
+-- > sumTree :: (Num a) => Tree a -> a
+-- > sumTree Nil = 0
+-- > sumTree (Node e t1 t2) = e + (sumTree t1) + (sumTree t2)
newtype State s a = State { runState :: s -> (a, s) }
@@ -107,47 +187,72 @@
get = State $ \s -> (s, s)
put s = State $ \_ -> ((), s)
-
-evalState :: State s a -> s -> a
+-- |Evaluate this state monad with the given initial state,throwing
+-- away the final state. Very much like @fst@ composed with
+-- @runstate@.
+
+evalState :: State s a -- ^The state to evaluate
+ -> s -- ^An initial value
+ -> a -- ^The return value of the state application
evalState m s = fst (runState m s)
-execState :: State s a -> s -> s
+-- |Execute this state and return the new state, throwing away the
+-- return value. Very much like @snd@ composed with
+-- @runstate@.
+
+execState :: State s a -- ^The state to evaluate
+ -> s -- ^An initial value
+ -> s -- ^The new state
execState m s = snd (runState m s)
+-- |Map a stateful computation from one (return value, state) pair to
+-- another. For instance, to convert numberTree from a function that
+-- returns a tree to a function that returns the sum of the numbered
+-- tree you may write:
+--
+-- > sumNumberedTree :: (Eq a) => Tree a -> State (Table a) Int
+-- > sumNumberedTree = mapState (\ (t, tab) -> (sumTree t, tab)) . numberTree
+
mapState :: ((a, s) -> (b, s)) -> State s a -> State s b
mapState f m = State $ f . runState m
+-- |Apply this function to this state and return the resulting state.
withState :: (s -> s) -> State s a -> State s a
withState f m = State $ runState m . f
--- ---------------------------------------------------------------------------
--- Our parameterizable state monad, with an inner monad
-newtype StateT s m a = StateT { runStateT :: s -> m (a,s) }
+-- ---------------------------------------------------------------------------
+-- A parameterizable state monad for encapsulating an inner monad.
---The StateT Monad structure is parameterized over two things:
+-- | The StateT Monad structure is parameterized over two things:
--
-- * s - The state.
+--
-- * m - The inner monad.
-
+--
-- Here are some examples of use:
-
+--
-- (Parser from ParseLib with Hugs)
--- type Parser a = StateT String [] a
--- ==> StateT (String -> [(a,String)])
--- For example, item can be written as:
--- item = do (x:xs) <- get
--- put xs
--- return x
-
--- type BoringState s a = StateT s Indentity a
--- ==> StateT (s -> Identity (a,s))
--
--- type StateWithIO s a = StateT s IO a
--- ==> StateT (s -> IO (a,s))
+-- > type Parser a = StateT String [] a
+-- > ==> StateT (String -> [(a,String)])
--
--- type StateWithErr s a = StateT s Maybe a
--- ==> StateT (s -> Maybe (a,s))
+-- For example, item can be written as:
+--
+-- > item = do (x:xs) <- get
+-- > put xs
+-- > return x
+-- >
+-- > type BoringState s a = StateT s Indentity a
+-- > ==> StateT (s -> Identity (a,s))
+-- >
+-- > type StateWithIO s a = StateT s IO a
+-- > ==> StateT (s -> IO (a,s))
+-- >
+-- > type StateWithErr s a = StateT s Maybe a
+-- > ==> StateT (s -> Maybe (a,s))
+
+newtype StateT s m a = StateT { runStateT :: s -> m (a,s) }
instance (Monad m) => Functor (StateT s m) where
fmap f m = StateT $ \s -> do
@@ -193,20 +298,23 @@
((a, f), s') <- runStateT m s
return ((a, s'), f)
-
+-- |Similar to 'evalState'
evalStateT :: (Monad m) => StateT s m a -> s -> m a
evalStateT m s = do
(a, _) <- runStateT m s
return a
+-- |Similar to 'execState'
execStateT :: (Monad m) => StateT s m a -> s -> m s
execStateT m s = do
(_, s') <- runStateT m s
return s'
+-- |Similar to 'mapState'
mapStateT :: (m (a, s) -> n (b, s)) -> StateT s m a -> StateT s n b
mapStateT f m = StateT $ f . runStateT m
+-- |Similar to 'withState'
withStateT :: (s -> s) -> StateT s m a -> StateT s m a
withStateT f m = StateT $ runStateT m . f
--=-=-=
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.1 (GNU/Linux)
Comment: Processed by Mailcrypt 3.5.8 <http://mailcrypt.sourceforge.net/>
iD8DBQE+bf6HBLPTZ7K4MaIRAlcAAJ0e6O8g0ioHHFS0BgkP41uXjF8m8QCgop4N
Vvrn8eliUnvyk2rOfUW0gwE=
=i5Tb
-----END PGP SIGNATURE-----
--=-=-=--