aboutsummaryrefslogtreecommitdiff
path: root/src/clojure/contrib/monads
diff options
context:
space:
mode:
Diffstat (limited to 'src/clojure/contrib/monads')
-rw-r--r--src/clojure/contrib/monads/examples.clj425
1 files changed, 0 insertions, 425 deletions
diff --git a/src/clojure/contrib/monads/examples.clj b/src/clojure/contrib/monads/examples.clj
deleted file mode 100644
index 00e5dfaf..00000000
--- a/src/clojure/contrib/monads/examples.clj
+++ /dev/null
@@ -1,425 +0,0 @@
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; Monad application examples
-;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(ns
- #^{:author "Konrad Hinsen"
- :skip-wiki true
- :doc "Examples for using monads"}
- clojure.contrib.monads.examples
- (:use [clojure.contrib.monads
- :only (domonad with-monad m-lift m-seq m-reduce m-when
- sequence-m
- maybe-m
- state-m fetch-state set-state
- writer-m write
- cont-m run-cont call-cc
- maybe-t)])
- (:require (clojure.contrib [accumulators :as accu])))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; Sequence manipulations with the sequence monad
-;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-; Note: in the Haskell world, this monad is called the list monad.
-; The Clojure equivalent to Haskell's lists are (possibly lazy)
-; sequences. This is why I call this monad "sequence". All sequences
-; created by sequence monad operations are lazy.
-
-; Monad comprehensions in the sequence monad work exactly the same
-; as Clojure's 'for' construct, except that :while clauses are not
-; available.
-(domonad sequence-m
- [x (range 5)
- y (range 3)]
- (+ x y))
-
-; Inside a with-monad block, domonad is used without the monad name.
-(with-monad sequence-m
- (domonad
- [x (range 5)
- y (range 3)]
- (+ x y)))
-
-; Conditions are written with :when, as in Clojure's for form:
-(domonad sequence-m
- [x (range 5)
- y (range (+ 1 x))
- :when (= (+ x y) 2)]
- (list x y))
-
-; :let is also supported like in for:
-(domonad sequence-m
- [x (range 5)
- y (range (+ 1 x))
- :let [sum (+ x y)
- diff (- x y)]
- :when (= sum 2)]
- (list diff))
-
-; An example of a sequence function defined in terms of a lift operation.
-(with-monad sequence-m
- (defn pairs [xs]
- ((m-lift 2 #(list %1 %2)) xs xs)))
-
-(pairs (range 5))
-
-; Another way to define pairs is through the m-seq operation. It takes
-; a sequence of monadic values and returns a monadic value containing
-; the sequence of the underlying values, obtained from chaining together
-; from left to right the monadic values in the sequence.
-(with-monad sequence-m
- (defn pairs [xs]
- (m-seq (list xs xs))))
-
-(pairs (range 5))
-
-; This definition suggests a generalization:
-(with-monad sequence-m
- (defn ntuples [n xs]
- (m-seq (replicate n xs))))
-
-(ntuples 2 (range 5))
-(ntuples 3 (range 5))
-
-; Lift operations can also be used inside a monad comprehension:
-(domonad sequence-m
- [x ((m-lift 1 (partial * 2)) (range 5))
- y (range 2)]
- [x y])
-
-; The m-plus operation does concatenation in the sequence monad.
-(domonad sequence-m
- [x ((m-lift 2 +) (range 5) (range 3))
- y (m-plus (range 2) '(10 11))]
- [x y])
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; Handling failures with the maybe monad
-;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-; Maybe monad versions of basic arithmetic
-(with-monad maybe-m
- (def m+ (m-lift 2 +))
- (def m- (m-lift 2 -))
- (def m* (m-lift 2 *)))
-
-; Division is special for two reasons: we can't call it m/ because that's
-; not a legal Clojure symbol, and we want it to fail if a division by zero
-; is attempted. It is best defined by a monad comprehension with a
-; :when clause:
-(defn safe-div [x y]
- (domonad maybe-m
- [a x
- b y
- :when (not (zero? b))]
- (/ a b)))
-
-; Now do some non-trivial computation with division
-; It fails for (1) x = 0, (2) y = 0 or (3) y = -x.
-(with-monad maybe-m
- (defn some-function [x y]
- (let [one (m-result 1)]
- (safe-div one (m+ (safe-div one (m-result x))
- (safe-div one (m-result y)))))))
-
-; An example that doesn't fail:
-(some-function 2 3)
-; And two that do fail, at different places:
-(some-function 2 0)
-(some-function 2 -2)
-
-; In the maybe monad, m-plus selects the first monadic value that
-; holds a valid value.
-(with-monad maybe-m
- (m-plus (some-function 2 0) (some-function 2 -2) (some-function 2 3)))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; Random numbers with the state monad
-;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-; A state monad item represents a computation that changes a state and
-; returns a value. Its structure is a function that takes a state argument
-; and returns a two-item list containing the value and the updated state.
-; It is important to realize that everything you put into a state monad
-; expression is a state monad item (thus a function), and everything you
-; get out as well. A state monad does not perform a calculation, it
-; constructs a function that does the computation when called.
-
-; First, we define a simple random number generator with explicit state.
-; rng is a function of its state (an integer) that returns the
-; pseudo-random value derived from this state and the updated state
-; for the next iteration. This is exactly the structure of a state
-; monad item.
-(defn rng [seed]
- (let [m 259200
- value (/ (float seed) (float m))
- next (rem (+ 54773 (* 7141 seed)) m)]
- [value next]))
-
-; We define a convenience function that creates an infinite lazy seq
-; of values obtained from iteratively applying a state monad value.
-(defn value-seq [f seed]
- (lazy-seq
- (let [[value next] (f seed)]
- (cons value (value-seq f next)))))
-
-; Next, we define basic statistics functions to check our random numbers
-(defn sum [xs] (apply + xs))
-(defn mean [xs] (/ (sum xs) (count xs)))
-(defn variance [xs]
- (let [m (mean xs)
- sq #(* % %)]
- (mean (for [x xs] (sq (- x m))))))
-
-; rng implements a uniform distribution in the interval [0., 1.), so
-; ideally, the mean would be 1/2 (0.5) and the variance 1/12 (0.8333).
-(mean (take 1000 (value-seq rng 1)))
-(variance (take 1000 (value-seq rng 1)))
-
-; We make use of the state monad to implement a simple (but often sufficient)
-; approximation to a Gaussian distribution: the sum of 12 random numbers
-; from rng's distribution, shifted by -6, has a distribution that is
-; approximately Gaussian with 0 mean and variance 1, by virtue of the central
-; limit theorem.
-; In the first version, we call rng 12 times explicitly and calculate the
-; shifted sum in a monad comprehension:
-(def gaussian1
- (domonad state-m
- [x1 rng
- x2 rng
- x3 rng
- x4 rng
- x5 rng
- x6 rng
- x7 rng
- x8 rng
- x9 rng
- x10 rng
- x11 rng
- x12 rng]
- (- (+ x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12) 6.)))
-
-; Let's test it:
-(mean (take 1000 (value-seq gaussian1 1)))
-(variance (take 1000 (value-seq gaussian1 1)))
-
-; Of course, we'd rather have a loop construct for creating the 12
-; random numbers. This would be easy if we could define a summation
-; operation on random-number generators, which would then be used in
-; combination with reduce. The lift operation gives us exactly that.
-; More precisely, we need (m-lift 2 +), because we want both arguments
-; of + to be lifted to the state monad:
-(def gaussian2
- (domonad state-m
- [sum12 (reduce (m-lift 2 +) (replicate 12 rng))]
- (- sum12 6.)))
-
-; Such a reduction is often quite useful, so there's m-reduce predefined
-; to simplify it:
-(def gaussian2
- (domonad state-m
- [sum12 (m-reduce + (replicate 12 rng))]
- (- sum12 6.)))
-
-; The statistics should be strictly the same as above, as long as
-; we use the same seed:
-(mean (take 1000 (value-seq gaussian2 1)))
-(variance (take 1000 (value-seq gaussian2 1)))
-
-; We can also do the subtraction of 6 in a lifted function, and get rid
-; of the monad comprehension altogether:
-(with-monad state-m
- (def gaussian3
- ((m-lift 1 #(- % 6.))
- (m-reduce + (replicate 12 rng)))))
-
-; Again, the statistics are the same:
-(mean (take 1000 (value-seq gaussian3 1)))
-(variance (take 1000 (value-seq gaussian3 1)))
-
-; For a random point in two dimensions, we'd like a random number generator
-; that yields a list of two random numbers. The m-seq operation can easily
-; provide it:
-(with-monad state-m
- (def rng2 (m-seq (list rng rng))))
-
-; Let's test it:
-(rng2 1)
-
-; fetch-state and get-state can be used to save the seed of the random
-; number generator and go back to that saved seed later on:
-(def identical-random-seqs
- (domonad state-m
- [seed (fetch-state)
- x1 rng
- x2 rng
- _ (set-state seed)
- y1 rng
- y2 rng]
- (list [x1 x2] [y1 y2])))
-
-(identical-random-seqs 1)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; Logging with the writer monad
-;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-; A basic logging example
-(domonad (writer-m accu/empty-string)
- [x (m-result 1)
- _ (write "first step\n")
- y (m-result 2)
- _ (write "second step\n")]
- (+ x y))
-
-; For a more elaborate application, let's trace the recursive calls of
-; a naive implementation of a Fibonacci function. The starting point is:
-(defn fib [n]
- (if (< n 2)
- n
- (let [n1 (dec n)
- n2 (dec n1)]
- (+ (fib n1) (fib n2)))))
-
-; First we rewrite it to make every computational step explicit
-; in a let expression:
-(defn fib [n]
- (if (< n 2)
- n
- (let [n1 (dec n)
- n2 (dec n1)
- f1 (fib n1)
- f2 (fib n2)]
- (+ f1 f2))))
-
-; Next, we replace the let by a domonad in a writer monad that uses a
-; vector accumulator. We can then place calls to write in between the
-; steps, and obtain as a result both the return value of the function
-; and the accumulated trace values.
-(with-monad (writer-m accu/empty-vector)
-
- (defn fib-trace [n]
- (if (< n 2)
- (m-result n)
- (domonad
- [n1 (m-result (dec n))
- n2 (m-result (dec n1))
- f1 (fib-trace n1)
- _ (write [n1 f1])
- f2 (fib-trace n2)
- _ (write [n2 f2])
- ]
- (+ f1 f2))))
-
-)
-
-(fib-trace 5)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; Sequences with undefined value: the maybe-t monad transformer
-;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-; A monad transformer is a function that takes a monad argument and
-; returns a monad as its result. The resulting monad adds some
-; specific behaviour aspect to the input monad.
-
-; The simplest monad transformer is maybe-t. It adds the functionality
-; of the maybe monad (handling failures or undefined values) to any other
-; monad. We illustrate this by applying maybe-t to the sequence monad.
-; The result is an enhanced sequence monad in which undefined values
-; (represented by nil) are not subjected to any transformation, but
-; lead immediately to a nil result in the output.
-
-; First we define the combined monad:
-(def seq-maybe-m (maybe-t sequence-m))
-
-; As a first illustration, we create a range of integers and replace
-; all even values by nil, using a simple when expression. We use this
-; sequence in a monad comprehension that yields (inc x). The result
-; is a sequence in which inc has been applied to all non-nil values,
-; whereas the nil values appear unmodified in the output:
-(domonad seq-maybe-m
- [x (for [n (range 10)] (when (odd? n) n))]
- (inc x))
-
-; Next we repeat the definition of the function pairs (see above), but
-; using the seq-maybe monad:
-(with-monad seq-maybe-m
- (defn pairs-maybe [xs]
- (m-seq (list xs xs))))
-
-; Applying this to a sequence containing nils yields the pairs of all
-; non-nil values interspersed with nils that result from any combination
-; in which one or both of the values is nil:
-(pairs-maybe (for [n (range 5)] (when (odd? n) n)))
-
-; It is important to realize that undefined values (nil) are not eliminated
-; from the iterations. They are simply not passed on to any operations.
-; The outcome of any function applied to arguments of which at least one
-; is nil is supposed to be nil as well, and the function is never called.
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; Continuation-passing style in the cont monad
-;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-; A simple computation performed in continuation-passing style.
-; (m-result 1) returns a function that, when called with a single
-; argument f, calls (f 1). The result of the domonad-computation is
-; a function that behaves in the same way, passing 3 to its function
-; argument. run-cont executes a continuation by calling it on identity.
-(run-cont
- (domonad cont-m
- [x (m-result 1)
- y (m-result 2)]
- (+ x y)))
-
-; Let's capture a continuation using call-cc. We store it in a global
-; variable so that we can do with it whatever we want. The computation
-; is the same one as in the first example, but it has the side effect
-; of storing the continuation at (m-result 2).
-(def continuation nil)
-
-(run-cont
- (domonad cont-m
- [x (m-result 1)
- y (call-cc (fn [c] (def continuation c) (c 2)))]
- (+ x y)))
-
-; Now we can call the continuation with whatever argument we want. The
-; supplied argument takes the place of 2 in the above computation:
-(run-cont (continuation 5))
-(run-cont (continuation 42))
-(run-cont (continuation -1))
-
-; Next, a function that illustrates how a captured continuation can be
-; used as an "emergency exit" out of a computation:
-(defn sqrt-as-str [x]
- (call-cc
- (fn [k]
- (domonad cont-m
- [_ (m-when (< x 0) (k (str "negative argument " x)))]
- (str (. Math sqrt x))))))
-
-(run-cont (sqrt-as-str 2))
-(run-cont (sqrt-as-str -2))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;