Ticket #1410: Reader.hs

File Reader.hs, 8.2 KB (added by guest, 7 years ago)

Control.Monad.Reader source

Line 
1{-# OPTIONS -fallow-undecidable-instances #-}
2{- |
3Module      :  Control.Monad.Reader
4Copyright   :  (c) Andy Gill 2001,
5               (c) Oregon Graduate Institute of Science and Technology 2001,
6               (c) Jeff Newbern 2003-2007,
7               (c) Andriy Palamarchuk 2007
8License     :  BSD-style (see the file libraries/base/LICENSE)
9
10Maintainer  :  libraries@haskell.org
11Stability   :  experimental
12Portability :  non-portable (multi-param classes, functional dependencies)
13
14[Computation type:] Computations which read values from a shared environment.
15
16[Binding strategy:] Monad values are functions from the environment to a value.
17The bound function is applied to the bound value, and both have access
18to the shared environment.
19
20[Useful for:] Maintaining variable bindings, or other shared environment.
21
22[Zero and plus:] None.
23
24[Example type:] @'Reader' [(String,Value)] a@
25
26The 'Reader' monad (also called the Environment monad).
27Represents a computation, which can read values from
28a shared environment, pass values from function to function,
29and execute sub-computations in a modified environment.
30Using 'Reader' monad for such computations is often clearer and easier
31than using the 'Control.Monad.State.State' monad.
32
33  Inspired by the paper
34  /Functional Programming with Overloading and
35      Higher-Order Polymorphism/,
36    Mark P Jones (<http://www.cse.ogi.edu/~mpj/>)
37    Advanced School of Functional Programming, 1995.
38-}
39
40module Control.Monad.Reader (
41    module Control.Monad.Reader.Class,
42    Reader(..),
43    mapReader,
44    withReader,
45    ReaderT(..),
46    mapReaderT,
47    withReaderT,
48    module Control.Monad,
49    module Control.Monad.Fix,
50    module Control.Monad.Trans,
51    -- * Example 1: Simple Reader Usage
52    -- $simpleReaderExample
53
54    -- * Example 2: Modifying Reader Content With @local@
55    -- $localExample
56
57    -- * Example 3: @ReaderT@ Monad Transformer
58    -- $ReaderTExample
59    ) where
60
61import Control.Monad
62import Control.Monad.Cont.Class
63import Control.Monad.Error.Class
64import Control.Monad.Fix
65import Control.Monad.Instances ()
66import Control.Monad.Reader.Class
67import Control.Monad.State.Class
68import Control.Monad.Trans
69import Control.Monad.Writer.Class
70
71-- ----------------------------------------------------------------------------
72-- The partially applied function type is a simple reader monad
73
74instance MonadReader r ((->) r) where
75    ask       = id
76    local f m = m . f
77
78{- |
79The parameterizable reader monad.
80
81The @return@ function creates a @Reader@ that ignores the environment,
82and produces the given value.
83
84The binding operator @>>=@ produces a @Reader@ that uses the environment
85to extract the value its left-hand side,
86and then applies the bound function to that value in the same environment.
87-}
88newtype Reader r a = Reader {
89    {- |
90    Runs @Reader@ and extracts the final value from it.
91    To extract the value apply @(runReader reader)@ to an environment value. 
92    Parameters:
93
94    * A @Reader@ to run.
95
96    * An initial environment.
97    -}
98runReader :: r -> a
99}
100
101mapReader :: (a -> b) -> Reader r a -> Reader r b
102mapReader f m = Reader $ f . runReader m
103
104-- | A more general version of 'local'.
105
106withReader :: (r' -> r) -> Reader r a -> Reader r' a
107withReader f m = Reader $ runReader m . f
108
109instance Functor (Reader r) where
110    fmap f m = Reader $ \r -> f (runReader m r)
111
112instance Monad (Reader r) where
113    return a = Reader $ \_ -> a
114    m >>= k  = Reader $ \r -> runReader (k (runReader m r)) r
115
116instance MonadFix (Reader r) where
117    mfix f = Reader $ \r -> let a = runReader (f a) r in a
118
119instance MonadReader r (Reader r) where
120    ask       = Reader id
121    local f m = Reader $ runReader m . f
122
123{- |
124The reader monad transformer.
125Can be used to add environment reading functionality to other monads.
126-}
127newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a }
128
129mapReaderT :: (m a -> n b) -> ReaderT w m a -> ReaderT w n b
130mapReaderT f m = ReaderT $ f . runReaderT m
131
132withReaderT :: (r' -> r) -> ReaderT r m a -> ReaderT r' m a
133withReaderT f m = ReaderT $ runReaderT m . f
134
135instance (Monad m) => Functor (ReaderT r m) where
136    fmap f m = ReaderT $ \r -> do
137        a <- runReaderT m r
138        return (f a)
139
140instance (Monad m) => Monad (ReaderT r m) where
141    return a = ReaderT $ \_ -> return a
142    m >>= k  = ReaderT $ \r -> do
143        a <- runReaderT m r
144        runReaderT (k a) r
145    fail msg = ReaderT $ \_ -> fail msg
146
147instance (MonadPlus m) => MonadPlus (ReaderT r m) where
148    mzero       = ReaderT $ \_ -> mzero
149    m `mplus` n = ReaderT $ \r -> runReaderT m r `mplus` runReaderT n r
150
151instance (MonadFix m) => MonadFix (ReaderT r m) where
152    mfix f = ReaderT $ \r -> mfix $ \a -> runReaderT (f a) r
153
154instance (Monad m) => MonadReader r (ReaderT r m) where
155    ask       = ReaderT return
156    local f m = ReaderT $ \r -> runReaderT m (f r)
157
158-- ---------------------------------------------------------------------------
159-- Instances for other mtl transformers
160
161instance MonadTrans (ReaderT r) where
162    lift m = ReaderT $ \_ -> m
163
164instance (MonadIO m) => MonadIO (ReaderT r m) where
165    liftIO = lift . liftIO
166
167instance (MonadCont m) => MonadCont (ReaderT r m) where
168    callCC f = ReaderT $ \r ->
169        callCC $ \c ->
170        runReaderT (f (\a -> ReaderT $ \_ -> c a)) r
171
172instance (MonadError e m) => MonadError e (ReaderT r m) where
173    throwError       = lift . throwError
174    m `catchError` h = ReaderT $ \r -> runReaderT m r
175        `catchError` \e -> runReaderT (h e) r
176
177-- Needs -fallow-undecidable-instances
178instance (MonadState s m) => MonadState s (ReaderT r m) where
179    get = lift get
180    put = lift . put
181
182-- This instance needs -fallow-undecidable-instances, because
183-- it does not satisfy the coverage condition
184instance (MonadWriter w m) => MonadWriter w (ReaderT r m) where
185    tell     = lift . tell
186    listen m = ReaderT $ \w -> listen (runReaderT m w)
187    pass   m = ReaderT $ \w -> pass   (runReaderT m w)
188
189{- $simpleReaderExample
190
191In this example the @Reader@ monad provides access to variable bindings.
192Bindings are a 'Map' of integer variables.
193The variable @count@ contains number of variables in the bindings.
194You can see how to run a Reader monad and retrieve data from it
195with 'runReader', how to access the Reader data with 'ask' and 'asks'.
196
197> type Bindings = Map String Int;
198>
199>-- Returns True if the "count" variable contains correct bindings size.
200>isCountCorrect :: Bindings -> Bool
201>isCountCorrect bindings = runReader calc_isCountCorrect bindings
202>
203>-- The Reader monad, which implements this complicated check.
204>calc_isCountCorrect :: Reader Bindings Bool
205>calc_isCountCorrect = do
206>    count <- asks (lookupVar "count")
207>    bindings <- ask
208>    return (count == (Map.size bindings))
209>
210>-- The selector function to  use with 'asks'.
211>-- Returns value of the variable with specified name.
212>lookupVar :: String -> Bindings -> Int
213>lookupVar name bindings = fromJust (Map.lookup name bindings)
214>
215>sampleBindings = Map.fromList [("count",3), ("1",1), ("b",2)]
216>
217>main = do
218>    putStr $ "Count is correct for bindings " ++ (show sampleBindings) ++ ": ";
219>    putStrLn $ show (isCountCorrect sampleBindings);
220-}
221
222{- $localExample
223
224Shows how to modify Reader content with 'local'.
225
226>calculateContentLen :: Reader String Int
227>calculateContentLen = do
228>    content <- ask
229>    return (length content);
230>
231>-- Calls calculateContentLen after adding a prefix to the Reader content.
232>calculateModifiedContentLen :: Reader String Int
233>calculateModifiedContentLen = local ("Prefix " ++) calculateContentLen
234>
235>main = do
236>    let s = "12345";
237>    let modifiedLen = runReader calculateModifiedContentLen s
238>    let len = runReader calculateContentLen s
239>    putStrLn $ "Modified 's' length: " ++ (show modifiedLen)
240>    putStrLn $ "Original 's' length: " ++ (show len)
241-}
242
243{- $ReaderTExample
244
245Now you are thinking: 'Wow, what a great monad! I wish I could use
246Reader functionality in MyFavoriteComplexMonad!'. Don't worry.
247This can be easy done with the 'ReaderT' monad transformer.
248This example shows how to combine @ReaderT@ with the IO monad.
249
250>-- The Reader/IO combined monad, where Reader stores a string.
251>printReaderContent :: ReaderT String IO ()
252>printReaderContent = do
253>    content <- ask
254>    liftIO $ putStrLn ("The Reader Content: " ++ content)
255>
256>main = do
257>    runReaderT printReaderContent "Some Content"
258-}