Skip to content

Commit 53f34f8

Browse files
committed
Add laws to MonadState and MonadReader
1 parent 6eee658 commit 53f34f8

File tree

2 files changed

+74
-7
lines changed

2 files changed

+74
-7
lines changed

Control/Monad/Reader/Class.hs

+47-2
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,59 @@ import Data.Monoid
6969
-- class MonadReader
7070
-- asks for the internal (non-mutable) state.
7171

72-
-- | See examples in "Control.Monad.Reader".
72+
-- | Monads with a notion of readable environment.
73+
--
74+
-- See examples in "Control.Monad.Reader".
7375
-- Note, the partially applied function type @(->) r@ is a simple reader monad.
7476
-- See the @instance@ declaration below.
77+
--
78+
-- === Laws
79+
--
80+
-- 'ask' has no side effects, and produces the same result at any time.
81+
--
82+
-- @
83+
-- 'ask' '>>' m = m
84+
-- 'ask' '>>=' \\s1 -> 'ask' '>>=' \\s2 -> k s1 s2 = 'ask' '>>=' \\s -> k s s
85+
--
86+
-- m '<*>' 'ask' = 'ask' 'Control.Applicative.<**>' m
87+
-- @
88+
--
89+
-- @'local' f@ applies @f@ to the environment produced by 'ask'.
90+
--
91+
-- @
92+
-- 'local' f 'ask' = f '<$>' 'ask'
93+
-- 'local' f u = 'ask' '>>=' \\s -> 'local' (\\_ -> f s) u
94+
-- @
95+
--
96+
-- 'local' is a monoid morphism from @(r -> r)@ to (reversed) @(m a -> m a)@
97+
-- (i.e., @('Data.Monoid.Endo' r -> 'Data.Monoid.Dual' ('Data.Monoid.Endo' (m a)))@).
98+
--
99+
-- @
100+
-- 'local' 'id' = 'id'
101+
-- 'local' g '.' 'local' f = 'local' (f '.' g)
102+
-- @
103+
--
104+
-- 'local' is a monad morphism from 'm' to 'm'.
105+
--
106+
-- @
107+
-- 'local' f ('pure' x) = 'pure' x
108+
-- 'local' f (a '>>=' k) = 'local' f a '>>=' \\x -> 'local' f (k x)
109+
-- @
110+
--
111+
-- 'reader' must be equivalent to its default definition in terms of 'ask',
112+
-- and conversely.
113+
--
114+
-- Under that last condition, a property which is equivalent to the first two
115+
-- laws is that 'reader' must be a monad morphism from @'Reader' r@ to 'm'.
116+
--
117+
-- Another property equivalent to the first three laws is that there
118+
-- is a monad morphism @phi :: forall a. 'ReaderT' r m a -> m a@ such that
119+
-- @phi 'ask' = 'ask'@ and @phi . 'lift' = 'id'@.
75120
class Monad m => MonadReader r m | m -> r where
76121
#if __GLASGOW_HASKELL__ >= 707
77122
{-# MINIMAL (ask | reader), local #-}
78123
#endif
79-
-- | Retrieves the monad environment.
124+
-- | Retrieves the environment.
80125
ask :: m r
81126
ask = reader id
82127

Control/Monad/State/Class.hs

+27-5
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
-- Stability : experimental
1717
-- Portability : non-portable (multi-param classes, functional dependencies)
1818
--
19-
-- MonadState class.
19+
-- 'MonadState' class.
2020
--
2121
-- This module is inspired by the paper
2222
-- /Functional Programming with Overloading and Higher-Order Polymorphism/,
@@ -52,17 +52,39 @@ import Data.Monoid
5252

5353
-- ---------------------------------------------------------------------------
5454

55-
-- | Minimal definition is either both of @get@ and @put@ or just @state@
55+
-- | Monads with a notion of global state.
56+
--
57+
-- === Laws
58+
--
59+
-- @
60+
-- 'get' '>>=' 'put' = 'pure' ()
61+
-- 'put' s '>>' 'get' = 'put' s '>>' 'pure' s
62+
-- 'put' s1 '>>' 'put' s2 = 'put' s2
63+
-- @
64+
--
65+
-- Those three laws imply the following equations expressing that
66+
-- 'get' has no side effects:
67+
--
68+
-- @
69+
-- 'get' '>>' m = m
70+
-- 'get' '>>=' \\s1 -> 'get' '>>=' \\s2 -> k s1 s2 = 'get' '>>=' \\s -> k s s
71+
-- @
72+
--
73+
-- 'state' must be equivalent to its default definition in terms of 'get'
74+
-- and 'put', and conversely.
75+
-- Under that last condition, a property which is equivalent to the laws above
76+
-- is that 'state' must be a monad morphism, from
77+
-- @'Control.Monad.State.State' s@ to @m@.
5678
class Monad m => MonadState s m | m -> s where
57-
-- | Return the state from the internals of the monad.
79+
-- | Return the state.
5880
get :: m s
5981
get = state (\s -> (s, s))
6082

61-
-- | Replace the state inside the monad.
83+
-- | Replace the state.
6284
put :: s -> m ()
6385
put s = state (\_ -> ((), s))
6486

65-
-- | Embed a simple state action into the monad.
87+
-- | Embed a simple state action.
6688
state :: (s -> (a, s)) -> m a
6789
state f = do
6890
s <- get

0 commit comments

Comments
 (0)