From 9efbe94f74c2ea61522fcdb3e3d012fde6034fcd Mon Sep 17 00:00:00 2001 From: Jim Pryor Date: Wed, 1 Dec 2010 20:01:06 -0500 Subject: [PATCH] manip trees: more explanation Signed-off-by: Jim Pryor --- manipulating_trees_with_monads.mdwn | 65 +++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 18 deletions(-) diff --git a/manipulating_trees_with_monads.mdwn b/manipulating_trees_with_monads.mdwn index c92065c4..61d29640 100644 --- a/manipulating_trees_with_monads.mdwn +++ b/manipulating_trees_with_monads.mdwn @@ -81,7 +81,7 @@ supplying the appropriate `int -> int` operation in place of `double`: - : int tree =ppp Node (Node (Leaf 4, Leaf 9), Node (Leaf 25, Node (Leaf 49, Leaf 121))) -Note that what `tree_map` does is take some global, contextual +Note that what `tree_map` does is take some unchanging contextual information---what to do to each leaf---and supplies that information to each subpart of the computation. In other words, `tree_map` has the behavior of a reader monad. Let's make that explicit. @@ -113,9 +113,7 @@ tree` in which each leaf `i` has been replaced with `f i`. With previous readers, we always knew which kind of environment to expect: either an assignment function (the original calculator simulation), a world (the intensionality monad), an integer (the -Jacobson-inspired link monad), etc. In the present case, it will be -enough to expect that our "environment" will be some function of type -`int -> int`. +Jacobson-inspired link monad), etc. In the present case, we expect that our "environment" will be some function of type `int -> int`. "Looking up" some `int` in the environment will return us the `int` that comes out the other side of that function. type 'a reader = (int -> int) -> 'a;; (* mnemonic: e for environment *) let reader_unit (a : 'a) : 'a reader = fun _ -> a;; @@ -141,10 +139,38 @@ But we can do this: reader_unit (Node (x, y))));; This function says: give me a function `f` that knows how to turn -something of type `'a` into an `'b reader`, and I'll show you how to -turn an `'a tree` into an `'b tree reader`. In more fanciful terms, -the `tree_monadize` function builds plumbing that connects all of the -leaves of a tree into one connected monadic network; it threads the +something of type `'a` into an `'b reader`---this is a function of the same type that you could bind an `'a reader` to---and I'll show you how to +turn an `'a tree` into an `'b tree reader`. That is, if you show me how to do this: + + ------------ + 1 ---> | 1 | + ------------ + +then I'll give you back the ability to do this: + + ____________ + . | . | + __|___ ---> | __|___ | + | | | | | | + 1 2 | 1 2 | + ------------ + +And how will that boxed tree behave? Whatever actions you perform on it will be transmitted down to corresponding operations on its leaves. For instance, our `int reader` expects an `int -> int` environment. If supplying environment `e` to our `int reader` doubles the contained `int`: + + ------------ + 1 ---> | 1 | applied to e ~~> 2 + ------------ + +Then we can expect that supplying it to our `int tree reader` will double all the leaves: + + ____________ + . | . | . + __|___ ---> | __|___ | applied to e ~~> __|___ + | | | | | | | | + 1 2 | 1 2 | 2 4 + ------------ + +In more fanciful terms, the `tree_monadize` function builds plumbing that connects all of the leaves of a tree into one connected monadic network; it threads the `'b reader` monad through the original tree's leaves. # tree_monadize int_readerize t1 double;; @@ -161,7 +187,7 @@ result: - : int tree = Node (Node (Leaf 4, Leaf 9), Node (Leaf 25, Node (Leaf 49, Leaf 121))) -Now that we have a tree transformer that accepts a reader monad as a +Now that we have a tree transformer that accepts a *reader* monad as a parameter, we can see what it would take to swap in a different monad. For instance, we can use a state monad to count the number of leaves in @@ -184,7 +210,7 @@ modification whatsoever, except for replacing the (parametric) type Then we can count the number of leaves in the tree: - # tree_monadize (fun a s -> (a, s+1)) t1 0;; + # tree_monadize (fun a -> fun s -> (a, s+1)) t1 0;; - : int tree * int = (Node (Node (Leaf 2, Leaf 3), Node (Leaf 5, Node (Leaf 7, Leaf 11))), 5) @@ -199,6 +225,7 @@ Then we can count the number of leaves in the tree: | | 7 11 +Why does this work? Because the operation `fun a -> fun s -> (a, s+1)` takes an `int` and wraps it in an `int state` monadic box that increments the state. When we give that same operations to our `tree_monadize` function, it then wraps an `int tree` in a box, one that does the same state-incrementing for each of its leaves. One more revealing example before getting down to business: replacing `state` everywhere in `tree_monadize` with `list` gives us @@ -211,7 +238,8 @@ One more revealing example before getting down to business: replacing Unlike the previous cases, instead of turning a tree into a function from some input to a result, this transformer replaces each `int` with -a list of `int`'s. +a list of `int`'s. We might also have done this with a Reader Monad, though then our environments would need to be of type `int -> int list`. Experiment with what happens if you supply the `tree_monadize` based on the List Monad an operation like `fun -> [ i; [2*i; 3*i] ]`. Use small trees for your experiment. +