author Jim Pryor Mon, 1 Nov 2010 04:44:57 +0000 (00:44 -0400) committer Jim Pryor Mon, 1 Nov 2010 04:44:57 +0000 (00:44 -0400)
Signed-off-by: Jim Pryor <profjim@jimpryor.net>
 week7.mdwn patch | blob | history

index 8b74a1c..351a6d9 100644 (file)
@@ -9,10 +9,10 @@ week 6 [[Towards Monads]].
In those notes, we saw a way to separate thinking about error
conditions (such as trying to divide by zero) from thinking about
normal arithmetic computations.  We did this by making use of the
-option monad: in each place where we had something of type `int`, we
+`option` type: in each place where we had something of type `int`, we
put instead something of type `int option`, which is a sum type
-consisting either of just an integer, or else some special value which
-we could interpret as signaling that something had gone wrong.
+consisting either of one choice with an `int` payload, or else a `None`
+choice which we interpret as  signaling that something has gone wrong.

The goal was to make normal computing as convenient as possible: when
@@ -39,16 +39,15 @@ material that otherwise would trigger a presupposition violation; but,
not surprisingly, these refinements will require some more
sophisticated techniques than the super-simple option monad.]

-So what exactly is a monad?  As usual, we're not going to be pedantic
-about it, but for our purposes, we can consider a monad to be a system
+So what exactly is a monad?  We can consider a monad to be a system
that provides at least the following three elements:

*      A complex type that's built around some more basic type. Usually
-       it will be polymorphic, and so can apply to different basic types.
+       the complex type will be polymorphic, and so can apply to different basic types.
In our division example, the polymorphism of the `'a option` type
provides a way of building an option out of any other type of object.
-       People often use a container metaphor: if `x` has type `int option`,
-       then `x` is a box that (may) contain an integer.
+       People often use a container metaphor: if `u` has type `int option`,
+       then `u` is a box that (may) contain an integer.

type 'a option = None | Some of 'a;;

@@ -90,14 +89,14 @@ that provides at least the following three elements:
let bind u f = match u with None -> None | Some x -> f x;;
val bind : 'a option -> ('a -> 'b option) -> 'b option = <fun>

-       Note the type. `bind` takes two arguments: first, a monadic "box"
-       (in this case, an 'a option); and second, a function from
+       Note the type: `bind` takes two arguments: first, a monadic box
+       (in this case, an `'a option`); and second, a function from
-       value: in this case, a 'b option (you can start with, e.g., int options
-       and end with bool options).
+       value: in this case, a `'b option` (you can start with, e.g., `int option`s
+       and end with `bool option`s).

-       Intuitively, the interpretation of what `bind` does is like this:
-       the first argument is a monadic value u, which
+       Intuitively, the interpretation of what `bind` does is this:
+       the first argument is a monadic value `u`, which
evaluates to a box that (maybe) contains some ordinary value, call it `x`.
Then the second argument uses `x` to compute a new monadic
value.  Conceptually, then, we have
@@ -105,41 +104,39 @@ that provides at least the following three elements:
let bind u f = (let x = unbox u in f x);;

The guts of the definition of the `bind` operation amount to
-       specifying how to unbox the monadic value `u`.  In the bind
-       opertor for the option monad, we unboxed the option monad by
-       matching the monadic value `u` with `Some x`---whenever `u`
+       specifying how to unbox the monadic value `u`.  In the `bind`
+       opertor for the option monad, we unboxed the monadic value by
+       matching it with the pattern `Some x`---whenever `u`
happened to be a box containing an integer `x`, this allowed us to
get our hands on that `x` and feed it to `f`.

-       If the monadic box didn't contain any ordinary value, then
-       we just pass through the empty box unaltered.
+       If the monadic box didn't contain any ordinary value,
+       we instead pass through the empty box unaltered.

In a more complicated case, like our whimsical "singing box" example
from before, if the monadic value happened to be a singing box
containing an integer `x`, then the `bind` operation would probably
be defined so as to make sure that the result of `f x` was also
-       a singing box. If `f` also inserted a song, you'd have to decide
+       a singing box. If `f` also wanted to insert a song, you'd have to decide
whether both songs would be carried through, or only one of them.

There is no single `bind` function that dictates how this must go.
For each new monadic type, this has to be worked out in an
useful way.

-So the "option/maybe monad" consists of the polymorphic option type, the
-unit/return function, and the bind function.  With the option monad, we can
-think of the "safe division" operation
+So the "option/maybe monad" consists of the polymorphic `option` type, the
+`unit`/return function, and the `bind` function.  With the option monad, we can
+think of the "safe division" operation:

-<pre>
-# let divide' num den = if den = 0 then None else Some (num/den);;
-val divide' : int -> int -> int option = <fun>
-</pre>
+       # let safe_divide num den = if 0 = den then None else Some (num/den);;
+       val safe_divide : int -> int -> int option = <fun>

as basically a function from two integers to an integer, except with
this little bit of option plumbing on the side.

A note on notation: Haskell uses the infix operator `>>=` to stand
for `bind`. Chris really hates that symbol.  Following Wadler, he prefers to
-use an infix five-pointed star, or on a keyboard, `*`. Jim on the other hand
+use an infix five-pointed star &#8902;, or on a keyboard, `*`. Jim on the other hand
thinks `>>=` is what the literature uses and students won't be able to
avoid it. Moreover, although &#8902; is OK (though not a convention that's been picked up), overloading the multiplication symbol invites its own confusion
and Jim feels very uneasy about that. If not `>>=` then we should use
@@ -175,39 +172,39 @@ As we proceed, we'll be seeing a variety of other monad systems. For example, an

# type 'a list

-The unit/return operation is:
+The `unit`/return operation is:

# let unit x = [x];;
val unit : 'a -> 'a list = <fun>

-That is, the simplest way to lift an 'a into an 'a list is just to make a
-singleton list of that 'a. Finally, the bind operation is:
+That is, the simplest way to lift an `'a` into an `'a list` is just to make a
+singleton list of that `'a`. Finally, the `bind` operation is:

# let bind u f = List.concat (List.map f u);;
val bind : 'a list -> ('a -> 'b list) -> 'b list = <fun>

-What's going on here? Well, consider (List.map f u) first. This goes through all
-the members of the list u. There may be just a single member, if `u = unit a`
-for some a. Or on the other hand, there may be no members, or many members. In
-any case, we go through them in turn and feed them to f. Anything that gets fed
-to f will be an 'a. f takes those values, and for each one, returns a 'b list.
+What's going on here? Well, consider `List.map f u` first. This goes through all
+the members of the list `u`. There may be just a single member, if `u = unit x`
+for some `x`. Or on the other hand, there may be no members, or many members. In
+any case, we go through them in turn and feed them to `f`. Anything that gets fed
+to `f` will be an `'a`. `f` takes those values, and for each one, returns a `'b list`.
For example, it might return a list of all that value's divisors. Then we'll
-have a bunch of 'b lists. The surrounding `List.concat ( )` converts that bunch
-of 'b lists into a single 'b list:
+have a bunch of `'b list`s. The surrounding `List.concat ( )` converts that bunch
+of `'b list`s into a single `'b list`:

# List.concat [; [1;2]; [1;3]; [1;2;4]]
- : int list = [1; 1; 2; 1; 3; 1; 2; 4]

monadic system, there has to be a specification of the complex monad type,
-which will be parameterized on some simpler type 'a, and the unit/return
-operation, and the bind operation. These will be different for different
+which will be parameterized on some simpler type `'a`, and the `unit`/return
+operation, and the `bind` operation. These will be different for different

Many monadic systems will also define special-purpose operations that only make
sense for that system.

-Although the unit and bind operation are defined differently for different
+Although the `unit` and `bind` operation are defined differently for different
monadic systems, there are some general rules they always have to follow.

@@ -219,30 +216,28 @@ them from hurting the people that use them or themselves.

*      **Left identity: unit is a left identity for the bind operation.**
That is, for all `f:'a -> 'a m`, where `'a m` is a monadic
-       value, we have `(unit x) * f == f x`.  For instance, `unit` is itself
+       type, we have `(unit x) * f == f x`.  For instance, `unit` is itself
a function of type `'a -> 'a m`, so we can use it for `f`:

-<pre>
-# let ( * ) u f = match u with None -> None | Some x -> f x;;
-val ( * ) : 'a option -> ('a -> 'b option) -> 'b option = <fun>
-# let unit x = Some x;;
-val unit : 'a -> 'a option = <fun>
-
-# unit 2;;
-- : int option = Some 2
-# unit 2 * unit;;
-- : int option = Some 2
-
-# divide 6 2;;
-- : int option = Some 3
-# unit 2 * divide 6;;
-- : int option = Some 3
-
-# divide 6 0;;
-- : int option = None
-# unit 0 * divide 6;;
-- : int option = None
-</pre>
+               # let ( * ) u f = match u with None -> None | Some x -> f x;;
+               val ( * ) : 'a option -> ('a -> 'b option) -> 'b option = <fun>
+               # let unit x = Some x;;
+               val unit : 'a -> 'a option = <fun>
+
+               # unit 2;;
+               - : int option = Some 2
+               # unit 2 * unit;;
+               - : int option = Some 2
+
+               # divide 6 2;;
+               - : int option = Some 3
+               # unit 2 * divide 6;;
+               - : int option = Some 3
+
+               # divide 6 0;;
+               - : int option = None
+               # unit 0 * divide 6;;
+               - : int option = None

The parentheses is the magic for telling OCaml that the
function to be defined (in this case, the name of the function
@@ -254,30 +249,28 @@ is `*`, pronounced "bind") is an infix operator, so we write
(u * f) * g == u * (fun x -> f x * g)

If you don't understand why the lambda form is necessary (the "fun
-       x" part), you need to look again at the type of bind.
+       x" part), you need to look again at the type of `bind`.

Some examples of associativity in the option monad:

-<pre>
-# Some 3 * unit * unit;;
-- : int option = Some 3
-# Some 3 * (fun x -> unit x * unit);;
-- : int option = Some 3
-
-# Some 3 * divide 6 * divide 2;;
-- : int option = Some 1
-# Some 3 * (fun x -> divide 6 x * divide 2);;
-- : int option = Some 1
-
-# Some 3 * divide 2 * divide 6;;
-- : int option = None
-# Some 3 * (fun x -> divide 2 x * divide 6);;
-- : int option = None
-</pre>
+               # Some 3 * unit * unit;;
+               - : int option = Some 3
+               # Some 3 * (fun x -> unit x * unit);;
+               - : int option = Some 3
+
+               # Some 3 * divide 6 * divide 2;;
+               - : int option = Some 1
+               # Some 3 * (fun x -> divide 6 x * divide 2);;
+               - : int option = Some 1
+
+               # Some 3 * divide 2 * divide 6;;
+               - : int option = None
+               # Some 3 * (fun x -> divide 2 x * divide 6);;
+               - : int option = None

Of course, associativity must hold for arbitrary functions of
type `'a -> 'a m`, where `m` is the monad type.  It's easy to
-convince yourself that the bind operation for the option monad
+convince yourself that the `bind` operation for the option monad
obeys associativity by dividing the inputs into cases: if `u`
matches `None`, both computations will result in `None`; if
`u` matches `Some x`, and `f x` evalutes to `None`, then both
@@ -288,12 +281,10 @@ to `g y`.
*      **Right identity: unit is a right identity for bind.**  That is,
`u * unit == u` for all monad objects `u`.  For instance,

-<pre>
-# Some 3 * unit;;
-- : int option = Some 3
-# None * unit;;
-- : 'a option = None
-</pre>
+               # Some 3 * unit;;
+               - : int option = Some 3
+               # None * unit;;
+               - : 'a option = None

@@ -308,7 +299,9 @@ serving as the left and right identity.  That is, temporarily using
presented here, a monad is not exactly a monoid, because (unlike the
arguments of a monoid operation) the two arguments of the bind are of
different types.  But it's possible to make the connection between
+monads and monoids much closer. This is discussed in [Monads in Category

Here are some papers that introduced monads into functional programming:

@@ -326,8 +319,8 @@ invited talk, *19'th Symposium on Principles of Programming Languages*, ACM Pres

There's a long list of monad tutorials on the [[Offsite Reading]] page. Skimming the titles makes me laugh.

-In the presentation we gave above, which is the usual one in functional programming, we took unit/return and bind as the primitive operations. From these a number of other general monad operations can be derived. It's also possible to take some of the others as primitive, and define for example bind in terms of them. This is discussed some in the [[Advanced Notes/Monads in Category Theory]].
-
+In the presentation we gave above---which follows the functional programming conventions---we took `unit`/return and `bind` as the primitive operations. From these a number of other general monad operations can be derived. It's also possible to take some of the others as primitive. The [Monads in Category

Here are some of the specifics. You don't have to master these; they're collected here for your reference.

@@ -350,7 +343,7 @@ The `lift` operation we asked you to define for last week's homework is a common
# let even x = (x mod 2 = 0);;
val g : int -> bool = <fun>

-`even` has the type int -> bool. Now what if we want to convert it into an operation on the option/maybe monad?
+`even` has the type `int -> bool`. Now what if we want to convert it into an operation on the option/maybe monad?

# let lift g = fun u -> bind u (fun x -> Some (g x));;
val lift : ('a -> 'b) -> 'a option -> 'b option = <fun>
@@ -363,20 +356,19 @@ also define a lift operation for binary functions:

`lift (+)` will now be a function from `int option`s  and `int option`s to `int option`s. This should look familiar to those who did the homework.

-The `lift` operation (just `lift`, not `lift2`) is sometimes also called the `map` operation. (In Haskell, they say `fmap` or `<\$>`.) And indeed when we're working with list monad, `lift f` is exactly `List.map f`!
+The `lift` operation (just `lift`, not `lift2`) is sometimes also called the `map` operation. (In Haskell, they say `fmap` or `<\$>`.) And indeed when we're working with the list monad, `lift f` is exactly `List.map f`!

-Wherever we have a well-defined monad, we can define a lift/map operation for that monad. The examples above used `Some (g x)` and so on; in the general case we'd use `unit (g x)`, using the pre-defined `unit` operation for the monad we're working with.
+Wherever we have a well-defined monad, we can define a lift/map operation for that monad. The examples above used `Some (g x)` and so on; in the general case we'd use `unit (g x)`, using the specific `unit` operation for the monad we're working with.

In general, any lift/map operation can be relied on to satisfy these laws:

* lift id = id
* lift (compose f g) = compose (lift f) (lift g)

-where id is `fun x -> x` and `compose a b` is `fun x -> a (b x)`. If you think about the special case of the map operation on lists, this should make sense. `List.map id lst` should give you back `lst` again. And you'd expect these
+where `id` is `fun x -> x` and `compose f g` is `fun x -> f (g x)`. If you think about the special case of the map operation on lists, this should make sense. `List.map id lst` should give you back `lst` again. And you'd expect these
two computations to give the same result:

List.map (fun x -> f (g x)) lst
-
List.map f (List.map g lst)

Another general monad operation is called `ap` in Haskell---short for "apply." (They also use `<*>`, but who can remember that?) This works like this:
@@ -384,12 +376,12 @@ Another general monad operation is called `ap` in Haskell---short for "apply." (
ap [f] [x; y] = [f x; f y]
ap (Some f) (Some x) = Some (f x)

-and so on. Here are the laws that any ap operation can be relied on to satisfy:
+and so on. Here are the laws that any `ap` operation can be relied on to satisfy:

-       ap (unit id) v = v
+       ap (unit id) u = u
ap (ap (ap (return compose) u) v) w = ap u (ap v w)
ap (unit f) (unit x) = unit (f x)
-       ap u (unit y) = ap (unit (fun f -> f y)) u
+       ap u (unit x) = ap (unit (fun f -> f x)) u

Another general monad operation is called `join`. This is the operation that takes you from an iterated monad to a single monad. Remember when we were explaining the `bind` operation for the list monad, there was a step where
we went from:
@@ -520,14 +512,12 @@ Characters (characters in the computational sense, i.e., letters like
`'a'` and `'b'`, not Kaplanian characters) will model individuals, and
OCaml booleans will serve for truth values.

-<pre>
-type 'a intension = s -> 'a;;
-let unit x (w:s) = x;;
+       type 'a intension = s -> 'a;;
+       let unit x (w:s) = x;;

-let ann = unit 'a';;
-let bill = unit 'b';;
-let cam = unit 'c';;
-</pre>
+       let ann = unit 'a';;
+       let bill = unit 'b';;
+       let cam = unit 'c';;

In our monad, the intension of an extensional type `'a` is `s -> 'a`,
a function from worlds to extensions.  Our unit will be the constant
@@ -540,12 +530,10 @@ as an argument.

Let's test compliance with the left identity law:

-<pre>
-# let bind u f (w:s) = f (u w) w;;
-val bind : (s -> 'a) -> ('a -> s -> 'b) -> s -> 'b = <fun>
-# bind (unit 'a') unit 1;;
-- : char = 'a'
-</pre>
+       # let bind u f (w:s) = f (u w) w;;
+       val bind : (s -> 'a) -> ('a -> s -> 'b) -> s -> 'b = <fun>
+       # bind (unit 'a') unit 1;;
+       - : char = 'a'

We'll assume that this and the other laws always hold.

@@ -560,15 +548,13 @@ Then the way to evaluate an extensional sentence is to determine the
extension of the verb phrase, and then apply that extension to the
extension of the subject:

-<pre>
-let extapp fn arg w = fn w (arg w);;
+       let extapp fn arg w = fn w (arg w);;

-extapp left ann 1;;
-# - : bool = true
+       extapp left ann 1;;
+       # - : bool = true

-extapp left cam 2;;
-# - : bool = false
-</pre>
+       extapp left cam 2;;
+       # - : bool = false

`extapp` stands for "extensional function application".
So Ann left in world 1, but Cam didn't leave in world 2.
@@ -599,12 +585,10 @@ Just as we used bind to define a version of addition that interacted
with the option monad, we now use bind to intensionalize an
extensional verb:

-<pre>
-let lift pred w arg = bind arg (fun x w -> pred w x) w;;
+       let lift pred w arg = bind arg (fun x w -> pred w x) w;;

-intapp (lift left) ann 1;; (* true: Ann still left in world 1 *)
-intapp (lift left) cam 2;; (* false: Cam still didn't leave in world 2 *)
-</pre>
+       intapp (lift left) ann 1;; (* true: Ann still left in world 1 *)
+       intapp (lift left) cam 2;; (* false: Cam still didn't leave in world 2 *)

Because `bind` unwraps the intensionality of the argument, when the
lifted "left" receives an individual concept (e.g., `unit 'a'`) as
@@ -615,12 +599,10 @@ third meaning postulate.)

Likewise for extensional transitive predicates like "saw":

-<pre>
-let lift2 pred w arg1 arg2 =
-  bind arg1 (fun x -> bind arg2 (fun y w -> pred w x y)) w;;
-intapp (intapp (lift2 saw) bill) ann 1;;  (* true: Ann saw Bill in world 1 *)
-intapp (intapp (lift2 saw) bill) ann 2;;  (* false: No one saw anyone in world 2 *)
-</pre>
+       let lift2 pred w arg1 arg2 =
+         bind arg1 (fun x -> bind arg2 (fun y w -> pred w x y)) w;;
+       intapp (intapp (lift2 saw) bill) ann 1;;  (* true: Ann saw Bill in world 1 *)
+       intapp (intapp (lift2 saw) bill) ann 2;;  (* false: No one saw anyone in world 2 *)

Crucially, an intensional predicate does not use `bind` to consume its
arguments.  Attitude verbs like "thought" are intensional with respect
@@ -628,22 +610,18 @@ to their sentential complement, but extensional with respect to their
subject (as Montague noticed, almost all verbs in English are
extensional with respect to their subject; a possible exception is "appear"):

-<pre>
-let think (w:s) (p:s->t) (x:e) =
-  match (x, p 2) with ('a', false) -> false | _ -> p w;;
-</pre>
+       let think (w:s) (p:s->t) (x:e) =
+         match (x, p 2) with ('a', false) -> false | _ -> p w;;

Ann disbelieves any proposition that is false in world 2.  Apparently,
she firmly believes we're in world 2.  Everyone else believes a
proposition iff that proposition is true in the world of evaluation.

-<pre>
-intapp (lift (intapp think
-                     (intapp (lift left)
-                             (unit 'b'))))
-       (unit 'a')
-1;; (* true *)
-</pre>
+       intapp (lift (intapp think
+                                                (intapp (lift left)
+                                                                (unit 'b'))))
+                  (unit 'a')
+       1;; (* true *)

So in world 1, Ann thinks that Bill left (because in world 2, Bill did leave).

@@ -651,13 +629,11 @@ The `lift` is there because "think Bill left" is extensional wrt its
subject.  The important bit is that "think" takes the intension of
"Bill left" as its first argument.

-<pre>
-intapp (lift (intapp think
-                     (intapp (lift left)
-                             (unit 'c'))))
-       (unit 'a')
-1;; (* false *)
-</pre>
+       intapp (lift (intapp think
+                                                (intapp (lift left)
+                                                                (unit 'c'))))
+                  (unit 'a')
+       1;; (* false *)

But even in world 1, Ann doesn't believe that Cam left (even though he
did: `intapp (lift left) cam 1 == true`).  Ann's thoughts are hung up
@@ -672,8 +648,8 @@ on what is happening in world 2, where Cam doesn't leave.
Finally, note that within an intensional grammar, extensional funtion
application is essentially just bind:

-<pre>
-# let swap f x y = f y x;;
-# bind cam (swap left) 2;;
-- : bool = false
-</pre>
+       # let swap f x y = f y x;;
+       # bind cam (swap left) 2;;
+       - : bool = false
+
+