summaryrefslogtreecommitdiff
path: root/ghc/docs/simple-monad.lhs
blob: 3efb2b9bb0873f099d5a4f654ad8d12e1e6f0ea9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
A Simple Country Boy's Guide to Monadic-Style Programming
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Forget the category theory, forget all the fancy talk, forget "monadic
I/O", forget Phil Wadler's papers!  Let's just do a little *plumbing*
in the monadic style, in plain-vanilla Haskell.

You can compile this guide as a Haskell module; I haven't put in
enough code to make it run or do anytning interesting.  Excuse me for
a moment, while I get some preliminaries out of the way...
\begin{code}
module Foo where

infixr 9 `thenFoo`, `thenFoo_` -- ignore me

data Foo = NullFoo | ConsFoo Int Foo -- assorted types, of little interest
type SwitchChecker = String -> Bool
type EnvA = [(String, Float)]
type NameSupply = Int
\end{code}

*** MOTIVATION *********

If you find that your Haskell functions are starting to carry around a
lot of baggage ..., e.g.,
\begin{code}
f :: EnvA -> SwitchChecker -> NameSupply -> Foo -> (Int, NameSupply)

f env sw_chkr names NullFoo = (0, names)

f env sw_chkr names (ConsFoo x xs)
  = let
	(x', names')  = f env sw_chkr names  xs
    in
    (x + x', names')
{-
    `env' is some kind of environment;
	what most people call "lookup tables".
    `sw_chkr' is a function which, when presented with a
  	String, will tell you if that string was present
  	on the command line.
    `names' is some kind of "name supply"; `f'
    `f' returns a depleted name supply (2nd component of result).
-}
\end{code}

...then it may be time to use monadic code to hide some of the mess!!

GRATUITOUS PLUMBING OF STATE MUST DIE.


*** SETTING UP THE MONAD MACHINERY *******

First, divide the things to be plumbed into:

    * things that are only passed "downwards" through the function;
      in the example above, the `env' and `sw_chkr' are such things;

    * things that are "threaded" through the function; you want the
      changed whatsit back from "down below"; `names' is such a thing.

Now, implement your monad; let's call it `FooM'; think of a `FooM
Wibble' as an *action* that, when performed, produces a `Wibble'.

\begin{code}
type FooM a =  EnvA		-- type of lookup-tbl being plumbed
	    -> SwitchChecker	-- type of switch looker-upper...
	    -> NameSupply	-- NameSupply going down...
	    -> (a,		-- result of the `FooM a' action
		NameSupply)	-- NameSupply that comes back...
\end{code}

(Note: in reality, it would be good practice to hide all this stuff
behind a clean interface, in another module.)

Let's write the basic operations on these `FooM a' guys:

\begin{code}
returnFoo :: a -> FooM a
    -- make a `FooM thing' action from a `thing' value
    -- [Phil W would call this `unitFoo']

thenFoo :: FooM a -> (a -> FooM b) -> FooM b
    -- sequence two actions; the second uses the
    -- result of the first
    -- [Phil W would call this `bindFoo', or semi-colon :-]

thenFoo_ :: FooM a -> FooM b -> FooM b
    -- sequence two actions; don't care about the
    -- result of the first
    -- [the name is a mnemonic for "... thenFoo \ _ -> ...]
\end{code}

They're implemented in the obvious way:
\begin{code}
returnFoo thing env sw_chkr ns = (thing, ns)

thenFoo action1 action2 env sw_chkr ns
  = case (action1 env sw_chkr ns) of
      (result1, ns1) -> action2 result1 env sw_chkr ns1

thenFoo_ action1 action2 env sw_chkr ns
  = case (action1 env sw_chkr ns) of
      (_{-boring result-}, ns1) -> action2 env sw_chkr ns1
\end{code}

All those are "pure plumbing".  We need a few "monadic functions" that
do something useful.

For example, you need to be able to "do a `FooM' action" and get the
answer back (along with the depleted NameSupply); for that, use...
\begin{code}
initFoo :: FooM a -> SwitchChecker -> NameSupply -> (NameSupply, a)

initFoo action sw_chkr ns
  = case (action [] sw_chkr ns) of
      (result, new_ns) -> (new_ns, result)
	-- gratuitous order-swapping
\end{code}

You would then have a this-monad-specific set of functions to ``reach
down'' in the plumbing and use the env, switch-checker, etc., that are
being carried around.  Some examples might be:
\begin{code}
getNewName :: FooM Int

getNewName env sw_chkr ns = (ns, ns+1)

------------

ifSwitchSet :: String -> FooM a -> FooM a -> FooM a

ifSwitchSet sw_to_chk then_ else_ env sw_chkr ns
  = (if (sw_chkr sw_to_chk) then then_ else else_) env sw_chkr ns

------------

lookupInEnv :: String -> FooM Float

lookupInEnv key env sw_chkr ns
  = case [ v | (k, v) <- env, k == key ] of
      []      -> error "lookupInEnv: no match"
      (val:_) -> (val, ns)
\end{code}

*** USING THE MONAD MACHINERY *******

We now have everything needed to write beautiful (!) monadic code.  To
remind you of the basic "monadic" functions at our disposal:

\begin{verbatim}
returnFoo :: a -> FooM a
thenFoo :: FooM a -> (a -> FooM b) -> FooM b
thenFoo_ :: FooM a -> FooM b -> FooM b
initFoo :: FooM a -> SwitchChecker -> NameSupply -> (NameSupply, a)

getNewName :: FooM Int
ifSwitchSet :: String -> FooM a -> FooM a -> FooM a
lookupInEnv :: String -> FooM Float
\end{verbatim}

Before going on: there are a few plumbing things that aren't
essential, but tend to be useful.  They needn't be written at the
"bare-bones" level; they show the use of `returnFoo' and `thenFoo'.
\begin{code}
mapFoo :: (a -> FooM b) -> [a] -> FooM [b]

mapFoo f []     = returnFoo []
mapFoo f (x:xs)
  = f x         `thenFoo` \ r  ->
    mapFoo f xs `thenFoo` \ rs ->
    returnFoo (r:rs)

mapAndUnzipFoo  :: (a -> FooM (b,c))   -> [a] -> FooM ([b],[c])

mapAndUnzipFoo f [] = returnFoo ([],[])
mapAndUnzipFoo f (x:xs)
  = f x		    	`thenFoo` \ (r1,  r2)  ->
    mapAndUnzipFoo f xs	`thenFoo` \ (rs1, rs2) ->
    returnFoo (r1:rs1, r2:rs2)
\end{code}

You should read

    f x `thenFoo` \ r -> ...

as

    "do `f' with argument `x', giving result `r'".

If you wanted, you could do really horrible things with the C
pre-processor (GHC and HBC let you do this...):
\begin{verbatim}
#define RETN returnFoo
#define BIND {--}
#define _TO_ `thenFoo` \ {--}

mapFoo f [] = RETN []
mapFoo f (x:xs)
  = BIND (f x)         _TO_ r  ->
    BIND (mapFoo f xs) _TO_ rs ->
    RETN (r:rs)
\end{verbatim}

*** USING THE MONAD MACHINERY, FOR REAL *******

We can finally re-write our `f' function in a "monadic style" (except
I'll call it `g'), using the functions above.
\begin{code}
g :: Foo -> FooM Int
    -- `g' has the same arguments as `f' (really), but in a different
    -- order: just unravel the type synonyms

g NullFoo = returnFoo 0

g (ConsFoo x xs)
  = g xs    `thenFoo` \ x' ->
    returnFoo (x + x')
\end{code}

LOOK, MOM, NO GRATUITOUS PLUMBING OF STATE!

OK, `g' shows how much the monadic style tidies up the plumbing, but
it is really boring---it doesn't use any of the functions we defined
earlier.  Here's a function that does:
\begin{code}
h :: Int -> FooM Integer

h x
  = getNewName	`thenFoo_` -- ignore that one...
    getNewName	`thenFoo`  \ int_name ->

    mapAndUnzipFoo zwonk [int_name ..]
		`thenFoo` \ (some_nums, more_nums) ->

    ifSwitchSet "-beat-hbc" (
	returnFoo (toInteger (some_nums !! 6) + 42)

    ) {-else-} (
	lookupInEnv "-ghc-is-cool"  `thenFoo` \ ghc_float ->
	returnFoo (toInteger (truncate ghc_float))
    )
  where
    zwonk :: Int -> FooM (Int, Int)
    zwonk i = returnFoo (i, x*i)
\end{code}

*** CONCLUSION *******

Ordinary Haskell programming, but in a "monadic style", is a good way
to control the plumbing of state through your code.

I have left out lots and lots of Neat Things you can do with monads --
see the papers for more interesting stuff.  But 99% of the monadic
code you're likely to write or see will look like the stuff in here.

Comments, suggestions, etc., to me, please.

Will Partain
partain@dcs.gla.ac.uk

% compile with:
%   ghc -cpp <other-flags> Foo.lhs
%   hbc -M <other-flags> Foo.lhs