Merge branch 'master' of ssh://server.philosophy.fas.nyu.edu/Users/lambda/lambda
authorChris Barker <barker@omega.(none)>
Mon, 6 Dec 2010 12:21:36 +0000 (07:21 -0500)
committerChris Barker <barker@omega.(none)>
Mon, 6 Dec 2010 12:21:36 +0000 (07:21 -0500)
assignment6.mdwn
assignment8.mdwn
code/tree_monadize.ml
hints/assignment_6_commentary.mdwn [new file with mode: 0644]
index.mdwn
new_stuff.mdwn
translating_between_OCaml_Scheme_and_Haskell.mdwn
week7.mdwn

index 5f8c1b6..a31cbf8 100644 (file)
@@ -24,6 +24,9 @@ your solution).
      You'll need to define a computation monad type, unit, bind, and lift2.
 We encourage you to consider this hint: [[hints/Assignment 6 Hint 1]].
 
+       See our [commentary](/hints/assignment_6_commentary) on your solutions.
+
+
 2. Prove that your monad satisfies the monad laws.  First, give
 examples illustrating specific cases in which the monad laws are
 obeyed, then explain (briefly, not exhaustively) why the laws hold in
index 256b2ce..f09556f 100644 (file)
@@ -45,9 +45,9 @@
                                                | Some z' -> Some (move_botleft z')
                                        (* return saved label *)
                                        in Some current
+                                   )
                                | None -> (* we've finished enumerating the fringe *)
                                        None
-                               )
                        (* return the next_leaf function *)
                        in next_leaf
                        ;;
@@ -64,7 +64,7 @@
                        ;;
 
 
-2.     Here's another implementation of the same-fringe function, in Scheme. It's taken from <http://c2.com/cgi/wiki?SameFringeProblem>. It uses thunks to delay the evaluation of code that computes the tail of a list of a tree's fringe. It also involves passing continuations (`tailk`s) as arguments. Your assignment is to fill in the blanks in the code, **and also to supply comments to the code,** to explain what every significant piece is doing. Don't forget to supply the comments, this is an important part of the assignment.
+2.     Here's another implementation of the same-fringe function, in Scheme. It's taken from <http://c2.com/cgi/wiki?SameFringeProblem>. It uses thunks to delay the evaluation of code that computes the tail of a list of a tree's fringe. It also involves passing "the rest of the enumeration of the fringe" as a thunk argument (`tail-thunk` below). Your assignment is to fill in the blanks in the code, **and also to supply comments to the code,** to explain what every significant piece is doing. Don't forget to supply the comments, this is an important part of the assignment.
 
        This code uses Scheme's `cond` construct. That works like this;
 
        Here is the implementation:
 
                (define (lazy-flatten tree)
-                 (letrec ([helper (lambda (tree tailk)
+                 (letrec ([helper (lambda (tree tail-thunk)
                                  (cond
                                    [(pair? tree)
-                                     (helper (car tree) (lambda () (helper _____ tailk)))]
-                                   [else (cons tree tailk)]))])
+                                     (helper (car tree) (lambda () (helper _____ tail-thunk)))]
+                                   [else (cons tree tail-thunk)]))])
                    (helper tree (lambda () _____))))
                
                (define (stream-equal? stream1 stream2)
index 16d0610..7c419a3 100644 (file)
@@ -1,6 +1,65 @@
 (*
  * tree_monadize.ml
  *
+ * If you've got some block of code that uses `unit`s and `bind`s, and you
+ * want to interpret it alternately using this monad, that monad, or another
+ * monad, you can use OCaml's module system. You'd write your code like this:
+ *) 
+
+module Reader_monad = struct
+    (* change this to suit your needs *)
+    type env = int -> int;;
+
+    type 'a monad = env -> 'a;;
+    let unit a : 'a monad = fun e -> a;;
+    let bind (u : 'a monad) (f : 'a -> 'b monad) : 'b monad =
+      fun e -> f (u e) e;;
+end
+
+module State_monad = struct
+    (* change this to suit your needs *)
+    type store = int;;
+
+    type 'a monad = store -> 'a * store;;
+    let unit a : 'a monad  = fun s -> (a, s);;
+    let bind (u : 'a monad) (f : 'a -> 'b monad) : 'b monad =
+      fun s -> (let (a, s') = u s in (f a) s');;
+end
+
+module List_monad = struct
+    type 'a monad = 'a list;;
+    let unit a : 'a monad = [a];;
+    let bind (u: 'a monad) (f : 'a -> 'b monad) : 'b monad =
+      List.concat(List.map f u);;
+end
+
+(*
+ * Then you can replace code that looks like this:
+ *     ... reader_bind ...
+ * with code that looks like this:
+ *     ... Reader_monad.bind ...
+ * and the latter can be reformulated like this:
+ *     let open Reader_monad in ... bind ...
+ * or equivalently, like this:
+ *     Reader_monad.(... bind ...)
+ * Then you can use literally the same `... bind ...` code when writing instead:
+ *     State_monad.(... bind ...)
+ *)
+
+(* That's great, however it still requires us to repeat the
+ * `... bind ...` code every time we want to change which monad we're working
+ * with. Shouldn't there be a way to _parameterize_ the `... bind ...` code
+ * on a monad, so that we only have to write the `... bind ...` code once,
+ * but can invoke it alternately with the Reader_monad supplied as an
+ * argument, or the State_monad, or another?
+ *
+ * There is a way to do this, but it requires putting the `... bind ...` code in
+ * its own module, and making that module parameterized on some X_monad
+ * module. Also we have to explicitly declare what commonality we're expecting
+ * from X_monad modules we're going to use as parameters. We'll explain how to
+ * do this in a moment.
+ *
+ * As preparation, a general observation:
  * 'a and so on are type variables in OCaml; they stand for arbitrary types.
  * What if you want a variable for a type constructor? For example, you want to
  * generalize this pattern:
  *      module T_maker(
  *      (* A sig...end block specifies the type of a module
  *       * What we're doing here is specifying the type of the 
        * module parameter that will choose
        * whether b = list or b = option or b = reader...
        * This module parameter may supply values as well as types *)
- *      Parm: sig
*       * module parameter that will choose
*       * whether b = list or b = option or b = reader...
*       * This module parameter may supply values as well as types *)
+ *      : sig
  *          type ('a) b
  *      end
  *      ) = 
  *      (* A struct...end block gives a module value
        * What we're doing here is building a new module that makes
        * use of the module that was supplied as Parm *)
*       * What we're doing here is building a new module that makes
*       * use of the module that was supplied as X *)
  *      struct
- *          type ('a) t = 'a -> ('a) Parm.b
+ *          type ('a) t = 'a -> ('a) X.b
  *      end
  * And here's how you'd use it:
  *      module T_list = T_maker(struct type 'a b = 'a list end);;
@@ -38,7 +97,9 @@
  *      type 'a t2 = 'a T_option.t;;
  *      (* and so on *)
  *
- * I know, it seems unnecessarily complicated.
+ * I know, it seems unnecessarily complicated. Nonetheless, that's how it
+ * works. And that is also the technique we'll use to make our
+ * `... bind ...` code parametric on some X_monad module.
  *)
 
 type 'a tree = Leaf of 'a | Node of ('a tree) * ('a tree);;
@@ -52,99 +113,68 @@ let t1 = Node
                 (Leaf 7, Leaf 11)));;
 
 
-module Tree_monadizer(Parm : sig
+module Tree_monadizer(X : sig
   (* the module we're using as a parameter has to supply function values
    * for unit and bind, as well as a monadic type constructor m *)
-  type 'a m
-  val unit : 'a -> 'a m
-  val bind : 'a m -> ('a -> 'b m) -> 'b m
+  type 'a monad
+  val unit : 'a -> 'a monad
+  val bind : 'a monad -> ('a -> 'b monad) -> 'b monad
 end) = struct
-  let rec monadize (f: 'a -> 'b Parm.m) (t: 'a tree) : 'b tree Parm.m =
+  let rec monadize (f: 'a -> 'b X.monad) (t: 'a tree) : 'b tree X.monad =
     match t with
-    | Leaf a -> Parm.bind (f a) (fun b -> Parm.unit (Leaf b))
+    | Leaf a -> X.bind (f a) (fun b -> X.unit (Leaf b))
     | Node(l, r) ->
-        Parm.bind (monadize f l) (fun l' ->
-          Parm.bind (monadize f r) (fun r' ->
-            Parm.unit (Node (l', r'))))
+        X.bind (monadize f l) (fun l' ->
+          X.bind (monadize f r) (fun r' ->
+            X.unit (Node (l', r'))))
 end;;
 
 
-type env = int -> int;;
-
-type 'a reader = env -> 'a;;
-let reader_unit a : 'a reader = fun e -> a;;
-let reader_bind (u : 'a reader) (f : 'a -> 'b reader) : 'b reader =
-  fun e -> f (u e) e;;
-
 (* Now we supply the Reader monad as a parameter to Tree_monadizer.
  * We'll get back a module TreeReader that contains a single value,
  * the monadize function specialized to the Reader monad *)
-module TreeReader = Tree_monadizer(struct
-  type 'a m = 'a reader
-  let unit = reader_unit
-  let bind = reader_bind
-end);;
-
+module TreeReader = Tree_monadizer(Reader_monad);;
 
-type store = int;;
-
-type 'a state = store -> 'a * store;;
-let state_unit a : 'a state  = fun s -> (a, s);;
-let state_bind (u : 'a state) (f : 'a -> 'b state) : 'b state =
-  fun s -> (let (a, s') = u s in (f a) s');;
 
 (* Make a TreeState module containing monadize specialized to the State monad *)
-module TreeState =  Tree_monadizer(struct
-  type 'a m = 'a state
-  let unit = state_unit
-  let bind = state_bind
-end);;
-
+module TreeState =  Tree_monadizer(State_monad);;
 
-let list_unit a = [a];;
-let list_bind (u: 'a list) (f : 'a -> 'b list) : 'b list =
-  List.concat(List.map f u);;
 
 (* Make a TreeList module containing monadize specialized to the List monad *)
-module TreeList =  Tree_monadizer(struct
-  type 'a m = 'a list
-  let unit = list_unit
-  let bind = list_bind
-end);;
+module TreeList =  Tree_monadizer(List_monad);;
+
 
+(* The Continuation monad is a bit more complicated *)
+module Continuation_monad = struct
+    type ('a,'r) monad = ('a -> 'r) -> 'r;;
+    let unit a : ('a,'r) monad = fun k -> k a;;
+    let bind (u: ('a,'r) monad) (f: 'a -> ('b,'r) monad) : ('b,'r) monad =
+      fun k -> u (fun a -> f a k);;
+end
 
-(* since the Continuation monad is parameterized on two types---it's
- * ('a,'r) cont not ('a) cont---we can't match the type ('a) m that
+(* Since the Continuation monad is parameterized on two types---it's
+ * ('a,'r) cont not ('a) cont---we can't match the type ('a) monad that
  * Tree_monadizer expects in its parameter. So we have to make a different
- * Tree_monadizer2 that takes a ('a,'x) m type constructor in its
+ * Tree_monadizer2 that takes a ('a,'z) monad type constructor in its
  * parameter instead *)
-module Tree_monadizer2(Parm : sig
-  type ('a,'x) m
-  val unit : 'a -> ('a,'x) m
-  val bind : ('a,'x) m -> ('a -> ('b,'x) m) -> ('b,'x) m
+module Tree_monadizer2(X : sig
+  type ('a,'z) monad
+  val unit : 'a -> ('a,'z) monad
+  val bind : ('a,'z) monad -> ('a -> ('b,'z) monad) -> ('b,'z) monad
 end) = struct
   (* the body of the monadize function is the same; the only difference is in
    * the types *)
-  let rec monadize (f: 'a -> ('b,'x) Parm.m) (t: 'a tree) : ('b tree,'x) Parm.m =
+  let rec monadize (f: 'a -> ('b,'x) X.monad) (t: 'a tree) : ('b tree,'x) X.monad =
     match t with
-    | Leaf a -> Parm.bind (f a) (fun b -> Parm.unit (Leaf b))
+    | Leaf a -> X.bind (f a) (fun b -> X.unit (Leaf b))
     | Node(l, r) ->
-        Parm.bind (monadize f l) (fun l' ->
-          Parm.bind (monadize f r) (fun r' ->
-            Parm.unit (Node (l', r'))))
+        X.bind (monadize f l) (fun l' ->
+          X.bind (monadize f r) (fun r' ->
+            X.unit (Node (l', r'))))
 end;;
 
-type ('a,'r) cont = ('a -> 'r) -> 'r;;
-let cont_unit a : ('a,'r) cont = fun k -> k a;;
-let cont_bind (u: ('a,'r) cont) (f: 'a -> ('b,'r) cont) : ('b,'r) cont =
-  fun k -> u (fun a -> f a k);;
-
 (* Make a TreeCont module containing monadize specialized to the Cont monad *)
-module TreeCont =  Tree_monadizer2(struct
-  type ('a,'r) m = ('a,'r) cont
-  let unit = cont_unit
-  let bind = cont_bind
-end);;
+module TreeCont =  Tree_monadizer2(Continuation_monad);;
 
 
 
@@ -154,7 +184,7 @@ end);;
  *)
 
 
-let int_readerize : int -> int reader =
+let int_readerize : int -> int Reader_monad.monad =
   fun (a : int) -> fun (env : int -> int) -> env a;;
 
 (* int_readerize takes an int and returns a Reader monad that
@@ -166,13 +196,43 @@ let int_readerize : int -> int reader =
 let env = fun i -> i + i in
 TreeReader.monadize int_readerize t1 env;;
 
+(* You can also avoid declaring a separate toplevel TreeReader module
+ * (or even a separate Reader_monad module) by ysing one of these forms:
+ *     ...
+ *     let module T = Tree_monadizer(Reader_monad) in
+ *     T.monadize int_readerize t1 env;;
+ * or:
+ *     ...
+ *     let env = fun i -> i + i in
+ *     let module Monad = struct
+ *       type env = int -> int;;
+ *       type 'a monad = env -> 'a;;
+ *       let unit a : 'a monad = fun e -> a;;
+ *       let bind (u : 'a monad) (f : 'a -> 'b monad) : 'b monad =
+ *         fun e -> f (u e) e;;
+ *     end in
+ *     let module T = Tree_monadizer(Monad) in
+ *     T.monadize int_readerize t1 env;;
+ * or:
+ *     ...
+ *     let module T = Tree_monadizer(struct
+ *       type env = int -> int;;
+ *       type 'a monad = env -> 'a;;
+ *       let unit a : 'a monad = fun e -> a;;
+ *       let bind (u : 'a monad) (f : 'a -> 'b monad) : 'b monad =
+ *         fun e -> f (u e) e;;
+ *     end) in
+ *     T.monadize int_readerize t1 env;;
+ *)
+
+
 (* square each leaf *)
 let env = fun i -> i * i in
 TreeReader.monadize int_readerize t1 env;;
 
 
 
-let incrementer : int -> int state =
+let incrementer : int -> int State_monad.monad =
   fun (a : int) -> fun s -> (a, s+1);;
 
 (* incrementer takes an 'a and returns it wrapped in a
@@ -191,7 +251,7 @@ TreeList.monadize (fun i -> [ [i;i*i] ]) t1;;
 
 (* do nothing *)
 let initial_continuation = fun t -> t in
-TreeCont.monadize cont_unit t1 initial_continuation;;
+TreeCont.monadize Continuation_monad.unit t1 initial_continuation;;
 
 (* convert tree to list of leaves *)
 let initial_continuation = fun t -> [] in
@@ -209,27 +269,3 @@ TreeCont.monadize (fun a k -> k [a; a*a]) t1 initial_continuation;;
 let initial_continuation = fun t -> 0 in
 TreeCont.monadize (fun a k -> 1 + k a) t1 initial_continuation;;
 
-
-
-
-(* Tree monad *)
-
-(* type 'a tree defined above *)
-let tree_unit (a: 'a) : 'a tree = Leaf a;;
-let rec tree_bind (u : 'a tree) (f : 'a -> 'b tree) : 'b tree =
-    match u with
-    | Leaf a -> f a
-    | Node (l, r) -> Node (tree_bind l f, tree_bind r f);;
-
-type 'a, treeTC_reader =
-               'a tree reader;;
-
-       let unit (a: 'a) : 'a tree reader =
-               M.unit (Leaf a);;
-
-       let rec bind (u : ('a, M) tree) (f : 'a -> ('b, M) tree) : ('b, M) tree =
-           match u with
-           | Leaf a -> M.bind (f a) (fun b -> M.unit (Leaf b))
-           | Node (l, r) -> M.bind (bind l f) (fun l' ->
-                                                       M.bind (bind r f) (fun r' ->
-                                                               M.unit (Node (l', r'));;
diff --git a/hints/assignment_6_commentary.mdwn b/hints/assignment_6_commentary.mdwn
new file mode 100644 (file)
index 0000000..d58a7fc
--- /dev/null
@@ -0,0 +1,70 @@
+Many of you offered a solution along the following lines:
+
+       type 'a state = int -> 'a * int;;
+       let unit (a : 'a) : 'a state =
+         fun count -> (a, count);;
+       let bind (u : 'a state) (f : 'a -> 'b state ) : 'b state =
+         fun count -> let (a, count') = u count in f a count';;
+
+       (* Looks good so far, now how are we going to increment the count? *)
+
+       let lift2 (f : 'a -> 'b -> 'c) (u : 'a state) (v : 'b state) : 'c state =
+         bind u (fun x ->
+           bind v (fun y ->
+             fun count -> (f x y, count + 1)));;
+
+Whoops. That will work for the cases you're probably thinking about. For instance, you can do:
+
+       lift2 (+) (unit 1) (lift2 (+) (unit 2) (unit 3));;
+
+and you'll get back an `int state` that when applied to a starting count of `0` yields the result `(6, 2)`---that is, the result of the computation was 6 and the number of operations was 2.
+
+However, there are several problems here. First off, you shouldn't name your function `lift2`, because we're using that name for a function that's interdefinable with `bind` in a specific way. Our canonical `lift2` function is:
+
+       let lift2 (f : 'a -> 'b -> 'c) (u : 'a state) (v : 'b state) : 'c state =
+         bind u (fun x ->
+           bind v (fun y ->
+             unit (f x y)));;
+
+(Haskell calls this `liftM2`, and calls our `lift` either `liftM` or `mapM`.)
+
+OK, so then you might call your function `loft2` instead. So what?
+
+The remaining problem is more subtle. It's that your solution isn't very modular. You've crafted a tool `loft2` that fuses the operation of incrementing the count with the behavior of our `lift2`. What if we needed to deal with some unary functions as well? Then you'd need a `loft1`. What if we need to deal with some functions that are already monadic? Then you'd need a tool that fuses the count-incrementing with the behavior of `bind`. And so on.
+
+It's nicer to just create a little module that does the count-incrementing, and then use that together with the pre-existing apparatus of `bind` and (our canonical) `lift` and `lift2`. You could do that like this:
+
+       let tick (a : 'a) : 'a state =
+         fun count -> (a, count + 1);;
+       
+       let result1 =
+         bind
+           (lift2 (+)
+             (unit 1)
+             (bind
+               (lift2 (+)
+                 (unit 2)
+                 (unit 3))
+               tick))
+           tick;;
+       
+       result1 0;; (* evaluates to (6, 2) *)
+
+Or like this:
+
+       let tock : unit state =
+         fun count -> ((), count + 1);;
+       
+       let result2 =
+         bind
+           tock
+           (fun _ -> lift2 (+)
+             (unit 1)
+             (bind
+               tock
+               (fun _ -> lift2 (+)
+                 (unit 2)
+                 (unit 3))));;
+       
+       result2 0;; (* evaluates to (6, 2) *)
+
index 0d7173b..0db0d06 100644 (file)
@@ -15,6 +15,8 @@ behind with the homework.  Don't.
 
 *      We've added a page on [[Translating between OCaml Scheme and Haskell]]
 
+*      We've added some [commentary](/hints/assignment_6_commentary) on some common issues in your solutions to [[Assignment6]].
+
 [[Older Announcements]]
 
 ##[[Lambda Evaluator]]##
index 037e2c6..f13e9fe 100644 (file)
@@ -9,3 +9,7 @@ Page for Chris and Jim to see what each other is working on, but hasn't necessar
 [[Curry-Howard]]
 
 [[Translating between OCaml Scheme and Haskell]]
+
+[Commentary](/hints/assignment_6_commentary) on some common issues in the solutions to [[Assignment6]].
+
+
index d0f7442..2e33b1a 100644 (file)
@@ -111,6 +111,9 @@ Additionally, the syntax of OCaml and SML is superficially much closer to Haskel
        But assuming you do manage to compile and install Oleg's library, here's how you'd use it in an OCaml session:
 
                #require "delimcc";; (* loading Oleg's library this way requires the findlib package *)
+                   (* if you don't have findlib, you'll need to start ocaml like
+                    * this instead: ocaml -I /path/to/directory/containing/delimcc delimcc.cma
+                    *)
                open Delimcc;; (* this lets you say e.g. new_prompt instead of Delimcc.new_prompt *)
                let p = new_prompt ();;
                let prompt thunk = push_prompt p thunk;;
@@ -183,15 +186,17 @@ We will however try to give some general advice about how to translate between O
                type Weight = Integer
                type Person = (Name, Address)    -- supposing types Name and Address to be declared elsewhere
 
-       then you can use a value of type `Integer` wherever a `Weight` is expected, and vice versa. `newtype` and `data` on the other hand, create genuinely new types. `newtype` is basically just an efficient version of `data` that you can use in special circumstances. `newtype` must always take one type argument and have one value constructor. For example:
+       then you can use a value of type `Integer` wherever a `Weight` is expected, and vice versa. <!-- `type` is allowed to be parameterized -->
+
+       `newtype` and `data` on the other hand, create genuinely new types. `newtype` is basically just an efficient version of `data` that you can use in special circumstances. `newtype` must always take one type argument and have one value constructor. For example:
 
                newtype PersonalData a = PD a
 
        You could also say:
 
-               data PersonalData a = PD a
+               data PersonalData2 a = PD2 a
 
-       And `data` also allows multiple type arguments, and multiple variants and value constructors.
+       And `data` also allows multiple type arguments, and multiple variants and value constructors. <!-- Subtle difference: whereas `PersonalData a` is isomorphic to `a`, `PersonalData2 a` has an additional value, namely `PD2 _|_`. In a strict language, this is an additional type an expression can have, but it would not be a value. -->
 
        OCaml just uses the one keyword `type` for all of these purposes:
 
@@ -201,7 +206,7 @@ We will however try to give some general advice about how to translate between O
 
 *      When a type only has a single variant, as with PersonalData, Haskell programmers will often use the same name for both the type and the value constructor, like this:
 
-               data PersonalData a = PersonalData a
+               data PersonalData3 a = PersonalData3 a
 
        The interpreter can always tell from the context when you're using the type name and when you're using the value constructor.
 
@@ -673,6 +678,7 @@ Haskell has more built-in support for monads, but one can define the monads one
        For more details, see:
 
        *       [Haskell wikibook on do-notation](http://en.wikibooks.org/wiki/Haskell/do_Notation)
+       *       [Yet Another Haskell Tutorial on do-notation](http://en.wikibooks.org/wiki/Haskell/YAHT/Monads#Do_Notation)
        *       [Do-notation considered harmful](http://www.haskell.org/haskellwiki/Do_notation_considered_harmful)
 
 *      If you like the Haskell do-notation, there's [a library](http://www.cas.mcmaster.ca/~carette/pa_monad/) you can compile and install to let you use something similar in OCaml.
index 2111ed8..35deeb2 100644 (file)
@@ -462,9 +462,11 @@ Theory](/advanced_topics/monads_in_category_theory).
 See also:
 
 *      [Haskell wikibook on Monad Laws](http://www.haskell.org/haskellwiki/Monad_Laws).
+*      [Yet Another Haskell Tutorial on Monad Laws](http://en.wikibooks.org/wiki/Haskell/YAHT/Monads#Definition)
 *      [Haskell wikibook on Understanding Monads](http://en.wikibooks.org/wiki/Haskell/Understanding_monads)
 *      [Haskell wikibook on Advanced Monads](http://en.wikibooks.org/wiki/Haskell/Advanced_monads)
 *      [Haskell wikibook on do-notation](http://en.wikibooks.org/wiki/Haskell/do_Notation)
+*      [Yet Another Haskell Tutorial on do-notation](http://en.wikibooks.org/wiki/Haskell/YAHT/Monads#Do_Notation)
 
 
 Here are some papers that introduced monads into functional programming: