├── PART1.md ├── PART2.md ├── PART3.md ├── PART4.md └── README.md /PART1.md: -------------------------------------------------------------------------------- 1 | A monad tutorial for Clojure programmers (part 1) 2 | ================= 3 | 4 | Monads in functional programming are most often associated with the Haskell language, where they play a central role in 5 | I/O and have found numerous other uses. Most introductions to monads are currently written for Haskell programmers. 6 | However, monads can be used with any functional language, even languages quite different from Haskell. Here I want to 7 | explain monads in the context of Clojure, a modern Lisp dialect with strong support for functional programming. A monad 8 | implementation for Clojure is available in the library clojure.algo.monads. Before trying out the examples given in this 9 | tutorial, type (use 'clojure.algo.monads) into your Clojure REPL. You also have to install the monad library, of course, 10 | which you can do by hand, or using build tools such as leiningen. 11 | 12 | Monads are about composing computational steps into a bigger multi-step computation. Let’s start with the simplest 13 | monad, known as the identity monad in the Haskell world. It’s actually built into the Clojure language, and you have 14 | certainly used it: it’s the let form. 15 | 16 | Consider the following piece of code: 17 | 18 | ```clj 19 | (let [a 1 20 | b (inc a)] 21 | (* a b)) 22 | ``` 23 | This can be seen as a three-step calculation: 24 | 25 | Compute 1 (a constant), and call the result a. 26 | Compute ``(inc a)``, and call the result `b`. 27 | Compute ``(* a b)``, which is the result of the multi-step computation. 28 | Each step has access to the results of all previous steps through the symbols to which their results have been bound. 29 | 30 | Now suppose that Clojure didn’t have a let form. Could you still compose computations by binding intermediate results to 31 | symbols? The answer is yes, using functions. The following expression is in fact equivalent to the previous one: 32 | 33 | ```clj 34 | ( (fn [a] ( (fn [b] (* a b)) (inc a) ) ) 1 ) 35 | ``` 36 | The outermost level defines an anonymous function of a and calls with with the argument 1 – this is how we bind 1 to the 37 | symbol ``a``. Inside the function of ``a``, the same construct is used once more: the body of ``(fn [a] ...)`` is a 38 | function of ``b`` called with argument ``(inc a)``. If you don’t believe that this somewhat convoluted expression is 39 | equivalent to the original let form, just paste both into Clojure! 40 | 41 | Of course the functional equivalent of the let form is not something you would want to work with. The computational 42 | steps appear in reverse order, and the whole construct is nearly unreadable even for this very small example. But we can 43 | clean it up and put the steps in the right order with a small helper function, bind. We will call it ``m-bind`` (for 44 | monadic bind) right away, because that’s the name it has in Clojure’s monad library. First, its definition: 45 | 46 | ```clj 47 | (defn m-bind [value function] 48 | (function value)) 49 | ``` 50 | As you can see, it does almost nothing, but it permits to write a value before the function that is applied to it. Using 51 | ``m-bind``, we can write our example as 52 | 53 | ```clj 54 | (m-bind 1 (fn [a] 55 | (m-bind (inc a) (fn [b] 56 | (* a b))))) 57 | ``` 58 | 59 | That’s still not as nice as the let form, but it comes a lot closer. In fact, all it takes to convert a let form into a 60 | chain of computations linked by ``m-bind`` is a little macro. This macro is called ``domonad``, and it permits us to 61 | write our example as 62 | 63 | ```clj 64 | (domonad identity-m 65 | [a 1 66 | b (inc a)] 67 | (* a b)) 68 | ``` 69 | 70 | This looks exactly like our original let form. Running ``macroexpand-1`` on it yields 71 | 72 | ```clj 73 | (clojure.algo.monads/with-monad identity-m 74 | (m-bind 1 (fn [a] (m-bind (inc a) (fn [b] (m-result (* a b))))))) 75 | ``` 76 | 77 | This is the expression you have seen above, wrapped in a ``(with-monad identity-m ...)`` block (to tell Clojure that you 78 | want to evaluate it in the identity monad) and with an additional call to ``m-result`` that I will explain later. For 79 | the identity monad, ``m-result`` is just identity – hence its name. 80 | 81 | As you might guess from all this, monads are generalizations of the let form that replace the simple ``m-bind`` function 82 | shown above by something more complex. Each monad is defined by an implementation of ``m-bind`` and an associated 83 | implementation of ``m-result``. A ``with-monad`` block simply binds (using a let form!) these implementations to the 84 | names ``m-bind`` and ``m-result``, so that you can use a single syntax for composing computations in any monad. Most 85 | frequently, you will use the ``domonad`` macro for this. 86 | 87 | As our second example, we will look at another very simple monad, but one that adds something useful that you don’t get 88 | in a let form. Suppose your computations can fail in some way, and signal failure by producing nil as a result. Let’s 89 | take our example expression again and wrap it into a function: 90 | 91 | ```clj 92 | (defn f [x] 93 | (let [a x 94 | b (inc a)] 95 | (* a b))) 96 | ``` 97 | 98 | In the new setting of possibly-failing computations, you want this to return ``nil`` when ``x`` is ``nil``, or when 99 | ``(inc a)`` yields ``nil``. (Of course ``(inc a)`` will never yield ``nil``, but that’s the nature of examples…) Anyway, 100 | the idea is that whenever a computational step yields ``nil``, the final result of the computation is ``nil``, and the 101 | remaining steps are never executed. All it takes to get this behaviour is a small change: 102 | 103 | ```clj 104 | (defn f [x] 105 | (domonad maybe-m 106 | [a x 107 | b (inc a)] 108 | (* a b))) 109 | ``` 110 | 111 | The maybe monad represents computations whose result is maybe a valid value, but maybe ``nil``. Its ``m-result`` 112 | function is still identity, so we don’t have to discuss ``m-result`` yet (be patient, we will get there in the second 113 | part of this tutorial). All the magic is in the m-bind function: 114 | 115 | ```clj 116 | (defn m-bind [value function] 117 | (if (nil? value) 118 | nil 119 | (function value))) 120 | ``` 121 | 122 | If its input value is non-nil, it calls the supplied function, just as in the identity monad. Recall that this function 123 | represents the rest of the computation, i.e. all following steps. If the value is ``nil``, then ``m-bind`` returns 124 | ``nil`` and the rest of the computation is never called. You can thus call ``(f 1)``, yielding 2 as before, but also 125 | ``(f nil)`` yielding ``nil``, without having to add nil-detecting code after every step of your computation, because 126 | ``m-bind`` does it behind the scenes. 127 | 128 | In [part 2](PART2.md), I will introduce some more monads, and look at some generic functions that can be used in any 129 | monad to aid in composing computations. 130 | -------------------------------------------------------------------------------- /PART2.md: -------------------------------------------------------------------------------- 1 | A monad tutorial for Clojure programmers (part 2) 2 | ================= 3 | 4 | In the first part of this tutorial, I have introduced the two most basic monads: the identity monad and the maybe monad. 5 | In this part, I will continue with the sequence monad, which will be the occasion to explain the role of the mysterious 6 | ``m-result`` function. I will also show a couple of useful generic monad operations. 7 | 8 | One of the most frequently used monads is the sequence monad (known in the Haskell world as the list monad). It is in 9 | fact so common that it is built into Clojure as well, in the form of the for form. Let’s look at an example: 10 | 11 | ```clj 12 | (for [a (range 5) 13 | b (range a)] 14 | (* a b)) 15 | ``` 16 | 17 | A for form resembles a let form not only syntactically. It has the same structure: a list of binding expressions, in 18 | which each expression can use the bindings from the preceding ones, and a final result expressions that typically 19 | depends on all the bindings as well. The difference between ``let`` and ``for`` is that ``let`` binds a single value to 20 | each symbol, whereas ``for`` binds several values in sequence. The expressions in the binding list must therefore 21 | evaluate to sequences, and the result is a sequence as well. The ``for`` form can also contain conditions in the form of 22 | ``:when`` and ``:while`` clauses, which I will discuss later. From the monad point of view of composable computations, 23 | the sequences are seen as the results of non-deterministic computations, i.e. computations that have more than one 24 | result. 25 | 26 | Using the monad library, the above loop is written as 27 | 28 | ```clj 29 | (domonad sequence-m 30 | [a (range 5) 31 | b (range a)] 32 | (* a b)) 33 | ``` 34 | 35 | Since we alread know that the domonad macro expands into a chain of ``m-bind`` calls ending in an expression that calls 36 | ``m-result``, all that remains to be explained is how ``m-bind`` and ``m-result`` are defined to obtain the desired 37 | looping effect. 38 | 39 | As we have seen before, ``m-bind`` calls a function of one argument that represents the rest of the computation, with 40 | the function argument representing the bound variable. To get a loop, we have to call this function repeatedly. A first 41 | attempt at such an ``m-bind`` function would be 42 | 43 | ```clj 44 | (defn m-bind-first-try [sequence function] 45 | (map function sequence)) 46 | ``` 47 | 48 | Let’s see what this does for our example: 49 | 50 | ```clj 51 | (m-bind-first-try (range 5) (fn [a] 52 | (m-bind-first-try (range a) (fn [b] 53 | (* a b))))) 54 | ``` 55 | 56 | This yields ``(() (0) (0 2) (0 3 6) (0 4 8 12))``, whereas the for form given above yields ``(0 0 2 0 3 6 0 4 8 12)``. 57 | Something is not yet quite right. We want a single flat result sequence, but what we get is a nested sequence whose 58 | nesting level equals the number of ``m-bind`` calls. Since ``m-bind`` introduces one level of nesting, it must also 59 | remove one. That sounds like a job for concat. So let’s try again: 60 | 61 | ```clj 62 | (defn m-bind-second-try [sequence function] 63 | (apply concat (map function sequence))) 64 | 65 | (m-bind-second-try (range 5) (fn [a] 66 | (m-bind-second-try (range a) (fn [b] 67 | (* a b))))) 68 | ``` 69 | 70 | This is worse: we get an exception. Clojure tells us: 71 | 72 | ```clj 73 | java.lang.IllegalArgumentException: Don't know how to create ISeq from: Integer 74 | ``` 75 | Back to thinking! 76 | 77 | Our current ``m-bind`` introduces a level of sequence nesting and also takes one away. Its result therefore has as many 78 | levels of nesting as the return value of the function that is called. The final result of our expression has as many 79 | nesting values as ``(* a b)`` – which means none at all. If we want one level of nesting in the result, no matter how 80 | many calls to ``m-bind`` we have, the only solution is to introduce one level of nesting at the end. Let’s try a quick 81 | fix: 82 | 83 | ```clj 84 | (m-bind-second-try (range 5) (fn [a] 85 | (m-bind-second-try (range a) (fn [b] 86 | (list (* a b)))))) 87 | ``` 88 | 89 | This works! Our ``(fn [b] ...)`` always returns a one-element list. The inner ``m-bind`` thus creates a sequence of 90 | one-element lists, one for each value of ``b``, and concatenates them to make a flat list. The outermost ``m-bind`` then 91 | creates such a list for each value of ``a`` and concatenates them to make another flat list. The result of each 92 | ``m-bind`` thus is a flat list, as it should be. And that illustrates nicely why we need ``m-result`` to make a monad 93 | work. The final definition of the sequence monad is thus given by 94 | 95 | ```clj 96 | (defn m-bind [sequence function] 97 | (apply concat (map function sequence))) 98 | 99 | (defn m-result [value] 100 | (list value)) 101 | ``` 102 | 103 | The role of ``m-result`` is to turn a bare value into the expression that, when appearing on the right-hand side in a 104 | monadic binding, binds the symbol to that value. This is one of the conditions that a pair of ``m-bind`` and 105 | ``m-result`` functions must fulfill in order to define a monad. Expressed as Clojure code, this condition reads 106 | 107 | ```clj 108 | (= (m-bind (m-result value) function) 109 | (function value)) 110 | ``` 111 | 112 | There are two more conditions that complete the three monad laws. One of them is 113 | 114 | ```clj 115 | (= (m-bind monadic-expression m-result) 116 | monadic-expression) 117 | ``` 118 | 119 | with monadic-expression standing for any expression valid in the monad under consideration, e.g. a sequence expression 120 | for the sequence monad. This condition becomes clearer when expressed using the domonad macro: 121 | 122 | ```clj 123 | (= (domonad 124 | [x monadic-expression] 125 | x) 126 | monadic-expression) 127 | ``` 128 | 129 | The final monad law postulates associativity: 130 | 131 | ```clj 132 | (= (m-bind (m-bind monadic-expression 133 | function1) 134 | function2) 135 | (m-bind monadic-expression 136 | (fn [x] (m-bind (function1 x) 137 | function2)))) 138 | ``` 139 | 140 | Again this becomes a bit clearer using domonad syntax: 141 | 142 | ```clj 143 | (= (domonad 144 | [y (domonad 145 | [x monadic-expression] 146 | (function1 x))] 147 | (function2 y)) 148 | (domonad 149 | [x monadic-expression 150 | y (m-result (function1 x))] 151 | (function2 y))) 152 | ``` 153 | 154 | It is not necessary to remember the monad laws for using monads, they are of importance only when you start to define 155 | your own monads. What you should remember about ``m-result`` is that ``(m-result x)`` represents the monadic computation 156 | whose result is ``x``. For the sequence monad, this means a sequence with the single element ``x``. For the identity 157 | monad and the maybe monad, which I have presented in the first part of the tutorial, there is no particular structure to 158 | monadic expressions, and therefore ``m-result`` is just the identity function. 159 | 160 | Now it’s time to relax: the most difficult material has been covered. I will return to monad theory in the next part, 161 | where I will tell you more about the ``:when`` clauses in for loops. The rest of this part will be of a more pragmatic 162 | nature. 163 | 164 | You may have wondered what the point of the identity and sequence monads is, given that Clojure already contains fully 165 | equivalent forms. The answer is that there are generic operations on computations that have an interpretation in any 166 | monad. Using the monad library, you can write functions that take a monad as an argument and compose computations in the 167 | given monad. I will come back to this later with a concrete example. The monad library also contains some useful 168 | predefined operations for use with any monad, which I will explain now. They all have names starting with the prefix m-. 169 | 170 | Perhaps the most frequently used generic monad function is ``m-lift``. It converts a function of n standard value 171 | arguments into a function of n monadic expressions that returns a monadic expression. The new function contains implicit 172 | ``m-bind`` and ``m-result`` calls. As a simple example, take 173 | 174 | ```clj 175 | (def nil-respecting-addition 176 | (with-monad maybe-m 177 | (m-lift 2 +))) 178 | ``` 179 | 180 | This is a function that returns the sum of two arguments, just like ``+`` does, except that it automatically returns 181 | ``nil`` when either of its arguments is ``nil``. Note that ``m-lift`` needs to know the number of arguments that the 182 | function has, as there is no way to obtain this information by inspecting the function itself. 183 | 184 | To illustrate how ``m-lift`` works, I will show you an equivalent definition in terms of ``domonad``: 185 | 186 | ```clj 187 | (defn nil-respecting-addition 188 | [x y] 189 | (domonad maybe-m 190 | [a x 191 | b y] 192 | (+ a b))) 193 | ``` 194 | 195 | This shows that ``m-lift`` implies one call to ``m-result`` and one ``m-bind`` call per argument. The same definition 196 | using the sequence monad would yield a function that returns a sequence of all possible sums of pairs from the two input 197 | sequences. 198 | 199 | Exercice: The following function is equivalent to a well-known built-in Clojure function. Which one? 200 | 201 | ```clj 202 | (with-monad sequence-m 203 | (defn mystery 204 | [f xs] 205 | ( (m-lift 1 f) xs ))) 206 | ``` 207 | 208 | Another popular monad operation is ``m-seq``. It takes a sequence of monadic expressions, and returns a sequence of 209 | their result values. In terms of domonad, the expression ``(m-seq [a b c])`` becomes 210 | 211 | ```clj 212 | (domonad 213 | [x a 214 | y b 215 | z c] 216 | (list x y z)) 217 | ``` 218 | 219 | Here is an example of how you might want to use it: 220 | 221 | ```clj 222 | (with-monad sequence-m 223 | (defn ntuples [n xs] 224 | (m-seq (replicate n xs)))) 225 | ``` 226 | 227 | Try it out for yourself! 228 | 229 | The final monad operation I want to mention is ``m-chain``. It takes a list of one-argument computations, and chains 230 | them together by calling each element of this list with the result of the preceding one. For example, ``(m-chain [a b 231 | c])`` is equivalent to 232 | 233 | ```clj 234 | (fn [arg] 235 | (domonad 236 | [x (a arg) 237 | y (b x) 238 | z (c y)] 239 | z)) 240 | ``` 241 | 242 | A usage example is the traversal of hierarchies. The Clojure function parents yields the parents of a given class or 243 | type in the hierarchy used for multimethod dispatch. When given a Java class, it returns its base classes. The following 244 | function builds on parents to find the n-th generation ascendants of a class: 245 | 246 | ```clj 247 | (with-monad sequence-m 248 | (defn n-th-generation 249 | [n cls] 250 | ( (m-chain (replicate n parents)) cls ))) 251 | 252 | (n-th-generation 0 (class [])) 253 | (n-th-generation 1 (class [])) 254 | (n-th-generation 2 (class [])) 255 | ``` 256 | 257 | You may notice that some classes can occur more than once in the result, because they are the base class of more than 258 | one class in the generation below. In fact, we ought to use sets instead of sequences for representing the ascendants at 259 | each generation. Well… that’s easy. Just replace ``sequence-m`` by ``set-m`` and run it again! 260 | 261 | 262 | In [part 3](PART3.md), I will come back to the ``:when`` clause in for loops, and show how it is implemented and 263 | generalized in terms of monads. I will also explain another monad or two. Stay tuned! 264 | -------------------------------------------------------------------------------- /PART3.md: -------------------------------------------------------------------------------- 1 | A monad tutorial for Clojure programmers (part 3) 2 | ================= 3 | 4 | Before moving on to the more advanced aspects of monads, let’s recapitulate what defines a monad (see [part 1](PART1.md) 5 | and [part 2](PART2.md) for explanations): 6 | 7 | A data structure that represents the result of a computation, or the computation itself. We haven’t seen an example of 8 | the latter case yet, but it will come soon. A function ``m-result`` that converts an arbitrary value to a monadic data 9 | structure equivalent to that value. A function ``m-bind`` that binds the result of a computation, represented by the 10 | monadic data structure, to a name (using a function of one argument) to make it available in the following computational 11 | step. Taking the sequence monad as an example, the data structure is the sequence, representing the outcome of a 12 | non-deterministic computation, ``m-result`` is the function list, which converts any value into a list containing just 13 | that value, and ``m-bind`` is a function that executes the remaining steps once for each element in a sequence, and 14 | removes one level of nesting in the result. 15 | 16 | The three ingredients above are what defines a monad, under the condition that the three monad laws are respected. Some 17 | monads have two additional definitions that make it possible to perform additional operations. These two definitions 18 | have the names ``m-zero`` and ``m-plus``. ``m-zero`` represents a special monadic value that corresponds to a 19 | computation with no result. One example is ``nil`` in the maybe monad, which typically represents a failure of some 20 | kind. Another example is the empty sequence in the sequence monad. The identity monad is an example of a monad that has 21 | no ``m-zero``. 22 | 23 | ``m-plus`` is a function that combines the results of two or more computations into a single one. For the sequence 24 | monad, it is the concatenation of several sequences. For the maybe monad, it is a function that returns the first of its 25 | arguments that is not ``nil``. 26 | 27 | There is a condition that has to be satisfied by the definitions of ``m-zero`` and ``m-plus`` for any monad: 28 | 29 | ```clj 30 | (= (m-plus m-zero monadic-expression) 31 | (m-plus monadic-expression m-zero) 32 | monadic-expression) 33 | ``` 34 | 35 | In words, combining ``m-zero`` with any monadic expression must yield the same expression. You can easily verify that 36 | this is true for the two examples (maybe and sequence) given above. 37 | 38 | One benefit of having an ``m-zero`` in a monad is the possibility to use conditions. In the first part, I promised to 39 | return to the ``:when`` clauses in Clojure’s for forms, and now the time has come to discuss them. A simple example is 40 | 41 | ```clj 42 | (for [a (range 5) 43 | :when (odd? a)] 44 | (* 2 a)) 45 | ``` 46 | 47 | The same construction is possible with ``domonad``: 48 | 49 | ```clj 50 | (domonad sequence 51 | [a (range 5) 52 | :when (odd? a)] 53 | (* 2 a)) 54 | ``` 55 | 56 | Recall that ``domonad`` is a macro that translates a let-like syntax into a chain of calls to ``m-bind`` ending in a 57 | call to ``m-result``. The clause a ``(range 5)`` becomes 58 | 59 | ```clj 60 | (m-bind (range 5) (fn [a] remaining-steps)) 61 | ``` 62 | 63 | where remaining-steps is the transformation of the rest of the domonad form. A ``:when`` clause is of course treated 64 | specially, it becomes 65 | 66 | ```clj 67 | (if predicate remaining-steps m-zero) 68 | ``` 69 | 70 | Our small example thus expands to 71 | 72 | ```clj 73 | (m-bind (range 5) (fn [a] 74 | (if (odd? a) (m-result (* 2 a)) m-zero))) 75 | ``` 76 | 77 | Inserting the definitions of ``m-bind``, ``m-result``, and ``m-zero``, we finally get 78 | 79 | ```clj 80 | (apply concat (map (fn [a] 81 | (if (odd? a) (list (* 2 a)) (list))) (range 5))) 82 | ``` 83 | 84 | The result of ``map`` is a sequence of lists that have zero or one elements: zero for even values (the value of 85 | ``m-zero``) and one for odd values (produced by ``m-result``). ``concat`` makes a single flat list out of this, which 86 | contains only the elements that satisfy the ``:when`` clause. 87 | 88 | As for ``m-plus``, it is in practice used mostly with the maybe and sequence monads, or with variations of them. A 89 | typical use would be a search algorithm (think of a parser, a regular expression search, a database query) that can 90 | succeed (with one or more results) or fail (no results). ``m-plus`` would then be used to pursue alternative searches 91 | and combine the results into one (sequence monad), or to continue searching until a result is found (maybe monad). Note 92 | that it is perfectly possible in principle to have a monad with an ``m-zero`` but no ``m-plus``, though in all common 93 | cases an ``m-plus`` can be defined as well if an ``m-zero`` is known. 94 | 95 | After this bit of theory, let’s get acquainted with more monads. In the beginning of this part, I mentioned that the 96 | data structure used in a monad does not always represent the result(s) of a computational step, but sometimes the 97 | computation itself. An example of such a monad is the state monad, whose data structure is a function. 98 | 99 | The state monad’s purpose is to facilitate the implementation of stateful algorithms in a purely functional way. 100 | Stateful algorithms are algorithms that require updating some variables. They are of course very common in imperative 101 | languages, but not compatible with the basic principle of pure functional programs which should not have mutable data 102 | structures. One way to simulate state changes while remaining purely functional is to have a special data item (in 103 | Clojure that would typically be a map) that stores the current values of all mutable variables that the algorithm refers 104 | to. A function that in an imperative program would modify a variable now takes the current state as an additional input 105 | argument and returns an updated state along with its usual result. The changing state thus becomes explicit in the form 106 | of a data item that is passed from function to function as the algorithm’s execution progresses. The state monad is a 107 | way to hide the state-passing behind the scenes and write an algorithm in an imperative style that consults and modifies 108 | the state. 109 | 110 | The state monad differs from the monads that we have seen before in that its data structure is a function. This is thus 111 | a case of a monad whose data structure represents not the result of a computation, but the computation itself. A state 112 | monad value is a function that takes a single argument, the current state of the computation, and returns a vector of 113 | length two containing the result of the computation and the updated state after the computation. In practice, these 114 | functions are typically closures, and what you use in your program code are functions that create these closures. Such 115 | state-monad-value-generating functions are the equivalent of statements in imperative languages. As you will see, the 116 | state monad allows you to compose such functions in a way that makes your code look perfectly imperative, even though it 117 | is still purely functional! 118 | 119 | Let’s start with a simple but frequent situation: the state that your code deals with takes the form of a map. You may 120 | consider that map to be a namespace in an imperative languages, with each key defining a variable. Two basic operations 121 | are reading the value of a variable, and modifying that value. They are already provided in the Clojure monad library, 122 | but I will show them here anyway because they make nice examples. 123 | 124 | First, we look at ``fetch-val``, which retrieves the value of a variable: 125 | 126 | ```clj 127 | (defn fetch-val [key] 128 | (fn [s] 129 | [(key s) s])) 130 | ``` 131 | 132 | Here we have a simple state-monad-value-generating function. It returns a function of a state variable s which, when 133 | executed, returns a vector of the return value and the new state. The return value is the value corresponding to the key 134 | in the map that is the state value. The new state is just the old one – a lookup should not change the state of course. 135 | 136 | Next, let’s look at ``set-val``, which modifies the value of a variable and returns the previous value: 137 | 138 | ```clj 139 | (defn set-val [key val] 140 | (fn [s] 141 | (let [old-val (get s key) 142 | new-s (assoc s key val)] 143 | [old-val new-s]))) 144 | ``` 145 | 146 | The pattern is the same again: ``set-val`` returns a function of state s that, when executed, returns the old value of 147 | the variable plus an updated state map in which the new value is the given one. 148 | 149 | With these two ingredients, we can start composing statements. Let’s define a statement that copies the value of one 150 | variable into another one and returns the previous value of the modified variable: 151 | 152 | ```clj 153 | (defn copy-val [from to] 154 | (domonad state-m 155 | [from-val (fetch-val from) 156 | old-to-val (set-val to from-val)] 157 | old-to-val)) 158 | ``` 159 | 160 | What is the result of ``copy-val``? A state-monad value, of course: a function of a state variable ``s`` that, when 161 | executed, returns the old value of variable to plus the state in which the copy has taken place. Let’s try it out: 162 | 163 | ```clj 164 | (let [initial-state {:a 1 :b 2} 165 | computation (copy-val :b :a) 166 | [result final-state] (computation initial-state)] 167 | final-state) 168 | ``` 169 | 170 | We get ``{:a 2, :b 2}``, as expected. But how does it work? To understand the state monad, we need to look at its 171 | definitions for ``m-result`` and ``m-bind``, of course. 172 | 173 | First, ``m-result``, which does not contain any surprises: it returns a function of a state variable s that, when 174 | executed, returns the result value ``v`` and the unchanged state ``s``: 175 | 176 | ```clj 177 | (defn m-result [v] (fn [s] [v s])) 178 | ``` 179 | 180 | The definition of ``m-bind`` is more interesting: 181 | 182 | ```clj 183 | (defn m-bind [mv f] 184 | (fn [s] 185 | (let [[v ss] (mv s)] 186 | ((f v) ss)))) 187 | ``` 188 | 189 | Obviously, it returns a function of a state variable ``s``. When that function is executed, it first runs the 190 | computation described by mv (the first 'statement' in the chain set up by ``m-bind``) by applying it to the state ``s``. 191 | The return value is decomposed into result ``v`` and new state ``ss``. The result of the first step, ``v``, is injected 192 | into the rest of the computation by calling ``f`` on it (like for the other ``m-bind`` functions that we have seen). The 193 | result of that call is of course another state-monad value, and thus a function of a state variable. When we are inside 194 | our ``(fn [s] ...)``, we are already at the execution stage, so we have to call that function on the state ss, the one 195 | that resulted from the execution of the first computational step. 196 | 197 | The state monad is one of the most basic monads, of which many variants are in use. Usually such a variant adds 198 | something to ``m-bind`` that is specific to the kind of state being handled. An example is the the stream monad in 199 | ``clojure.contrib.stream-utils``. (NOTE: the stream monad has not been migrated to the new Clojure contrib library set.) 200 | Its state describes a stream of data items, and the ``m-bind`` function checks for invalid values and for the 201 | end-of-stream condition in addition to what the basic ``m-bind`` of the state monad does. 202 | 203 | A variant of the state monad that is so frequently used that has itself become one of the standard monads is the writer 204 | monad. Its state is an accumulator (any type implementing the protocol writer-monad-protocol, for example strings, 205 | lists, vectors, and sets), to which computations can add something by calling the function write. The name comes from a 206 | particularly popular application: logging. Take a basic computation in the identity monad, for example (remember that 207 | the identity monad is just Clojure’s built-in let). Now assume you want to add a protocol of the computation in the form 208 | of a list or a string that accumulates information about the progress of the computation. Just change the identity monad 209 | to the writer monad, and add calls to write where required! 210 | 211 | Here is a concrete example: the well-known Fibonacci function in its most straightforward (and most inefficient) 212 | implementation: 213 | 214 | ```clj 215 | (defn fib [n] 216 | (if (< n 2) 217 | n 218 | (let [n1 (dec n) 219 | n2 (dec n1)] 220 | (+ (fib n1) (fib n2))))) 221 | ``` 222 | 223 | Let’s add some protocol of the computation in order to see which calls are made to arrive at the final result. First, we 224 | rewrite the above example a bit to make every computational step explicit: 225 | 226 | ```clj 227 | (defn fib [n] 228 | (if (< n 2) 229 | n 230 | (let [n1 (dec n) 231 | n2 (dec n1) 232 | f1 (fib n1) 233 | f2 (fib n2)] 234 | (+ f1 f2)))) 235 | ``` 236 | 237 | Second, we replace let by domonad and choose the writer monad with a vector accumulator: 238 | 239 | ```clj 240 | (with-monad (writer-m []) 241 | 242 | (defn fib-trace [n] 243 | (if (< n 2) 244 | (m-result n) 245 | (domonad 246 | [n1 (m-result (dec n)) 247 | n2 (m-result (dec n1)) 248 | f1 (fib-trace n1) 249 | _ (write [n1 f1]) 250 | f2 (fib-trace n2) 251 | _ (write [n2 f2])] 252 | (+ f1 f2)))) 253 | 254 | ) 255 | ``` 256 | 257 | Finally, we run ``fib-trace`` and look at the result: 258 | 259 | ```clj 260 | (fib-trace 3) 261 | [2 [[1 1] [0 0] [2 1] [1 1]]] 262 | ``` 263 | 264 | The first element of the return value, 2, is the result of the function fib. The second element is the protocol vector 265 | containing the arguments and results of the recursive calls. 266 | 267 | Note that it is sufficient to comment out the lines with the calls to write and change the monad to ``identity-m`` to 268 | obtain a standard ``fib`` function with no protocol – try it out for yourself! 269 | 270 | [Part 4](PART4.md) will show you how to define your own monads by combining monad building blocks called monad 271 | transformers. As an illustration, I will explain the probability monad and how it can be used for Bayesian estimates 272 | when combined with the maybe-transformer. 273 | -------------------------------------------------------------------------------- /PART4.md: -------------------------------------------------------------------------------- 1 | A monad tutorial for Clojure programmers (part 4) 2 | ================= 3 | 4 | In this fourth and last part of my monad tutorial, I will write about monad transformers. I will deal with only one of 5 | them, but it’s a start. I will also cover the probability monad, and how it can be extended using a monad transformer. 6 | 7 | Basically, a monad transformer is a function that takes a monad argument and returns another monad. The returned monad 8 | is a variant of the one passed in to which some functionality has been added. The monad transformer defines that added 9 | functionality. Many of the common monads that I have presented before have monad transformer analogs that add the 10 | monad's functionality to another monad. This makes monads modular by permitting client code to assemble monad building 11 | blocks into a customized monad that is just right for the task at hand. 12 | 13 | Consider two monads that I have discussed before: the maybe monad and the sequence monad. The maybe monad is for 14 | computations that can fail to produce a valid value, and return ``nil`` in that case. The sequence monad is for 15 | computations that return multiple results, in the form of monadic values that are sequences. A monad combining the two 16 | can take two forms: 1) computations yielding multiple results, any of which could be ``nil`` indicating failure 2) 17 | computations yielding either a sequence of results or ``nil`` in the case of failure. The more interesting combination 18 | is 1), because 2) is of little practical use: failure can be represented more easily and with no additional effort by 19 | returning an empty result sequence. 20 | 21 | So how can we create a monad that puts the maybe monad functionality inside sequence monad values? Is there a way we can 22 | reuse the existing implementations of the maybe monad and the sequence monad? It turns out that this is not possible, 23 | but we can keep one and rewrite the other one as a monad transformer, which we can then apply to the sequence monad (or 24 | in fact some other monad) to get the desired result. To get the combination we want, we need to turn the maybe monad 25 | into a transformer and apply it to the sequence monad. 26 | 27 | First, as a reminder, the definitions of the maybe and the sequence monads: 28 | 29 | ```clj 30 | (defmonad maybe-m 31 | [m-zero nil 32 | m-result (fn [v] v) 33 | m-bind (fn [mv f] 34 | (if (nil? mv) nil (f mv))) 35 | m-plus (fn [& mvs] 36 | (first (drop-while nil? mvs))) 37 | ]) 38 | 39 | (defmonad sequence-m 40 | [m-result (fn [v] 41 | (list v)) 42 | m-bind (fn [mv f] 43 | (apply concat (map f mv))) 44 | m-zero (list) 45 | m-plus (fn [& mvs] 46 | (apply concat mvs)) 47 | ]) 48 | ``` 49 | 50 | And now the definition of the maybe monad transformer: 51 | 52 | ```clj 53 | (defn maybe-t 54 | [m] 55 | (monad [m-result (with-monad m m-result) 56 | m-bind (with-monad m 57 | (fn [mv f] 58 | (m-bind mv 59 | (fn [x] 60 | (if (nil? x) 61 | (m-result nil) 62 | (f x)))))) 63 | m-zero (with-monad m m-zero) 64 | m-plus (with-monad m m-plus) 65 | ])) 66 | ``` 67 | 68 | The real definition in clojure.algo.monads is a bit more complicated, and I will explain the differences later, but for 69 | now this basic version is good enough. The combined monad is constructed by 70 | 71 | ```clj 72 | (def maybe-in-sequence-m (maybe-t sequence-m)) 73 | ``` 74 | 75 | which is a straightforward function call, the result of which is a monad. Let’s first look at what ``m-result`` does. 76 | The ``m-result`` of ``maybe-m`` is the identity function, so we’d expect that our combined monad ``m-result`` is just 77 | the one from ``sequence-m``. This is indeed the case, as ``(with-monad m m-result)`` returns the ``m-result`` function 78 | from monad ``m``. We see the same construct for ``m-zero`` and ``m-plus``, meaning that all we need to understand is 79 | ``m-bind``. 80 | 81 | The combined ``m-bind`` calls the ``m-bind`` of the base monad ``(sequence-m`` in our case``)``, but it modifies the 82 | function argument, i.e. the function that represents the rest of the computation. Before calling it, it first checks if 83 | its argument would be ``nil``. If it isn't, the original function is called, meaning that the combined monad behaves 84 | just like the base monad as long as no computation ever returns ``nil``. If there is a ``nil`` value, the maybe monad 85 | says that no further computation should take place and that the final result should immediately be ``nil``. However, we 86 | can’t just return ``nil``, as we must return a valid monadic value in the combined monad (in our example, a sequence of 87 | possibly-``nil`` values). So we feed ``nil`` into the base monad's ``m-result``, which takes care of wrapping up ``nil`` 88 | in the required data structure. 89 | 90 | Let's see it in action: 91 | 92 | ```clj 93 | (domonad maybe-in-sequence-m 94 | [x [1 2 nil 4] 95 | y [10 nil 30 40]] 96 | (+ x y)) 97 | ``` 98 | 99 | The output is: 100 | 101 | ```clj 102 | (11 nil 31 41 12 nil 32 42 nil 14 nil 34 44) 103 | ``` 104 | 105 | As expected, there are all the combinations of non-``nil`` values in both input sequences. However, it is surprising at 106 | first sight that there are four ``nil`` entries. Shouldn’t there be eight, resulting from the combinations of a ``nil`` 107 | in one sequence with the four values in the other sequence? 108 | 109 | To understand why there are four ``nil``s, let’s look again at how the ``m-bind`` definition in ``maybe-t`` handles 110 | them. At the top level, it will be called with the vector ``[1 2 nil 4]`` as the monadic value. It hands this to the 111 | ``m-bind`` of ``sequence-m``, which calls the anonymous function in ``maybe-t``'s ``m-bind`` four times, once for each 112 | element of the vector. For the three non-``nil`` values, no special treatment is added. For the one ``nil`` value, the 113 | net result of the computation is ``nil`` and the rest of the computation is never called. The ``nil`` in the first input 114 | vector thus accounts for one ``nil`` in the result, and the rest of the computation is called three times. Each of these 115 | three rounds produces then three valid results and one ``nil``. We thus have 3×3 valid results, 3×1 ``nil`` from the 116 | second vector, plus the one ``nil`` from the first vector. That makes nine valid results and four ``nil``s. 117 | 118 | Is there a way to get all sixteen combinations, with all the possible ``nil`` results in the result? Yes, but not using 119 | the ``maybe-t`` transformer. You have to use the maybe and the sequence monads separately, for example like this: 120 | 121 | ```clj 122 | (with-monad maybe-m 123 | (def maybe-+ (m-lift 2 +))) 124 | 125 | (domonad sequence-m 126 | [x [1 2 nil 4] 127 | y [10 nil 30 40]] 128 | (maybe-+ x y)) 129 | ``` 130 | 131 | When you use ``maybe-t``, you always get the shortcutting behaviour seen above: as soon as there is a ``nil``, the total 132 | result is ``nil`` and the rest of the computation is never executed. In most situations, that’s what you want. 133 | 134 | The combination of ``maybe-t`` and ``sequence-m`` is not so useful in practice because a much easier (and more 135 | efficient) way to handle invalid results is to remove them from the sequences before any further processing happens. But 136 | the example is simple and thus fine for explaining the basics. You are now ready for a more realistic example: the use 137 | of ``maybe-t`` with the probability distribution monad. 138 | 139 | The probability distribution monad is made for working with finite probability distributions, i.e. probability 140 | distributions in which a finite set of values has a non-zero probability. Such a distribution is represented by a map 141 | from the values to their probabilities. The monad and various useful functions for working with finite distributions is 142 | defined in the library 143 | [clojure.contrib.probabilities.finite-distributions](https://github.com/richhickey/clojure-contrib/blob/master/src/main/clojure/clojure/contrib/probabilities/finite_distributions.clj) 144 | (NOTE: this module has not yet been migrated to the new Clojure contrib library set.). 145 | 146 | A simple example of a finite distribution: 147 | 148 | ```clj 149 | (use 'clojure.contrib.probabilities.finite-distributions) 150 | (def die (uniform #{1 2 3 4 5 6})) 151 | (prob odd? die) 152 | ``` 153 | 154 | This prints ``1/2``, the probability that throwing a single die yields an odd number. The value of die is the 155 | probability distribution of the outcome of throwing a die: 156 | 157 | ```clj 158 | {6 1/6, 5 1/6, 4 1/6, 3 1/6, 2 1/6, 1 1/6} 159 | ``` 160 | 161 | Suppose we throw the die twice and look at the sum of the two values. What is its probability distribution? That’s where 162 | the monad comes in: 163 | 164 | ```clj 165 | (domonad dist-m 166 | [d1 die 167 | d2 die] 168 | (+ d1 d2)) 169 | ``` 170 | 171 | The result is: 172 | 173 | ```clj 174 | {2 1/36, 3 1/18, 4 1/12, 5 1/9, 6 5/36, 7 1/6, 8 5/36, 9 1/9, 10 1/12, 11 1/18, 12 1/36} 175 | ``` 176 | 177 | You can read the above ``domonad`` block as 'draw a value from the distribution die and call it d1, draw a value from 178 | the distribution die and call it d2, then give me the distribution of ``(+ d1 d2)``'. This is a very simple example; in 179 | general, each distribution can depend on the values drawn from the preceding ones, thus creating the joint distribution 180 | of several variables. This approach is known as 'ancestral sampling'. 181 | 182 | The monad ``dist-m`` applies the basic rule of combining probabilities: if event A has probability p and event B has 183 | probability q, and if the events are independent (or at least uncorrelated), then the probability of the combined event 184 | (A and B) is p×q. Here is the definition of ``dist-m``: 185 | 186 | ```clj 187 | (defmonad dist-m 188 | [m-result (fn [v] {v 1}) 189 | m-bind (fn [mv f] 190 | (letfn [(add-prob [dist [x p]] 191 | (assoc dist x (+ (get dist x 0) p)))] 192 | (reduce add-prob {} 193 | (for [[x p] mv [y q] (f x)] 194 | [y (* q p)])))) 195 | ]) 196 | ``` 197 | 198 | As usually, the interesting stuff happens in ``m-bind``. Its first argument, ``mv``, is a map representing a probability 199 | distribution. Its second argument, ``f``, is a function representing the rest of the calculation. It is called for each 200 | possible value in the probability distribution in the for form. This for form iterates over both the possible values of 201 | the input distribution and the possible values of the distribution returned by ``(f x)``, combining the probabilities by 202 | multiplication and putting them into the output distribution. This is done by reducing over the helper function 203 | ``add-prob``, which checks if the value is already present in the map, and if so, adds the probability to the previously 204 | obtained one. This is necessary because the samples from the ``(f x)`` distribution can contain the same value more than 205 | once if they were obtained for different ``x``. 206 | 207 | For a more interesting example, let’s consider the famous [Monty Hall 208 | problem](http://en.wikipedia.org/wiki/Monty_Hall_problem). In a game show, the player faces three doors. A prize is 209 | waiting for him behind one of them, but there is nothing behind the two other ones. If he picks the right door, he gets 210 | the prize. Up to there, the problem is simple: the probability of winning is ``1/3``. 211 | 212 | But there is a twist. After the player makes his choice, the game host open one of the two other doors, which shows an 213 | empty space. He then asks the player if he wants to change his mind and choose the last remaining door instead of his 214 | initial choice. Is this a good strategy? 215 | 216 | To make this a well-defined problem, we have to assume that the game host knows where the prize is and that he would not 217 | open the corresponding door. Then we can start coding: 218 | 219 | ```clj 220 | (def doors #{:A :B :C}) 221 | 222 | (domonad dist-m 223 | [prize (uniform doors) 224 | choice (uniform doors)] 225 | (if (= choice prize) :win :loose)) 226 | ``` 227 | 228 | Let's go through this step by step. First, we choose the prize door by drawing from a uniform distribution over the 229 | three doors ``:A``, ``:B``, and ``:C``. That represents what happens before the player comes in. Then the player's 230 | initial choice is made, drawing from the same distribution. Finally, we ask for the distribution of the outcome of the 231 | game, code>``:win`` or ``:loose``. The answer is, unsurprisingly, ``{:win 1/3, :loose 2/3}``. 232 | 233 | This covers the case in which the player does not accept the host's proposition to change his mind. If he does, the game 234 | becomes more complicated: 235 | 236 | ```clj 237 | (domonad dist-m 238 | [prize (uniform doors) 239 | choice (uniform doors) 240 | opened (uniform (disj doors prize choice)) 241 | choice (uniform (disj doors opened choice))] 242 | (if (= choice prize) :win :loose)) 243 | ``` 244 | 245 | The third step is the most interesting one: the game host opens a door which is neither the prize door nor the initial 246 | choice of the player. We model this by removing both prize and choice from the set of doors, and draw uniformly from the 247 | resulting set, which can have one or two elements depending on prize and choice. The player then changes his mind and 248 | chooses from the set of doors other than the open one and his initial choice. With the standard three-door game, that 249 | set has exactly one element, but the code above also works for a larger number of doors - try it out yourself! 250 | 251 | Evaluating this piece of code yields ``{:loose 1/3, :win 2/3}``, indicating that the change-your-mind strategy is indeed 252 | the better one. 253 | 254 | Back to the ``maybe-t`` transformer. The finite-distribution library defines a second monad by 255 | 256 | ```clj 257 | (def cond-dist-m (maybe-t dist-m)) 258 | ``` 259 | 260 | This makes ``nil`` a special value in distributions, which is used to represent events that we don't want to consider as 261 | possible ones. With the definitions of ``maybe-t`` and ``dist-m``, you can guess how ``nil`` values are propagated when 262 | distributions are combined: for any ``nil`` value, the distributions that potentially depend on it are never evaluated, 263 | and the ``nil`` value's probability is transferred entirely to the probability of ``nil`` in the output distribution. 264 | But how does ``nil`` ever get into a distribution? And, most of all, what is that good for? 265 | 266 | Let's start with the last question. The goal of this ``nil``-containing distributions is to eliminate certain values. 267 | Once the final distribution is obtained, the ``nil`` value is removed, and the remaining distribution is normalized to 268 | make the sum of the probabilities of the remaining values equal to one. This ``nil``-removal and normalization is 269 | performed by the utility function normalize-cond. The ``cond-dist-m`` monad is thus a sophisticated way to compute 270 | conditional probabilities, and in particular to facilitate Bayesian inference, which is an important technique in all 271 | kinds of data analysis. 272 | 273 | As a first exercice, let's calculate a simple conditional probability from an input distribution and a predicate. The 274 | output distribution should contain only the values satisfying the predicate, but be normalized to one: 275 | 276 | ```clj 277 | (defn cond-prob [pred dist] 278 | (normalize-cond (domonad cond-dist-m 279 | [v dist 280 | :when (pred v)] 281 | v)))) 282 | ``` 283 | 284 | The important line is the one with the ``:when`` condition. As I have explained in parts 1 and 2, the ``domonad`` form 285 | becomes 286 | 287 | ```clj 288 | (m-bind dist 289 | (fn [v] 290 | (if (pred v) 291 | (m-result v) 292 | m-zero))) 293 | ``` 294 | 295 | If you have been following carefully, you should complain now: with the definitions of ``dist-m`` and ``maybe-t`` I have 296 | given above, cond-``dist-m`` should not have any ``m-zero``! But as I said earlier, the ``maybe-t`` shown here is a 297 | simplified version. The real one checks if the base monad has ``m-zero``, and if it hasn't, it substitutes its own, 298 | which is ``(with-monad m (m-result nil))``. Therefore the ``m-zero`` of ``cond-dist-m`` is ``{nil 1}``, the distribution 299 | whose only value is ``nil``. 300 | 301 | The net effect of the ``domonad`` form in this example is thus to keep all values that satisfy the predicate with their 302 | initial probabilities, but to transfer the probability of all values to ``nil``. The call to normalize-cond then takes 303 | out the ``nil`` and re-distributes its probability to the other values. Example: 304 | 305 | ```clj 306 | (cond-prob odd? die) 307 | -> {5 1/3, 3 1/3, 1 1/3} 308 | ``` 309 | 310 | The ``cond-dist-m`` monad really becomes interesting for Bayesian inference problems. Bayesian inference is technique 311 | for drawing conclusions from incomplete observations. It has a wide range of applications, from spam filters to weather 312 | forecasts. For an introduction to the technique and its mathematical basis, you can start with the [Wikipedia 313 | article](http://en.wikipedia.org/wiki/Bayesian_inference). 314 | 315 | Here I will discuss a very simple inference problem and its solution in Clojure. Suppose someone has three dice, one 316 | with six faces, one with eight, and one with twelve. This person picks one die, throws it a few times, and gives us the 317 | numbers, but doesn't tell us which die it was. Given these observations, we would like to infer the probabilities for 318 | each of the three dice to have been picked. We start by defining a function that returns the distribution of a die with 319 | n faces: 320 | 321 | ```clj 322 | (defn die-n [n] (uniform (range 1 (inc n)))) 323 | ``` 324 | 325 | Next, we come to the core of Bayesian inference. One central ingredient is the probability for throwing a given number 326 | under the assumption that die X was used. We thus need the probability distributions for each of our three dice: 327 | 328 | ```clj 329 | (def dice {:six (die-n 6) 330 | :eight (die-n 8 ) 331 | :twelve (die-n 12)}) 332 | ``` 333 | 334 | The other central ingredient is a distribution representing our 'prior knowledge' about the chosen die. We actually know 335 | nothing at all, so each die has the same weight in this distribution: 336 | 337 | ```clj 338 | (def prior (uniform (keys dice))) 339 | ``` 340 | 341 | Now we can write the inference function. It takes as input the prior-knowledge distribution and a number that was 342 | obtained from the die. It returns the a posteriori distribution that combines the prior information with the information 343 | from the observation. 344 | 345 | ```clj 346 | (defn add-observation [prior observation] 347 | (normalize-cond 348 | (domonad cond-dist-m 349 | [die prior 350 | number (get dice die) 351 | :when (= number observation)] 352 | die))) 353 | ``` 354 | 355 | Let's look at the ``domonad`` form. The first step picks one die according to the prior knowledge. The second line 356 | "throws" that die, obtaining a number. The third line eliminates the numbers that don't match the observation. And then 357 | we ask for the distribution of the die. 358 | 359 | It is instructive to compare this function with the mathematical formula for Bayes' theorem, which is the basis of 360 | Bayesian inference. Bayes' theorem is P(H|E) = P(E|H) P(H) / P(E), where H stands for the hypothesis ("the die chosen 361 | was X") and E stands for the evidence ("the number thrown was N"). P(H) is the prior knowledge. The formula must be 362 | evaluated for a fixed value of E, which is the observation. 363 | 364 | The first line of our ``domonad`` form implements P(H), the second line implements P(E|H). These two lines together thus 365 | sample P(E, H) using ancestral sampling, as we have seen before. The ``:when`` line represents the observation; we wish 366 | to apply Bayes' theorem for a fixed value of E. Once E has been fixed, P(E) is just a number, required for 367 | normalization. This is handled by ``normalize-cond`` in our code. 368 | 369 | Let's see what happens when we add a single observation: 370 | 371 | ```clj 372 | (add-observation prior 1) 373 | -> {:twelve 2/9, :eight 1/3, :six 4/9} 374 | ``` 375 | 376 | We see that the highest probability is given to ``:six``, then ``:eight``, and finally ``:twelve``. This happens because 377 | 1 is a possible value for all dice, but it is more probable as a result of throwing a six-faced die (1/6) than as a 378 | result of throwing an eight-faced die (1/8) or a twelve-faced die (1/12). The observation thus favours a die with a 379 | small number of faces. 380 | 381 | If we have three observations, we can call add-observation repeatedly: 382 | 383 | ```clj 384 | (-> prior (add-observation 1) 385 | (add-observation 3) 386 | (add-observation 7)) 387 | -> {:twelve 8/35, :eight 27/35} 388 | ``` 389 | 390 | Now we see that the candidate ``:six`` has disappeared. In fact, the observed value of 7 rules it out completely. 391 | Moreover, the observed numbers strongly favour ``:eight`` over ``:twelve``, which is again due to the preference for the 392 | smallest possible die in the game. 393 | 394 | This inference problem is very similar to how a spam filter works. In that case, the three dice are replaced by the 395 | choices ``:spam`` or ``:no-spam``. For each of them, we have a distribution of words, obtained by analyzing large 396 | quantities of e-mail messages. The function add-observation is strictly the same, we'd just pick different variable 397 | names. And then we'd call it for each word in the message we wish to evaluate, starting from a prior distribution 398 | defined by the total number of ``:spam`` and ``:no-spam`` messages in our database. 399 | 400 | To end this introduction to monad transformers, I will explain the ``m-zero`` problem in ``maybe-t``. As you know, the 401 | maybe monad has an ``m-zero`` definition (``nil``) and an ``m-plus`` definition, and those two can be carried over into 402 | a monad created by applying ``maybe-t`` to some base monad. This is what we have seen in the case of ``cond-dist-m``. 403 | However, the base monad might have its own ``m-zero`` and ``m-plus``, as we have seen in the case of ``sequence-m``. 404 | Which set of definitions should the combined monad have? Only the user of ``maybe-t`` can make that decision, so 405 | ``maybe-t`` has an optional parameter for this (see its documentation for the details). The only clear case is a base 406 | monad without ``m-zero`` and ``m-plus``; in that case, nothing is lost if ``maybe-t`` imposes its own. 407 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | monads-in-clojure 2 | ================= 3 | 4 | Back in 2009 I wrote a four-part tutorial on monads for Clojure 5 | programmers. It can be considered an introduction to the 6 | [algo.monads](http://github.com/clojure/algo.monads) library. 7 | 8 | The tutorial was published on a blog hosted at 9 | [onclojure.com](http://onclojure.com), which seems to have disappeared 10 | in the meantime. [Dan Boykis](http://github.com/danboykis) recovered it from 11 | [archive.org](http://archive.org/) and reformatted it in Markdown, 12 | ready for reading right here on GitHub. For now this repository 13 | contains just this copy of the original tutorial, but I may 14 | update it in the future. 15 | 16 | --------------------------------------------------------------------------------