Mercurial > lasercutter
comparison src/clojure/contrib/test_contrib/monads/examples.clj @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
9:35cf337adfcf | 10:ef7dbbd6452c |
---|---|
1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
3 ;; | |
4 ;; Monad application examples | |
5 ;; | |
6 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
8 | |
9 (ns | |
10 #^{:author "Konrad Hinsen" | |
11 :skip-wiki true | |
12 :doc "Examples for using monads"} | |
13 clojure.contrib.monads.examples | |
14 (:use [clojure.contrib.monads | |
15 :only (domonad with-monad m-lift m-seq m-reduce m-when | |
16 sequence-m | |
17 maybe-m | |
18 state-m fetch-state set-state | |
19 writer-m write | |
20 cont-m run-cont call-cc | |
21 maybe-t)]) | |
22 (:require (clojure.contrib [accumulators :as accu]))) | |
23 | |
24 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
25 ;; | |
26 ;; Sequence manipulations with the sequence monad | |
27 ;; | |
28 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
29 | |
30 ; Note: in the Haskell world, this monad is called the list monad. | |
31 ; The Clojure equivalent to Haskell's lists are (possibly lazy) | |
32 ; sequences. This is why I call this monad "sequence". All sequences | |
33 ; created by sequence monad operations are lazy. | |
34 | |
35 ; Monad comprehensions in the sequence monad work exactly the same | |
36 ; as Clojure's 'for' construct, except that :while clauses are not | |
37 ; available. | |
38 (domonad sequence-m | |
39 [x (range 5) | |
40 y (range 3)] | |
41 (+ x y)) | |
42 | |
43 ; Inside a with-monad block, domonad is used without the monad name. | |
44 (with-monad sequence-m | |
45 (domonad | |
46 [x (range 5) | |
47 y (range 3)] | |
48 (+ x y))) | |
49 | |
50 ; Conditions are written with :when, as in Clojure's for form: | |
51 (domonad sequence-m | |
52 [x (range 5) | |
53 y (range (+ 1 x)) | |
54 :when (= (+ x y) 2)] | |
55 (list x y)) | |
56 | |
57 ; :let is also supported like in for: | |
58 (domonad sequence-m | |
59 [x (range 5) | |
60 y (range (+ 1 x)) | |
61 :let [sum (+ x y) | |
62 diff (- x y)] | |
63 :when (= sum 2)] | |
64 (list diff)) | |
65 | |
66 ; An example of a sequence function defined in terms of a lift operation. | |
67 (with-monad sequence-m | |
68 (defn pairs [xs] | |
69 ((m-lift 2 #(list %1 %2)) xs xs))) | |
70 | |
71 (pairs (range 5)) | |
72 | |
73 ; Another way to define pairs is through the m-seq operation. It takes | |
74 ; a sequence of monadic values and returns a monadic value containing | |
75 ; the sequence of the underlying values, obtained from chaining together | |
76 ; from left to right the monadic values in the sequence. | |
77 (with-monad sequence-m | |
78 (defn pairs [xs] | |
79 (m-seq (list xs xs)))) | |
80 | |
81 (pairs (range 5)) | |
82 | |
83 ; This definition suggests a generalization: | |
84 (with-monad sequence-m | |
85 (defn ntuples [n xs] | |
86 (m-seq (replicate n xs)))) | |
87 | |
88 (ntuples 2 (range 5)) | |
89 (ntuples 3 (range 5)) | |
90 | |
91 ; Lift operations can also be used inside a monad comprehension: | |
92 (domonad sequence-m | |
93 [x ((m-lift 1 (partial * 2)) (range 5)) | |
94 y (range 2)] | |
95 [x y]) | |
96 | |
97 ; The m-plus operation does concatenation in the sequence monad. | |
98 (domonad sequence-m | |
99 [x ((m-lift 2 +) (range 5) (range 3)) | |
100 y (m-plus (range 2) '(10 11))] | |
101 [x y]) | |
102 | |
103 | |
104 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
105 ;; | |
106 ;; Handling failures with the maybe monad | |
107 ;; | |
108 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
109 | |
110 ; Maybe monad versions of basic arithmetic | |
111 (with-monad maybe-m | |
112 (def m+ (m-lift 2 +)) | |
113 (def m- (m-lift 2 -)) | |
114 (def m* (m-lift 2 *))) | |
115 | |
116 ; Division is special for two reasons: we can't call it m/ because that's | |
117 ; not a legal Clojure symbol, and we want it to fail if a division by zero | |
118 ; is attempted. It is best defined by a monad comprehension with a | |
119 ; :when clause: | |
120 (defn safe-div [x y] | |
121 (domonad maybe-m | |
122 [a x | |
123 b y | |
124 :when (not (zero? b))] | |
125 (/ a b))) | |
126 | |
127 ; Now do some non-trivial computation with division | |
128 ; It fails for (1) x = 0, (2) y = 0 or (3) y = -x. | |
129 (with-monad maybe-m | |
130 (defn some-function [x y] | |
131 (let [one (m-result 1)] | |
132 (safe-div one (m+ (safe-div one (m-result x)) | |
133 (safe-div one (m-result y))))))) | |
134 | |
135 ; An example that doesn't fail: | |
136 (some-function 2 3) | |
137 ; And two that do fail, at different places: | |
138 (some-function 2 0) | |
139 (some-function 2 -2) | |
140 | |
141 ; In the maybe monad, m-plus selects the first monadic value that | |
142 ; holds a valid value. | |
143 (with-monad maybe-m | |
144 (m-plus (some-function 2 0) (some-function 2 -2) (some-function 2 3))) | |
145 | |
146 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
147 ;; | |
148 ;; Random numbers with the state monad | |
149 ;; | |
150 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
151 | |
152 ; A state monad item represents a computation that changes a state and | |
153 ; returns a value. Its structure is a function that takes a state argument | |
154 ; and returns a two-item list containing the value and the updated state. | |
155 ; It is important to realize that everything you put into a state monad | |
156 ; expression is a state monad item (thus a function), and everything you | |
157 ; get out as well. A state monad does not perform a calculation, it | |
158 ; constructs a function that does the computation when called. | |
159 | |
160 ; First, we define a simple random number generator with explicit state. | |
161 ; rng is a function of its state (an integer) that returns the | |
162 ; pseudo-random value derived from this state and the updated state | |
163 ; for the next iteration. This is exactly the structure of a state | |
164 ; monad item. | |
165 (defn rng [seed] | |
166 (let [m 259200 | |
167 value (/ (float seed) (float m)) | |
168 next (rem (+ 54773 (* 7141 seed)) m)] | |
169 [value next])) | |
170 | |
171 ; We define a convenience function that creates an infinite lazy seq | |
172 ; of values obtained from iteratively applying a state monad value. | |
173 (defn value-seq [f seed] | |
174 (lazy-seq | |
175 (let [[value next] (f seed)] | |
176 (cons value (value-seq f next))))) | |
177 | |
178 ; Next, we define basic statistics functions to check our random numbers | |
179 (defn sum [xs] (apply + xs)) | |
180 (defn mean [xs] (/ (sum xs) (count xs))) | |
181 (defn variance [xs] | |
182 (let [m (mean xs) | |
183 sq #(* % %)] | |
184 (mean (for [x xs] (sq (- x m)))))) | |
185 | |
186 ; rng implements a uniform distribution in the interval [0., 1.), so | |
187 ; ideally, the mean would be 1/2 (0.5) and the variance 1/12 (0.8333). | |
188 (mean (take 1000 (value-seq rng 1))) | |
189 (variance (take 1000 (value-seq rng 1))) | |
190 | |
191 ; We make use of the state monad to implement a simple (but often sufficient) | |
192 ; approximation to a Gaussian distribution: the sum of 12 random numbers | |
193 ; from rng's distribution, shifted by -6, has a distribution that is | |
194 ; approximately Gaussian with 0 mean and variance 1, by virtue of the central | |
195 ; limit theorem. | |
196 ; In the first version, we call rng 12 times explicitly and calculate the | |
197 ; shifted sum in a monad comprehension: | |
198 (def gaussian1 | |
199 (domonad state-m | |
200 [x1 rng | |
201 x2 rng | |
202 x3 rng | |
203 x4 rng | |
204 x5 rng | |
205 x6 rng | |
206 x7 rng | |
207 x8 rng | |
208 x9 rng | |
209 x10 rng | |
210 x11 rng | |
211 x12 rng] | |
212 (- (+ x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12) 6.))) | |
213 | |
214 ; Let's test it: | |
215 (mean (take 1000 (value-seq gaussian1 1))) | |
216 (variance (take 1000 (value-seq gaussian1 1))) | |
217 | |
218 ; Of course, we'd rather have a loop construct for creating the 12 | |
219 ; random numbers. This would be easy if we could define a summation | |
220 ; operation on random-number generators, which would then be used in | |
221 ; combination with reduce. The lift operation gives us exactly that. | |
222 ; More precisely, we need (m-lift 2 +), because we want both arguments | |
223 ; of + to be lifted to the state monad: | |
224 (def gaussian2 | |
225 (domonad state-m | |
226 [sum12 (reduce (m-lift 2 +) (replicate 12 rng))] | |
227 (- sum12 6.))) | |
228 | |
229 ; Such a reduction is often quite useful, so there's m-reduce predefined | |
230 ; to simplify it: | |
231 (def gaussian2 | |
232 (domonad state-m | |
233 [sum12 (m-reduce + (replicate 12 rng))] | |
234 (- sum12 6.))) | |
235 | |
236 ; The statistics should be strictly the same as above, as long as | |
237 ; we use the same seed: | |
238 (mean (take 1000 (value-seq gaussian2 1))) | |
239 (variance (take 1000 (value-seq gaussian2 1))) | |
240 | |
241 ; We can also do the subtraction of 6 in a lifted function, and get rid | |
242 ; of the monad comprehension altogether: | |
243 (with-monad state-m | |
244 (def gaussian3 | |
245 ((m-lift 1 #(- % 6.)) | |
246 (m-reduce + (replicate 12 rng))))) | |
247 | |
248 ; Again, the statistics are the same: | |
249 (mean (take 1000 (value-seq gaussian3 1))) | |
250 (variance (take 1000 (value-seq gaussian3 1))) | |
251 | |
252 ; For a random point in two dimensions, we'd like a random number generator | |
253 ; that yields a list of two random numbers. The m-seq operation can easily | |
254 ; provide it: | |
255 (with-monad state-m | |
256 (def rng2 (m-seq (list rng rng)))) | |
257 | |
258 ; Let's test it: | |
259 (rng2 1) | |
260 | |
261 ; fetch-state and get-state can be used to save the seed of the random | |
262 ; number generator and go back to that saved seed later on: | |
263 (def identical-random-seqs | |
264 (domonad state-m | |
265 [seed (fetch-state) | |
266 x1 rng | |
267 x2 rng | |
268 _ (set-state seed) | |
269 y1 rng | |
270 y2 rng] | |
271 (list [x1 x2] [y1 y2]))) | |
272 | |
273 (identical-random-seqs 1) | |
274 | |
275 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
276 ;; | |
277 ;; Logging with the writer monad | |
278 ;; | |
279 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
280 | |
281 ; A basic logging example | |
282 (domonad (writer-m accu/empty-string) | |
283 [x (m-result 1) | |
284 _ (write "first step\n") | |
285 y (m-result 2) | |
286 _ (write "second step\n")] | |
287 (+ x y)) | |
288 | |
289 ; For a more elaborate application, let's trace the recursive calls of | |
290 ; a naive implementation of a Fibonacci function. The starting point is: | |
291 (defn fib [n] | |
292 (if (< n 2) | |
293 n | |
294 (let [n1 (dec n) | |
295 n2 (dec n1)] | |
296 (+ (fib n1) (fib n2))))) | |
297 | |
298 ; First we rewrite it to make every computational step explicit | |
299 ; in a let expression: | |
300 (defn fib [n] | |
301 (if (< n 2) | |
302 n | |
303 (let [n1 (dec n) | |
304 n2 (dec n1) | |
305 f1 (fib n1) | |
306 f2 (fib n2)] | |
307 (+ f1 f2)))) | |
308 | |
309 ; Next, we replace the let by a domonad in a writer monad that uses a | |
310 ; vector accumulator. We can then place calls to write in between the | |
311 ; steps, and obtain as a result both the return value of the function | |
312 ; and the accumulated trace values. | |
313 (with-monad (writer-m accu/empty-vector) | |
314 | |
315 (defn fib-trace [n] | |
316 (if (< n 2) | |
317 (m-result n) | |
318 (domonad | |
319 [n1 (m-result (dec n)) | |
320 n2 (m-result (dec n1)) | |
321 f1 (fib-trace n1) | |
322 _ (write [n1 f1]) | |
323 f2 (fib-trace n2) | |
324 _ (write [n2 f2]) | |
325 ] | |
326 (+ f1 f2)))) | |
327 | |
328 ) | |
329 | |
330 (fib-trace 5) | |
331 | |
332 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
333 ;; | |
334 ;; Sequences with undefined value: the maybe-t monad transformer | |
335 ;; | |
336 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
337 | |
338 ; A monad transformer is a function that takes a monad argument and | |
339 ; returns a monad as its result. The resulting monad adds some | |
340 ; specific behaviour aspect to the input monad. | |
341 | |
342 ; The simplest monad transformer is maybe-t. It adds the functionality | |
343 ; of the maybe monad (handling failures or undefined values) to any other | |
344 ; monad. We illustrate this by applying maybe-t to the sequence monad. | |
345 ; The result is an enhanced sequence monad in which undefined values | |
346 ; (represented by nil) are not subjected to any transformation, but | |
347 ; lead immediately to a nil result in the output. | |
348 | |
349 ; First we define the combined monad: | |
350 (def seq-maybe-m (maybe-t sequence-m)) | |
351 | |
352 ; As a first illustration, we create a range of integers and replace | |
353 ; all even values by nil, using a simple when expression. We use this | |
354 ; sequence in a monad comprehension that yields (inc x). The result | |
355 ; is a sequence in which inc has been applied to all non-nil values, | |
356 ; whereas the nil values appear unmodified in the output: | |
357 (domonad seq-maybe-m | |
358 [x (for [n (range 10)] (when (odd? n) n))] | |
359 (inc x)) | |
360 | |
361 ; Next we repeat the definition of the function pairs (see above), but | |
362 ; using the seq-maybe monad: | |
363 (with-monad seq-maybe-m | |
364 (defn pairs-maybe [xs] | |
365 (m-seq (list xs xs)))) | |
366 | |
367 ; Applying this to a sequence containing nils yields the pairs of all | |
368 ; non-nil values interspersed with nils that result from any combination | |
369 ; in which one or both of the values is nil: | |
370 (pairs-maybe (for [n (range 5)] (when (odd? n) n))) | |
371 | |
372 ; It is important to realize that undefined values (nil) are not eliminated | |
373 ; from the iterations. They are simply not passed on to any operations. | |
374 ; The outcome of any function applied to arguments of which at least one | |
375 ; is nil is supposed to be nil as well, and the function is never called. | |
376 | |
377 | |
378 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
379 ;; | |
380 ;; Continuation-passing style in the cont monad | |
381 ;; | |
382 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
383 | |
384 ; A simple computation performed in continuation-passing style. | |
385 ; (m-result 1) returns a function that, when called with a single | |
386 ; argument f, calls (f 1). The result of the domonad-computation is | |
387 ; a function that behaves in the same way, passing 3 to its function | |
388 ; argument. run-cont executes a continuation by calling it on identity. | |
389 (run-cont | |
390 (domonad cont-m | |
391 [x (m-result 1) | |
392 y (m-result 2)] | |
393 (+ x y))) | |
394 | |
395 ; Let's capture a continuation using call-cc. We store it in a global | |
396 ; variable so that we can do with it whatever we want. The computation | |
397 ; is the same one as in the first example, but it has the side effect | |
398 ; of storing the continuation at (m-result 2). | |
399 (def continuation nil) | |
400 | |
401 (run-cont | |
402 (domonad cont-m | |
403 [x (m-result 1) | |
404 y (call-cc (fn [c] (def continuation c) (c 2)))] | |
405 (+ x y))) | |
406 | |
407 ; Now we can call the continuation with whatever argument we want. The | |
408 ; supplied argument takes the place of 2 in the above computation: | |
409 (run-cont (continuation 5)) | |
410 (run-cont (continuation 42)) | |
411 (run-cont (continuation -1)) | |
412 | |
413 ; Next, a function that illustrates how a captured continuation can be | |
414 ; used as an "emergency exit" out of a computation: | |
415 (defn sqrt-as-str [x] | |
416 (call-cc | |
417 (fn [k] | |
418 (domonad cont-m | |
419 [_ (m-when (< x 0) (k (str "negative argument " x)))] | |
420 (str (. Math sqrt x)))))) | |
421 | |
422 (run-cont (sqrt-as-str 2)) | |
423 (run-cont (sqrt-as-str -2)) | |
424 | |
425 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |