cleanup
[lambda.git] / monad_transformers.mdwn
index 53a1689..26fb13f 100644 (file)
@@ -12,9 +12,9 @@ So far, we've defined monads as single-layered boxes. Though in the Groenendijk,
        let unit (a : 'a) : 'a reader =
                fun e -> a;;
        let bind (u: 'a reader) (f : 'a -> 'b reader) : 'b reader =
-               fun e -> (fun v -> f v e) (u e);;
+               fun e -> (fun a -> f a e) (u e);;
 
-We've just beta-expanded the familiar `f (u e) e` into `(fun v -> f v
+We've just beta-expanded the familiar `f (u e) e` into `(fun a -> f a
 e) (u e)`. We did that so as to factor out the parts where any Reader monad is
 being supplied as an argument to another function. That will help make some patterns that are coming up more salient.
 
@@ -50,7 +50,7 @@ Here's how it's implemented:
                fun e -> M.unit a;;
 
        let bind (u : 'a readerT(M)) (f : 'a -> 'b readerT(M)) : 'b readerT(M) =
-               fun e -> M.bind (u e) (fun v -> f v e);;
+               fun e -> M.bind (u e) (fun a -> f a e);;
 
 Notice the key differences: where before `unit` was implemented by a function that just returned `a`, now we
 instead return `M.unit a`. Where before `bind` just supplied value `u e`
@@ -99,9 +99,27 @@ Do you see the pattern? Where before `unit` was implemented by a function that r
 Once again, what do you think you'd get if you wrapped StateT monadic packaging around an Identity monad?
 
 
-We spell out all the common monads, their common dedicated operations (such as `lookup` for the Reader monad), and monad transformer cousins of all of these, in an OCaml [[monad library]]. Read the linked page for details about how to use the library, and some design choices we made. Our [[State Monad Tutorial]] gives some more examples of using the library.
+We spell out all the common monads, their common dedicated operations (such as `lookup`- and `shift`-like operations for the Reader monad), and monad transformer cousins of all of these, in an OCaml [[monad library]]. Read the linked page for details about how to use the library, and some design choices we made. Our [[State Monad Tutorial]] gives some more examples of using the library.
 
-When a T monadic layer encloses an inner M monad, the T's interface is the most exposed one. To use operations defined in the inner M monad, you'll need to use T's `elevate` function on them. This brings the M-based operation into the T-around-M monadic bundle. Haskell calls this `lift` (MORE...)
+When a T monadic layer encloses an inner M monad, the T's interface is the most exposed one. To use operations defined in the inner M monad, you'll have to "elevate" them into the outer T packaging. Haskell calls this operation `lift`, but we call it `elevate` because the term "lift" is already now too overloaded. In our usage, `lift` (and `lift2`) are functions that bring non-monadic operations into a monad; `elevate` brings monadic operations from a wrapped monad out into the wrapping.
+
+Here's an example. Suppose `S` is an instance of a State monad:
+
+       # #use "path/to/monads.ml";;
+       # module S = State_monad(struct type store = int end);;
+
+and `MS` is a MaybeT wrapped around `S`:
+
+       # module MS = Maybe_monad.T(S);;
+
+Then if you want to use an `S`-specific monad like `puts succ` inside `MS`, you'll have to use `MS`'s `elevate` function, like this:
+
+       # MS.(...elevate (S.puts succ) ...)
+
+Each monad transformer's `elevate` function will be defined differently. They have to obey the following laws:
+
+*      `Outer.elevate (Inner.unit a) <~~> Outer.unit a`
+*      `Outer.elevate (Inner.bind u f) <~~> Outer.bind (Outer.elevate u) (fun a -> Outer.elevate (f a))`
 
 We said that when T encloses M, you can rely on T's interface to be most exposed. That is intuitive. What you cannot also assume is that the implementing type has a Tish structure surrounding an Mish structure. Often it will be reverse: a ListT(Maybe) is implemented by a `'a list option`, not by an `'a option list`. Until you've tried to write the code to a monadic transformer library yourself, this will probably remain counter-intuitive. But you don't need to concern yourself with it in practise. Think of what you have as a ListT(Maybe); don't worry about whether the underlying implementation is as an `'a list option` or an `'a option list` or as something more complicated.
 
@@ -111,11 +129,71 @@ Notice from the code for StateT, above, that an `'a stateT(M)` is not an `('a M)
        't1 -> 't0           --->  't1 -> 't0 M
        ('t1 -> 't0) -> 't0  --->  ('t1 -> 't0 M) -> 't0 M
 
-Ken Shan's paper [Monads for natural language semantics](http://arxiv.org/abs/cs/0205026v1) (2001) discusses how to systematically move from some plain monads to the corresponding monad transformers. But as he notes, his algorithm isn't the only one possible, and it only applies to monads whose type has a certain form. (Reader and State have that form; List for example doesn't.)
+Ken Shan's paper [Monads for natural language semantics](http://arxiv.org/abs/cs/0205026v1) (2001) discusses how to systematically move from some base monads to the corresponding monad transformers. But as he notes, his algorithm isn't the only one possible, and it only applies to monads whose type has a certain form. (Reader and State have that form; List for example doesn't.)
+
+As best we know, figuring out how a monad transformer should be defined is still something of an art, not something that can be done mechanically. However, you can think that all of the art goes into deciding what StateT and so on should be; having figured that out, plain State would follow as the simple case where StateT is parameterized on the Identity monad.
+
+Apart from whose interface is outermost, the behavior of a StateT(Maybe) and a MaybeT(State) will partly coincide. But in certain crucial respects they will diverge, and you need to think carefully about which behavior you want and what the appropriate layering is for your needs. Consider these examples:
+
+       # module MS = Maybe_monad.T(S);;
+       # module SM = S.T(Maybe_monad);;
+       # MS.(run (elevate (S.puts succ) >> zero () >> elevate S.get >>= fun cur -> unit (cur+10) )) 0;;
+       - : int option * S.store = (None, 1)
+       # MS.(run (elevate (S.puts succ) >> zero () >> elevate (S.put 5) )) 0;;
+       - : unit option * S.store = (None, 1)
+
+Although we have a wrapped `None`, notice that the store (as it was at the point of failure) is still retrievable.
+
+       # SM.(run (puts succ >> elevate (Maybe_monad.zero ()) >> get >>= fun cur -> unit (cur+10) )) 0;;
+       - : ('a, int * S.store) Maybe_monad.result = None
+
+When Maybe is on the inside, on the other hand, a failure means the whole computation has failed, and even the store is no longer available.
+
+<!--
+       # ES.(run( elevate (S.puts succ) >> throw "bye" >> elevate S.get >>= fun i -> unit(i+10) )) 0;;
+       - : int Failure.error * S.store = (Failure.Error "bye", 1)
+       # SE.(run( puts succ >> elevate (Failure.throw "bye") >> get >>= fun i -> unit(i+10) )) 0;;
+       - : (int * S.store) Failure.result = Failure.Error "bye"
+       # ES.(run_exn( elevate (S.puts succ) >> throw "bye" >> elevate S.get >>= fun i -> unit(i+10) )) 0;;
+       Exception: Failure "bye".
+       # SE.(run_exn( puts succ >> elevate (Failure.throw "bye") >> get >>= fun i -> unit(i+10) )) 0;;
+       Exception: Failure "bye".
+-->
+
+Here's an example wrapping Maybe around List, and vice versa:
+
+       # module LM = List_monad.T(Maybe_monad);;
+       # module ML = Maybe_monad.T(List_monad);;
+       # ML.(run (plus (zero ()) (unit 20) >>= fun i -> unit (i+10)));;
+       - : ('_a, int) ML.result = [Some 30]
 
-As best we know, figuring out how a monad transformer should be defined is still something of an art, not something that can be done mechanically. However, you can think that all of the art goes into deciding what ReaderT and so on should be; having figured that out, plain Reader would follow as the simple case where ReaderT is parameterized on the Identity monad.
+When List is on the inside, the failed results just get dropped and the computation proceeds without them.
+
+       # LM.(run (plus (elevate (Maybe_monad.zero ())) (unit 20) >>= fun i -> unit (i+10)));;
+       - : ('_a, int) LM.result = None
+
+On the other hand, when Maybe is on the inside, failures abort the whole computation.
+
+<!--
+       # EL.(run( plus (throw "bye") (unit 20) >>= fun i -> unit(i+10)));;
+       - : int EL.result = [Failure.Error "bye"; Failure.Success 30]
+       # LE.(run( plus (elevate (Failure.throw "bye")) (unit 20) >>= fun i -> unit(i+10)));;
+       - : int LE.result = Failure.Error "bye"
+       # EL.(run_exn( plus (throw "bye") (unit 20) >>= fun i -> unit(i+10)));;
+       Exception: Failure "bye".
+       # LE.(run_exn( plus (elevate (Failure.throw "bye")) (unit 20) >>= fun i -> unit(i+10)));;
+       Exception: Failure "bye".
+-->
+
+This is fun. Notice the difference it makes whether the second `plus` is native to the outer `List_monad`, or whether it's the inner `List_monad`'s `plus` elevated into the outer wrapper:
+
+       # module LL = List_monad.T(List_monad);;
+
+       # LL.(run(plus (unit 1) (unit 2) >>= fun i -> plus (unit i) (unit(10*i)) ));;
+       - : ('_a, int) LL.result = \[[1; 10; 2; 20]]
+       # LL.(run(plus (unit 1) (unit 2) >>= fun i -> elevate L.(plus (unit i) (unit(10*i)) )));;
+       - : ('_a, int) LL.result = [[1; 2]; [1; 20]; [10; 2]; [10; 20]]
 
-Apart from whose interface is outermost, the behavior of a StateT(Maybe) and a MaybeT(State) will partly coincide. But in certain crucial respects they will diverge, and you need to think carefully about which behavior you want and what the appropriate layering is for your needs. (MORE...)
 
 
 Further Reading
@@ -174,7 +252,7 @@ then `list_bind u f` would be `concat [[]; [2]; [2; 4]; [2; 4; 8]]`, that is `[2
                                 |  |
                                 4  8
 
-Except, as we mentioned, our implementation of the Tree monad incorporates an Optionish layer too. So `f' 2` should be not `Leaf 2` but `Some (Leaf 2)`. What if `f'` also mapped `1` to `None` and `4` to `Some (Node (Leaf 2, Leaf 4))`. Then binding the tree `Node (Leaf 1, Node (Leaf 2, Leaf 4))` to `f'` would delete thr branch corresponding to the original `Leaf 1`, and would splice in the results for `f' 2` and `f' 4`, yielding:
+Except, as we mentioned, our implementation of the Tree monad incorporates an Optionish layer too. So `f' 2` should be not `Leaf 2` but `Some (Leaf 2)`. What if `f'` also mapped `1` to `None` and `4` to `Some (Node (Leaf 2, Leaf 4))`. Then binding the tree `Node (Leaf 1, Node (Leaf 2, Leaf 4))` (really the tree itself needs to be wrapped in a `Some`, too, but let me neglect that) to `f'` would delete the branch corresponding to the original `Leaf 1`, and would splice in the results for `f' 2` and `f' 4`, yielding:
 
         .                        
        _|__  >>=  f' ~~>         
@@ -209,15 +287,205 @@ You have to instead say something like this:
 How is all this related to our tree\_monadize function?
 -------------------------------------------------------
 
+Our Tree monad has a corresponding TreeT transformer. Simplified, its implementation looks something like this (we apply it to an inner Reader monad):
+
+
+       type 'a tree_reader = 'a tree reader;;
+       (* really it's an 'a tree option reader, but as I said we're simplifying *)
+
+       let tree_reader_unit (a:'a) : 'a tree_reader = reader_unit (Leaf a);;
+
+       let tree_reader_bind (u: 'a tree_reader) (f: 'a -> 'b tree_reader) : 'b tree_reader =
+         reader_bind u (fun us ->
+               let rec loop us = match us with
+                 | Leaf a ->
+                 f a
+                 | Node(l,r) ->
+                         reader_bind (loop l) (fun ls ->
+                               reader_bind (loop r) (fun rs ->
+                                 reader_unit (Node(ls, rs))))
+               in loop us);;
+
+       let tree_reader_elevate (inner : 'a reader) : 'a tree_reader =
+               reader_bind inner (fun a -> reader_unit (Leaf a))
+
 Recall our earlier definition of `tree_monadize`, specialized for the Reader monad:
 
        let rec tree_monadize (f : 'a -> 'b reader) (t : 'a tree) : 'b tree reader =
            match t with
-           | Leaf a -> reader_bind (f a) (fun b -> reader_unit (Leaf b))
-           | Node (l, r) -> reader_bind (tree_monadize f l) (fun l' ->
-                              reader_bind (tree_monadize f r) (fun r' ->
-                                reader_unit (Node (l', r'))));;
+           | Leaf a ->
+                       (* the next line is equivalent to: tree_reader_elevate (f a) *)
+               reader_bind (f a) (fun b -> reader_unit (Leaf b))
+           | Node (l, r) ->
+               reader_bind (tree_monadize f l) (fun ls ->
+                 reader_bind (tree_monadize f r) (fun rs ->
+                   reader_unit (Node (ls, rs))));;
+
+We rendered the result type here as `'b tree reader`, as we did in our earlier discussion, but as we can see from the above implementation of TreeT(Reader), that's the type of an `'b tree_reader`, that is, of a layered box consisting of TreeT packaging wrapped around an inner Reader box.
+
+The definitions of `tree_monadize` and `tree_reader_bind` should look very similar. They're not quite the same. There's the difference in the order of their function-like and tree-like arguments, but that's inconsequential. More important is that the types of their arguments differs. `tree_reader_bind` wants a tree that's already fused with a reader; `tree_monadize` instead just wants a plain tree. `tree_reader_bind` wants a function that takes the elements occupying its leaves into other `tree_reader`s; `tree_monadize` just wants it to take them into plain `reader`s. That's why the application of `f` to `a` has to be `elevate`d in the `tree_monadize` clause for `Leaf a -> ...`.
+
+But there is an obvious common structure to these two functions, and indeed in the [[monad library]] their more complicated cousins are defined in terms of common pieces. In the monad library, the `tree_monadize` function is called `distribute`; this is an operation living inside the TreeT packaging. There's an analogous `distribute` function living inside the ListT packaging. (Haskell has the second but not the first; it calls it `mapM` and it lives inside the wrapped base monad, instead of the List packaging.)
+
+We linked to [some code](/code/tree_monadize.ml) earlier that demonstrated all the `tree_monadize` examples in a compact way.
+
+Here's how to demonstrate the same examples, using the monad library. First, preliminaries:
+
+       # #use "path/to/monads.ml";;
+       # module T = Tree_monad;;
+       # module R = Reader_monad(struct type env = int -> int end);;
+       # module S = State_monad(struct type store = int end);;
+       # module L = List_monad;;
+       # module C = Continuation_monad;;
+       # module TR = T.T(R);;
+       # module TS = T.T(S);;
+       # module TL = T.T(L);;
+       # module TC = T.T(C);;
+       # let t1 = Some (T.Node (T.Node (T.Leaf 2, T.Leaf 3), T.Node (T.Leaf 5, T.Node (T.Leaf 7, T.Leaf 11))));;
+
+We can use TreeT(Reader) to modify leaves:
+
+       # let tree_reader = TR.distribute (fun i -> R.asks (fun e -> e i)) t1;;
+       # TR.run tree_reader (fun i -> i+i);;
+       - : int T.tree option =
+       Some
+        (T.Node
+          (T.Node (T.Leaf 4, T.Leaf 6),
+               T.Node (T.Leaf 10, T.Node (T.Leaf 14, T.Leaf 22))))
+
+Here's a comparison of how distribute works for trees and how it works for lists:
+
+       # module LR = L.T(R);;
+       # let l1 = [2; 3; 5; 7; 11];;
+       # LR.(run (distribute (fun i -> R.(asks (fun e -> e i))) l1)) (fun i -> i+i);;
+       - : int list = [4; 6; 10; 14; 22]
+
+<!--
+More complex: here we use the monadic `list_reader` or `tree_reader` we got back from `distribute` and `bind` it to other operations:
+
+       # let u = LR.distribute (fun i -> R.(asks (fun e -> e i))) l1 in
+         LR.(run(u >>= fun i -> plus (unit i) (unit (10*i)))) (fun i -> i + i);;
+       - : int list = [4; 40; 6; 60; 10; 100; 14; 140; 22; 220]
+       # let v = TR.distribute (fun i -> R.(asks (fun e -> e i))) t1 in
+         TR.(run(v >>= fun i -> plus (unit i) (unit (10*i)))) (fun i -> i + i);;
+       - : int T.tree option =
+       Some
+        (T.Node
+          (T.Node (T.Node (T.Leaf 4, T.Leaf 40), T.Node (T.Leaf 6, T.Leaf 60)),
+               T.Node
+                (T.Node (T.Leaf 10, T.Leaf 100),
+                 T.Node (T.Node (T.Leaf 14, T.Leaf 140), T.Node (T.Leaf 22, T.Leaf 220)))))
+-->
+
+We can use TreeT(State) to count leaves:
+
+       # let tree_counter = TS.distribute (fun i -> S.(puts succ >> unit i)) t1 in
+         TS.run tree_counter 0;;
+       - : int T.tree option * S.store =
+       (Some
+         (T.Node
+               (T.Node (T.Leaf 2, T.Leaf 3),
+                T.Node (T.Leaf 5, T.Node (T.Leaf 7, T.Leaf 11)))),
+        5)
+
+or to annotate leaves:
+
+       # let tree_annotater = TS.distribute (fun i -> S.(puts succ >> get >>= fun s -> unit (i,s))) t1 in
+         TS.run tree_annotater 0;;
+       - : (int * S.store) T.tree option * S.store =
+       (Some
+         (T.Node
+               (T.Node (T.Leaf (2, 1), T.Leaf (3, 2)),
+                T.Node (T.Leaf (5, 3), T.Node (T.Leaf (7, 4), T.Leaf (11, 5))))),
+        5)
+
+Here's a comparison of how distribute works for trees and how it works for lists:
+
+       # module LS = L.T(S);;
+
+       # let list_counter = LS.distribute (fun i -> S.(puts succ >> unit i)) l1 in
+         LS.run list_counter 0;;
+       - : int list * S.store = ([2; 3; 5; 7; 11], 5)
+
+       # let list_annotater = LS.distribute (fun i -> S.(puts succ >> get >>= fun s -> unit (i,s) )) l1 in
+         LS.run list_annotater 0;;
+       - : (int * S.store) list * S.store =
+       ([(2, 1); (3, 2); (5, 3); (7, 4); (11, 5)], 5)
+
+
+<!--
+#   let u = LS.distribute (fun i -> if i = -1 then S.get else if i < 0 then S.(puts     succ >> unit 0) else S.unit i) [10;-1;-2;-1;20] in
+  LS.run u 0;;
+- : S.store list * S.store = ([10; 0; 0; 1; 20], 1)
+-->
+
+
+We can use TreeT(List) to copy the tree with different choices for some of the leaves:
+
+       # let tree_chooser = TL.distribute (fun i -> L.(if i = 2 then plus (unit 20) (unit 21) else unit i)) t1;;
+       # TL.run tree_chooser;;
+       - : ('_a, int) TL.result =
+       [Some
+         (T.Node
+               (T.Node (T.Leaf 20, T.Leaf 3),
+                T.Node (T.Leaf 5, T.Node (T.Leaf 7, T.Leaf 11))));
+        Some
+         (T.Node
+               (T.Node (T.Leaf 21, T.Leaf 3),
+                T.Node (T.Leaf 5, T.Node (T.Leaf 7, T.Leaf 11))))]
+
+
+Finally, we use TreeT(Continuation) to do various things. For reasons I won't explain here, the library currently requires you to run the Tree-plus-Continuation bundle using a different sequence of `run` commands:
+
+We can do nothing:
+<!--
+let initial_continuation = fun t -> t in
+TreeCont.monadize Continuation_monad.unit t1 initial_continuation;;
+-->
+
+       # C.run_exn TC.(run (distribute C.unit t1)) (fun t -> t);;
+       - : int T.tree option =
+       Some
+        (T.Node
+          (T.Node (T.Leaf 2, T.Leaf 3),
+               T.Node (T.Leaf 5, T.Node (T.Leaf 7, T.Leaf 11))))
+
+We can square each leaf:
+<!--
+let initial_continuation = fun t -> t in
+TreeCont.monadize (fun a k -> k (a*a)) t1 initial_continuation;;
+-->
+
+       # C.run_exn TC.(run (distribute C.(fun a -> shift (fun k -> k (a*a))) t1)) (fun t -> t);;
+       - : int T.tree option =
+       Some
+        (T.Node
+          (T.Node (T.Leaf 4, T.Leaf 9),
+               T.Node (T.Leaf 25, T.Node (T.Leaf 49, T.Leaf 121))))
+
+The meaning of `shift` will be explained in [[CPS and Continuation Operators]]. Here you should just regard it as a primitive operation in our Continuation monad. In [this code](/code/tree_monadize.ml) you could simply write:
+
+       TreeCont.monadize (fun a -> fun k -> k (a*a)) t1 (fun t -> t);;
+
+But because of the way our monad library hides the underlying machinery, here you can no longer just say `fun k -> k (a*a)`; you have to say `shift (fun k -> k (a*a))`.
+
+Moving on, we can count the leaves:
+<!--
+let initial_continuation = fun t -> 0 in
+TreeCont.monadize (fun a k -> 1 + k a) t1 initial_continuation;;
+-->
+
+       # C.run_exn TC.(run (distribute C.(fun a -> shift (fun k -> k a >>= fun v -> unit (1+v))) t1)) (fun t -> 0);;
+       - : int = 5
+
+
+And we can convert the tree to a list of leaves:
+<!--
+let initial_continuation = fun t -> [] in
+TreeCont.monadize (fun a k -> a :: k a) t1 initial_continuation;;
+-->
 
+       # C.run_exn TC.(run (distribute C.(fun a -> shift (fun k -> k a >>= fun v -> unit (a::v))) t1)) (fun t -> []);;
+       - : int list = [2; 3; 5; 7; 11]
 
-(MORE...)