view src/clojure/contrib/monads.clj @ 10:ef7dbbd6452c

added clojure source goodness
author Robert McIntyre <rlm@mit.edu>
date Sat, 21 Aug 2010 06:25:44 -0400
parents
children
line wrap: on
line source
1 ;; Monads in Clojure
3 ;; by Konrad Hinsen
4 ;; last updated June 30, 2009
6 ;; Copyright (c) Konrad Hinsen, 2009. All rights reserved. The use
7 ;; and distribution terms for this software are covered by the Eclipse
8 ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
9 ;; which can be found in the file epl-v10.html at the root of this
10 ;; distribution. By using this software in any fashion, you are
11 ;; agreeing to be bound by the terms of this license. You must not
12 ;; remove this notice, or any other, from this software.
14 (ns
15 ^{:author "Konrad Hinsen"
16 :see-also [["http://onclojure.com/2009/03/05/a-monad-tutorial-for-clojure-programmers-part-1/" "Monad tutorial part 1"]
17 ["http://onclojure.com/2009/03/06/a-monad-tutorial-for-clojure-programmers-part-2/" "Monad tutorial part 2"]
18 ["http://onclojure.com/2009/03/23/a-monad-tutorial-for-clojure-programmers-part-3/" "Monad tutorial part 3"]
19 ["http://onclojure.com/2009/04/24/a-monad-tutorial-for-clojure-programmers-part-4/" "Monad tutorial part 4"]
20 ["http://intensivesystems.net/tutorials/monads_101.html" "Monads in Clojure part 1"]
21 ["http://intensivesystems.net/tutorials/monads_201.html" "Monads in Clojure part 2"]]
22 :doc "This library contains the most commonly used monads as well
23 as macros for defining and using monads and useful monadic
24 functions."}
25 clojure.contrib.monads
26 (:require [clojure.contrib.accumulators])
27 (:use [clojure.contrib.macro-utils :only (with-symbol-macros defsymbolmacro)])
28 (:use [clojure.contrib.def :only (name-with-attributes)]))
30 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
31 ;;
32 ;; Defining monads
33 ;;
34 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
36 (defmacro monad
37 "Define a monad by defining the monad operations. The definitions
38 are written like bindings to the monad operations m-bind and
39 m-result (required) and m-zero and m-plus (optional)."
40 [operations]
41 `(let [~'m-bind ::undefined
42 ~'m-result ::undefined
43 ~'m-zero ::undefined
44 ~'m-plus ::undefined
45 ~@operations]
46 {:m-result ~'m-result
47 :m-bind ~'m-bind
48 :m-zero ~'m-zero
49 :m-plus ~'m-plus}))
51 (defmacro defmonad
52 "Define a named monad by defining the monad operations. The definitions
53 are written like bindings to the monad operations m-bind and
54 m-result (required) and m-zero and m-plus (optional)."
56 ([name doc-string operations]
57 (let [doc-name (with-meta name {:doc doc-string})]
58 `(defmonad ~doc-name ~operations)))
60 ([name operations]
61 `(def ~name (monad ~operations))))
64 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
65 ;;
66 ;; Using monads
67 ;;
68 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
70 (defn- add-monad-step
71 "Add a monad comprehension step before the already transformed
72 monad comprehension expression mexpr."
73 [mexpr step]
74 (let [[bform expr] step]
75 (cond (identical? bform :when) `(if ~expr ~mexpr ~'m-zero)
76 (identical? bform :let) `(let ~expr ~mexpr)
77 :else (list 'm-bind expr (list 'fn [bform] mexpr)))))
79 (defn- monad-expr
80 "Transforms a monad comprehension, consisting of a list of steps
81 and an expression defining the final value, into an expression
82 chaining together the steps using :bind and returning the final value
83 using :result. The steps are given as a vector of
84 binding-variable/monadic-expression pairs."
85 [steps expr]
86 (when (odd? (count steps))
87 (throw (Exception. "Odd number of elements in monad comprehension steps")))
88 (let [rsteps (reverse (partition 2 steps))
89 [lr ls] (first rsteps)]
90 (if (= lr expr)
91 ; Optimization: if the result expression is equal to the result
92 ; of the last computation step, we can eliminate an m-bind to
93 ; m-result.
94 (reduce add-monad-step
95 ls
96 (rest rsteps))
97 ; The general case.
98 (reduce add-monad-step
99 (list 'm-result expr)
100 rsteps))))
102 (defmacro with-monad
103 "Evaluates an expression after replacing the keywords defining the
104 monad operations by the functions associated with these keywords
105 in the monad definition given by name."
106 [monad & exprs]
107 `(let [name# ~monad
108 ~'m-bind (:m-bind name#)
109 ~'m-result (:m-result name#)
110 ~'m-zero (:m-zero name#)
111 ~'m-plus (:m-plus name#)]
112 (with-symbol-macros ~@exprs)))
114 (defmacro domonad
115 "Monad comprehension. Takes the name of a monad, a vector of steps
116 given as binding-form/monadic-expression pairs, and a result value
117 specified by expr. The monadic-expression terms can use the binding
118 variables of the previous steps.
119 If the monad contains a definition of m-zero, the step list can also
120 contain conditions of the form :when p, where the predicate p can
121 contain the binding variables from all previous steps.
122 A clause of the form :let [binding-form expr ...], where the bindings
123 are given as a vector as for the use in let, establishes additional
124 bindings that can be used in the following steps."
125 ([steps expr]
126 (monad-expr steps expr))
127 ([name steps expr]
128 (let [mexpr (monad-expr steps expr)]
129 `(with-monad ~name ~mexpr))))
131 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
132 ;;
133 ;; Defining functions used with monads
134 ;;
135 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
137 (defmacro defmonadfn
138 "Like defn, but for functions that use monad operations and are used inside
139 a with-monad block."
140 {:arglists '([name docstring? attr-map? args expr]
141 [name docstring? attr-map? (args expr) ...])}
142 [name & options]
143 (let [[name options] (name-with-attributes name options)
144 fn-name (symbol (str *ns*) (format "m+%s+m" (str name)))
145 make-fn-body (fn [args expr]
146 (list (vec (concat ['m-bind 'm-result
147 'm-zero 'm-plus] args))
148 (list `with-symbol-macros expr)))]
149 (if (list? (first options))
150 ; multiple arities
151 (let [arglists (map first options)
152 exprs (map second options)
153 ]
154 `(do
155 (defsymbolmacro ~name (partial ~fn-name ~'m-bind ~'m-result
156 ~'m-zero ~'m-plus))
157 (defn ~fn-name ~@(map make-fn-body arglists exprs))))
158 ; single arity
159 (let [[args expr] options]
160 `(do
161 (defsymbolmacro ~name (partial ~fn-name ~'m-bind ~'m-result
162 ~'m-zero ~'m-plus))
163 (defn ~fn-name ~@(make-fn-body args expr)))))))
166 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
167 ;;
168 ;; Commonly used monad functions
169 ;;
170 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
172 ; Define the four basic monad operations as symbol macros that
173 ; expand to their unqualified symbol equivalents. This makes it possible
174 ; to use them inside macro templates without having to quote them.
175 (defsymbolmacro m-result m-result)
176 (defsymbolmacro m-bind m-bind)
177 (defsymbolmacro m-zero m-zero)
178 (defsymbolmacro m-plus m-plus)
180 (defmacro m-lift
181 "Converts a function f of n arguments into a function of n
182 monadic arguments returning a monadic value."
183 [n f]
184 (let [expr (take n (repeatedly #(gensym "x_")))
185 vars (vec (take n (repeatedly #(gensym "mv_"))))
186 steps (vec (interleave expr vars))]
187 (list `fn vars (monad-expr steps (cons f expr)))))
189 (defmonadfn m-join
190 "Converts a monadic value containing a monadic value into a 'simple'
191 monadic value."
192 [m]
193 (m-bind m identity))
195 (defmonadfn m-fmap
196 "Bind the monadic value m to the function returning (f x) for argument x"
197 [f m]
198 (m-bind m (fn [x] (m-result (f x)))))
200 (defmonadfn m-seq
201 "'Executes' the monadic values in ms and returns a sequence of the
202 basic values contained in them."
203 [ms]
204 (reduce (fn [q p]
205 (m-bind p (fn [x]
206 (m-bind q (fn [y]
207 (m-result (cons x y)))) )))
208 (m-result '())
209 (reverse ms)))
211 (defmonadfn m-map
212 "'Executes' the sequence of monadic values resulting from mapping
213 f onto the values xs. f must return a monadic value."
214 [f xs]
215 (m-seq (map f xs)))
217 (defmonadfn m-chain
218 "Chains together monadic computation steps that are each functions
219 of one parameter. Each step is called with the result of the previous
220 step as its argument. (m-chain (step1 step2)) is equivalent to
221 (fn [x] (domonad [r1 (step1 x) r2 (step2 r1)] r2))."
222 [steps]
223 (reduce (fn m-chain-link [chain-expr step]
224 (fn [v] (m-bind (chain-expr v) step)))
225 m-result
226 steps))
228 (defmonadfn m-reduce
229 "Return the reduction of (m-lift 2 f) over the list of monadic values mvs
230 with initial value (m-result val)."
231 ([f mvs]
232 (if (empty? mvs)
233 (m-result (f))
234 (let [m-f (m-lift 2 f)]
235 (reduce m-f mvs))))
236 ([f val mvs]
237 (let [m-f (m-lift 2 f)
238 m-val (m-result val)]
239 (reduce m-f m-val mvs))))
241 (defmonadfn m-until
242 "While (p x) is false, replace x by the value returned by the
243 monadic computation (f x). Return (m-result x) for the first
244 x for which (p x) is true."
245 [p f x]
246 (if (p x)
247 (m-result x)
248 (domonad
249 [y (f x)
250 z (m-until p f y)]
251 z)))
253 (defmacro m-when
254 "If test is logical true, return monadic value m-expr, else return
255 (m-result nil)."
256 [test m-expr]
257 `(if ~test ~m-expr (~'m-result nil)))
259 (defmacro m-when-not
260 "If test if logical false, return monadic value m-expr, else return
261 (m-result nil)."
262 [test m-expr]
263 `(if ~test (~'m-result nil) ~m-expr))
265 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
266 ;;
267 ;; Utility functions used in monad definitions
268 ;;
269 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
271 (defn- flatten*
272 "Like #(apply concat %), but fully lazy: it evaluates each sublist
273 only when it is needed."
274 [ss]
275 (lazy-seq
276 (when-let [s (seq ss)]
277 (concat (first s) (flatten* (rest s))))))
279 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
280 ;;
281 ;; Commonly used monads
282 ;;
283 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
285 ; Identity monad
286 (defmonad identity-m
287 "Monad describing plain computations. This monad does in fact nothing
288 at all. It is useful for testing, for combination with monad
289 transformers, and for code that is parameterized with a monad."
290 [m-result identity
291 m-bind (fn m-result-id [mv f]
292 (f mv))
293 ])
295 ; Maybe monad
296 (defmonad maybe-m
297 "Monad describing computations with possible failures. Failure is
298 represented by nil, any other value is considered valid. As soon as
299 a step returns nil, the whole computation will yield nil as well."
300 [m-zero nil
301 m-result (fn m-result-maybe [v] v)
302 m-bind (fn m-bind-maybe [mv f]
303 (if (nil? mv) nil (f mv)))
304 m-plus (fn m-plus-maybe [& mvs]
305 (first (drop-while nil? mvs)))
306 ])
308 ; Sequence monad (called "list monad" in Haskell)
309 (defmonad sequence-m
310 "Monad describing multi-valued computations, i.e. computations
311 that can yield multiple values. Any object implementing the seq
312 protocol can be used as a monadic value."
313 [m-result (fn m-result-sequence [v]
314 (list v))
315 m-bind (fn m-bind-sequence [mv f]
316 (flatten* (map f mv)))
317 m-zero (list)
318 m-plus (fn m-plus-sequence [& mvs]
319 (flatten* mvs))
320 ])
322 ; Set monad
323 (defmonad set-m
324 "Monad describing multi-valued computations, like sequence-m,
325 but returning sets of results instead of sequences of results."
326 [m-result (fn m-result-set [v]
327 #{v})
328 m-bind (fn m-bind-set [mv f]
329 (apply clojure.set/union (map f mv)))
330 m-zero #{}
331 m-plus (fn m-plus-set [& mvs]
332 (apply clojure.set/union mvs))
333 ])
335 ; State monad
336 (defmonad state-m
337 "Monad describing stateful computations. The monadic values have the
338 structure (fn [old-state] [result new-state])."
339 [m-result (fn m-result-state [v]
340 (fn [s] [v s]))
341 m-bind (fn m-bind-state [mv f]
342 (fn [s]
343 (let [[v ss] (mv s)]
344 ((f v) ss))))
345 ])
347 (defn update-state
348 "Return a state-monad function that replaces the current state by the
349 result of f applied to the current state and that returns the old state."
350 [f]
351 (fn [s] [s (f s)]))
353 (defn set-state
354 "Return a state-monad function that replaces the current state by s and
355 returns the previous state."
356 [s]
357 (update-state (fn [_] s)))
359 (defn fetch-state
360 "Return a state-monad function that returns the current state and does not
361 modify it."
362 []
363 (update-state identity))
365 (defn fetch-val
366 "Return a state-monad function that assumes the state to be a map and
367 returns the value corresponding to the given key. The state is not modified."
368 [key]
369 (domonad state-m
370 [s (fetch-state)]
371 (key s)))
373 (defn update-val
374 "Return a state-monad function that assumes the state to be a map and
375 replaces the value associated with the given key by the return value
376 of f applied to the old value. The old value is returned."
377 [key f]
378 (fn [s]
379 (let [old-val (get s key)
380 new-s (assoc s key (f old-val))]
381 [old-val new-s])))
383 (defn set-val
384 "Return a state-monad function that assumes the state to be a map and
385 replaces the value associated with key by val. The old value is returned."
386 [key val]
387 (update-val key (fn [_] val)))
389 (defn with-state-field
390 "Returns a state-monad function that expects a map as its state and
391 runs statement (another state-monad function) on the state defined by
392 the map entry corresponding to key. The map entry is updated with the
393 new state returned by statement."
394 [key statement]
395 (fn [s]
396 (let [substate (get s key nil)
397 [result new-substate] (statement substate)
398 new-state (assoc s key new-substate)]
399 [result new-state])))
401 (defn state-m-until
402 "An optimized implementation of m-until for the state monad that
403 replaces recursion by a loop."
404 [p f x]
405 (letfn [(until [p f x s]
406 (if (p x)
407 [x s]
408 (let [[x s] ((f x) s)]
409 (recur p f x s))))]
410 (fn [s] (until p f x s))))
412 ; Writer monad
413 (defn writer-m
414 "Monad describing computations that accumulate data on the side, e.g. for
415 logging. The monadic values have the structure [value log]. Any of the
416 accumulators from clojure.contrib.accumulators can be used for storing the
417 log data. Its empty value is passed as a parameter."
418 [empty-accumulator]
419 (monad
420 [m-result (fn m-result-writer [v]
421 [v empty-accumulator])
422 m-bind (fn m-bind-writer [mv f]
423 (let [[v1 a1] mv
424 [v2 a2] (f v1)]
425 [v2 (clojure.contrib.accumulators/combine a1 a2)]))
426 ]))
428 (defmonadfn write [v]
429 (let [[_ a] (m-result nil)]
430 [nil (clojure.contrib.accumulators/add a v)]))
432 (defn listen [mv]
433 (let [[v a] mv] [[v a] a]))
435 (defn censor [f mv]
436 (let [[v a] mv] [v (f a)]))
438 ; Continuation monad
440 (defmonad cont-m
441 "Monad describing computations in continuation-passing style. The monadic
442 values are functions that are called with a single argument representing
443 the continuation of the computation, to which they pass their result."
444 [m-result (fn m-result-cont [v]
445 (fn [c] (c v)))
446 m-bind (fn m-bind-cont [mv f]
447 (fn [c]
448 (mv (fn [v] ((f v) c)))))
449 ])
451 (defn run-cont
452 "Execute the computation c in the cont monad and return its result."
453 [c]
454 (c identity))
456 (defn call-cc
457 "A computation in the cont monad that calls function f with a single
458 argument representing the current continuation. The function f should
459 return a continuation (which becomes the return value of call-cc),
460 or call the passed-in current continuation to terminate."
461 [f]
462 (fn [c]
463 (let [cc (fn cc [a] (fn [_] (c a)))
464 rc (f cc)]
465 (rc c))))
468 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
469 ;;
470 ;; Monad transformers
471 ;;
472 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
474 (defmacro monad-transformer
475 "Define a monad transforer in terms of the monad operations and the base
476 monad. The argument which-m-plus chooses if m-zero and m-plus are taken
477 from the base monad or from the transformer."
478 [base which-m-plus operations]
479 `(let [which-m-plus# (cond (= ~which-m-plus :m-plus-default)
480 (if (= ::undefined (with-monad ~base ~'m-plus))
481 :m-plus-from-transformer
482 :m-plus-from-base)
483 (or (= ~which-m-plus :m-plus-from-base)
484 (= ~which-m-plus :m-plus-from-transformer))
485 ~which-m-plus
486 :else
487 (throw (java.lang.IllegalArgumentException.
488 "undefined m-plus choice")))
489 combined-monad# (monad ~operations)]
490 (if (= which-m-plus# :m-plus-from-base)
491 (assoc combined-monad#
492 :m-zero (with-monad ~base ~'m-zero)
493 :m-plus (with-monad ~base ~'m-plus))
494 combined-monad#)))
496 (defn maybe-t
497 "Monad transformer that transforms a monad m into a monad in which
498 the base values can be invalid (represented by nothing, which defaults
499 to nil). The third argument chooses if m-zero and m-plus are inherited
500 from the base monad (use :m-plus-from-base) or adopt maybe-like
501 behaviour (use :m-plus-from-transformer). The default is :m-plus-from-base
502 if the base monad m has a definition for m-plus, and
503 :m-plus-from-transformer otherwise."
504 ([m] (maybe-t m nil :m-plus-default))
505 ([m nothing] (maybe-t m nothing :m-plus-default))
506 ([m nothing which-m-plus]
507 (monad-transformer m which-m-plus
508 [m-result (with-monad m m-result)
509 m-bind (with-monad m
510 (fn m-bind-maybe-t [mv f]
511 (m-bind mv
512 (fn [x]
513 (if (identical? x nothing)
514 (m-result nothing)
515 (f x))))))
516 m-zero (with-monad m (m-result nothing))
517 m-plus (with-monad m
518 (fn m-plus-maybe-t [& mvs]
519 (if (empty? mvs)
520 (m-result nothing)
521 (m-bind (first mvs)
522 (fn [v]
523 (if (= v nothing)
524 (apply m-plus-maybe-t (rest mvs))
525 (m-result v)))))))
526 ])))
528 (defn sequence-t
529 "Monad transformer that transforms a monad m into a monad in which
530 the base values are sequences. The argument which-m-plus chooses
531 if m-zero and m-plus are inherited from the base monad
532 (use :m-plus-from-base) or adopt sequence-like
533 behaviour (use :m-plus-from-transformer). The default is :m-plus-from-base
534 if the base monad m has a definition for m-plus, and
535 :m-plus-from-transformer otherwise."
536 ([m] (sequence-t m :m-plus-default))
537 ([m which-m-plus]
538 (monad-transformer m which-m-plus
539 [m-result (with-monad m
540 (fn m-result-sequence-t [v]
541 (m-result (list v))))
542 m-bind (with-monad m
543 (fn m-bind-sequence-t [mv f]
544 (m-bind mv
545 (fn [xs]
546 (m-fmap flatten*
547 (m-map f xs))))))
548 m-zero (with-monad m (m-result (list)))
549 m-plus (with-monad m
550 (fn m-plus-sequence-t [& mvs]
551 (m-reduce concat (list) mvs)))
552 ])))
554 ;; Contributed by Jim Duey
555 (defn state-t
556 "Monad transformer that transforms a monad m into a monad of stateful
557 computations that have the base monad type as their result."
558 [m]
559 (monad [m-result (with-monad m
560 (fn m-result-state-t [v]
561 (fn [s]
562 (m-result [v s]))))
563 m-bind (with-monad m
564 (fn m-bind-state-t [stm f]
565 (fn [s]
566 (m-bind (stm s)
567 (fn [[v ss]]
568 ((f v) ss))))))
569 m-zero (with-monad m
570 (if (= ::undefined m-zero)
571 ::undefined
572 (fn [s]
573 m-zero)))
574 m-plus (with-monad m
575 (if (= ::undefined m-plus)
576 ::undefined
577 (fn [& stms]
578 (fn [s]
579 (apply m-plus (map #(% s) stms))))))
580 ]))