Merge branch 'working'
authorJim <jim.pryor@nyu.edu>
Sat, 4 Apr 2015 23:25:15 +0000 (19:25 -0400)
committerJim <jim.pryor@nyu.edu>
Sat, 4 Apr 2015 23:25:15 +0000 (19:25 -0400)
* working:
  tweak reader2.ml, add Juli8.tgz

exercises/_assignment8.mdwn
index.mdwn
juli8.mdwn [new file with mode: 0644]
topics/week9_using_the_monad_library.mdwn [moved from topics/week9_using_monad_library.mdwn with 82% similarity]

index 8e28810..a05707a 100644 (file)
@@ -44,3 +44,8 @@ When you have it working, try
     # match eval t2 with Leaf (Num f) -> f g;;
     - : int = 13
 
+3. Add in the maybe monad.  Start here:
+
+        type num = env -> int option
+
+   Show that your code handles division by zero gracefully.
index a71eb4d..4872e3d 100644 (file)
@@ -166,17 +166,7 @@ Practical advice for working with OCaml and/or Haskell (will be posted someday);
 > Topics: [[Safe division with monads|topics/week8_safe_division_with_monads]]; [[Long but useful ramble|topics/week8_ramble]]
 
 (**Week 9**) Thursday April 2
-> Topics: [[Using the OCaml Monad library|/topics/week9_using_monad_library]]; the State monad; Programming with mutable state; Using multiple monads together
-
-> Here are some OCaml libraries: [[Juli8|code/juli8.ml]] and [[Monad|code/monad.ml]]. We'll write up explanations of these soon. But quickly, before you `#use "juli8.ml"`, you have to run this in your OCaml session. (I have these lines in my `~/.ocamlinit` file, so they run every time OCaml starts up:
-
->     #load "str.cma";;
->     module Std = struct
->       include Pervasives
->       module List = List
->       module Random = Random
->       module String = String
->     end
+> Topics: [[Installing and using the OCaml Juli8 library|/juli8]] (in progress); [[Using the OCaml Monad library|/topics/week9_using_the_monad_library]]; the State monad; Programming with mutable state; Using multiple monads together
 
 
 <!--
diff --git a/juli8.mdwn b/juli8.mdwn
new file mode 100644 (file)
index 0000000..a5e6087
--- /dev/null
@@ -0,0 +1,184 @@
+[[!toc]]
+
+<!--
+> Here are some OCaml libraries: [[Juli8|code/juli8.ml]] and [[Monad|code/monad.ml]]. We'll write up explanations of these soon. But quickly, before you `#use "juli8.ml"`, you have to run this in your OCaml session. (I have these lines in my `~/.ocamlinit` file, so they run every time OCaml starts up:
+
+>     #load "str.cma";;
+>     module Std = struct
+>       include Pervasives
+>       module List = List
+>       module Random = Random
+>       module String = String
+>     end
+-->
+
+
+## What are the Juli8 Libraries? ##
+
+In addition to the fine programming language Kapulet, which doesn't yet exist in a form you can actually execute --- though the full-featured interpreter we provided last week (LINK) is a good start --- I decided it would be useful to have a collection of basic libraries for our teaching (and other) purposes, that brought OCaml, Haskell, and the Scheme implementations we recommend, more onto even footing. Of course there are fundamental differences between these languages, such as the lack of types in Scheme (though both Racket and Chicken have some facility for working with types in extensions), or the default lazy evaluation strategy in Haskell. But there are also many simply accidental differences between the languages too, in that this one provides a library function doing so-and-so, but the other one doesn't, or calls it by a different name. The Juli8 collection of libraries is aimed to reduce these differences, to make it easier to move back and forth between the languages, and also to just make some of the languages generally easier to use (from my perspective). Juli8 will eventually have components that you can install into each of Haskell, OCaml, Racket, and Chicken. For the moment, OCaml is the most developed of these, and Haskell a bit, with the Scheme components deferred for another time.
+
+Juli8 tries to give OCaml more-or-less the same functionality that you have in Haskell's Maybe, List, and Monad libraries, as well as a few others. It doesn't try for an exact match, and it doesn't strictly use the same names as Haskell does. I've aimed to at least stay close to the existing OCaml naming patterns, and also the Scheme naming patterns when they are salient to me.
+
+Below, we'll give instructions on how to install Juli8 into your existing OCaml and/or Haskell setups. We'll also discuss how to use the non-monadic parts of the Juli8 libraries for OCaml. We discuss how to use the monadic parts of the Juli8 libraries for OCaml elsewhere (LINK). We'll also say a little bit about the little bit that Juli8 provides for you if you're using Haskell.
+
+## Setting up OCaml in general, and for use with Juli8 ##
+
+1. In our instructions for installing OCaml, some of the strategies involved installing the OPAM package manager. (This doesn't appear to be available for Windows.) If you did install OPAM, then we recommend typing the following commands inside a terminal session (that is, *not* inside an OCaml session, but inside the prompt where you'd start OCaml by typing `ocaml`).
+
+        opam update
+        opam upgrade
+        opam install delimcc
+
+    We'll comment on the `delimcc` package in a moment.
+
+    We also recommend upgrading to one of the more recent versions of OCaml. This gets you the most recent stable release:
+
+        opam switch 4.02.1 && opam install --switch=4.02.1 delimcc && eval `opam config env`
+
+    This will get you a slightly older release, which however has "improved type error messages":
+
+        opam switch 4.02.0+improved-errors && opam install --switch=4.02.0+improved-errors delimcc && eval `opam config env`
+
+    Installing the `delimcc` library gives you Oleg's Delimited Continuation library for OCaml, which we will encourage you to play around with later in the term. It's not essential to have it, though. There are some advantages to using one of the 4.02.x versions of OCaml, though, rather than the 4.01.0 version I think most of us ended up installing. One advantage is that now you can use the special <code>#show <i>symbol</i></code> command in OCaml sessions, which works like Haskell's special <code>:info <i>symbol</i></code> inside a GHCi session. Then for example, instead of having to type:
+
+        module type SOMETHING = Monad.OPTION
+
+    as I directed you elsewhere (LINK), you can instead just type:
+
+        #show Monad.OPTION
+
+2. The program that starts up when you type `ocaml` is OCaml's Standard "Toplevel" Interactive Interpreter. There's an alternative interactive interpreter that you can try out, which some people like. It's called **utop** and [you can read about it here](https://github.com/diml/utop) or [here](https://opam.ocaml.org/blog/about-utop). To install it, you can just type `opam install utop`. I'm not so crazy about it myself. But I prefer to use *some* kind of helper program with OCaml's Standard Toplevel, because the Standard Toplevel itself doesn't let you scroll back through commands you typed previously, has only very rudimentary facilities for editing a line if you made a mistake and so on. One virtue of utop is that it does those things better, but there are also other ways to do them better. What I use is a wrapper program called **rlwrap**. Here are instructions for how to install that:
+
+    > First, I had to upgrade the version of the "GNU readline" library on my computer. My Mac with System 10.9.5 has a version of that library, but it's too old to use with recent versions of `rlwrap`. So I downloaded [the source code for GNU readline](http://ftp.gnu.org/gnu/readline/readline-6.3.tar.gz). Double click the downloaded archive to expand it, if your browser doesn't do that automatically. Then go inside the `readline-6.3` folder in a Terminal. On a Mac, you can click on the folder in the Finder and do a Copy (or cmd-C). Then open a Terminal and type `cd` followed by a space then do a Paste (cmd-V). Then press <code><i>return</i></code>. Once you're inside the `readline-6.3` folder, type this command in the Terminal:
+
+    >     ./configure --prefix=$HOME && make && make install
+
+    > That should build and install the readline library in your local user directories. It will take a couple of minutes. Next, download [the source code for rlwrap](http://utopia.knoware.nl/~hlub/rlwrap/rlwrap-0.42.tar.gz). Double-click to expand and go inside the `rlwrap-0.42` folder in a Terminal, as before. Then type this command in the Terminal:
+
+    >     ./configure --prefix=$HOME && make CFLAGS="-g -O2 -I $HOME/include -L $HOME/lib" && make install
+
+    If that all completes without errors, then you have gotten `rlwrap` installed. Congratulations. To use it, you just will now type `rlwrap ocaml ...` wherever before you would ordinarily type `ocaml ...` This is just to make interactive OCaml sessions nicer. To compile code with `ocamlc ...` and so on, you don't use `rlwrap` for that. (If you see error messages of the form `-bash: rlwrap: command not found`, then you should make sure that you have a `.bash_profile` or `.bashrc` in your `$HOME` directory which has a line containing something like `export PATH="$HOME/bin:$HOME/Library/Haskell/bin:$PATH"`. Then maybe start up a new Terminal.)
+
+    Some keycommands you can now use are:
+
+    * TODO
+
+    If you want to customize `rlwrap` or these keycommands, you can read the documentation at `man rlwrap` and `man readline`, and also look into the Preferences of your Terminal program. (On the Mac, look under Terminal menu/Preferences/Keyboard tab.) This gets complicated quickly, but those are the places to start looking if you want to experiment. You should of course also read around on the web, rather than just changing these blindly.
+
+3. Whether you use `utop` or `rlwrap ocaml` or just plain `ocaml`, you will need to set up some initialization commands in order to use the Juli8 libraries. You'll want to edit or create an file called `.ocamlinit` in your `$HOME` directory. The file may already be there and have some commands in it if you used OPAM.
+
+    After any commands that are already there, add these lines:
+
+        #load "str.cma";;
+        module Std = struct
+          include Pervasives
+          module List = List
+          module Random = Random
+          module String = String
+        end
+
+        #directory "+../delimcc";;
+
+        #directory "/Users/jim/.juli8/ocaml";; (* you'll have to substitute your own $HOME directory in for `/Users/jim/` *)
+
+        #use "juli8.ml";;
+
+4. Next create a folder in your `$HOME` directory named `.juli8`. Download the Juli8 code from [[here]] (LINK). That link will no doubt be updated frequently in April and May 2015. The current version is: 1.2, posted 4 April 2015. Copy the contents of the `Juli8` folder that you downloaded into the `$HOME/.juli8` folder that you created.
+
+    Now whenever you start up OCaml, the Juli8 OCaml libraries (including their monad components, which we'll be making extensive use of) will automatically be loaded. <!-- If later you want to load Oleg's Delimcc library, type `#load "delimcc.cma";;` then use the `Delimcc` module. -->
+
+
+## Using the Juli8 libraries with OCaml ##
+
+Will add here some guidance on using the non-monadic parts of the Juli8 library. For the monadic part, [[see here]] (LINK).
+
+
+
+## Setting up Haskell for use with Juli8 ##
+
+1. When (If) you installed Haskell, we hope you did it via a method that gave you the Haskell Platform. This will give you a recent version of the Glasgow Haskell Compiler (GHC), which comes with the interactive Haskell session program `ghci`, along with the **Cabal** package manager (the analogue of OCaml's OPAM) and also with a collection of the most widely-used libraries in the Haskell community, that don't come along with GHC itself.
+
+2. Assuming you do have Cabal, we recommend you do the following. First, find out where Cabal installs logs of its activity. On my Mac, it puts them in the folder `~/Library/Haskell/logs/world`. Now what I did was to type the following command in a Terminal:
+
+        ln -s ~/Library/Haskell/logs/world ~/.cabal
+
+    Now I can find a link to what Cabal has done inside cabal's own folder, without needing to remember or hunt down where the hell on my disk that information has been stored. (Alright, to be honest, you can skip this whole step if you want. But I recommend doing it.)
+
+3. Still assuming you have Cabal, type the following in a Terminal:
+
+        cabal update
+        cabal install --user ghc-paths semigroups hoogle
+
+4. If you haven't already downloaded and installed the Juli8 libraries as described in Step 4 under the earlier OCaml section, do that now. Also type the following lines in a Terminal:
+
+        ln -s ~/.juli8/haskell ~/.ghc/juli8
+
+5. Check to see whether any of the following files exist on your system. `$HOME` will be some directory like `/Users/jim` or `/home/jim` or `C:/Documents\ and\ Settings/jim`:
+
+    * `$HOME/.ghci`
+    * `$HOME/.ghc/ghci.conf`
+    * `$HOME/"Application Data"/ghc/ghci.conf`
+
+    If you find such a file, you will add lines to it in the next step. If you don't find such a file, create one.
+
+6. Add these lines to the `.ghci` or `ghci.conf` file identified in the previous step:
+
+        -- reads ghci commands from any file named in $GHCIRC
+        :cmd (System.Environment.getEnvironment >>= maybe (return "") readFile . lookup "GHCIRC")
+
+        -- special :commands from Juli8
+        :{
+        :cmd do { dot_ghc <- System.Directory.getAppUserDataDirectory "ghc";
+                  let { juli8 = dot_ghc ++ "/juli8";
+                        cmds = juli8 ++ "/commands" };
+                  juli8_exists <- System.Directory.doesDirectoryExist juli8;
+                  cmds_exists <- System.Directory.doesFileExist cmds;
+                  Control.Monad.when cmds_exists $ putStrLn "Loading juli8/commands ...";
+                  return $ unlines $ if cmds_exists then [":set -i"++juli8, ":script "++cmds] else if juli8_exists then [":set -i"++juli8] else [] }
+        :}
+
+        :def! url (\l->return $ ":!open "++l)
+        -- :set editor vim
+        -- :set +m -- for multiline input
+        -- :set +t -- to print types after each binding
+
+        :load Juli8
+        :mod Juli8
+
+    You may want to uncomment the `:set editor vim` line, but only if you know how to use the text editor `vim`. Other text editors you may be familiar with, and can use here are:
+
+        :set editor emacs
+        :set editor nano
+        :set editor open -a TextEdit -- Mac-only
+        :set editor bbedit -- Mac-only, see http://www.barebones.com/support/bbedit/cmd-line-tools.html
+        :set editor mate -- Mac-only, see http://manual.macromates.com/en/using_textmate_from_terminal.html
+        -- for Windows, use one of https://wiki.haskell.org/Windows#Editors
+
+    You may want to uncomment the `:set +m` line. What this does is let you type multi-line commands in the `ghci` sessions. There is a different way to do that, where you type like this:
+
+        :{
+        multiline
+          command
+        :}
+
+    but that's pretty cumbersome. The downside of having `:set +m` on is that sometimes you'll have to type an extra blank line before `ghci` will respond to your input.
+
+If everything works, then when you start up GHCi, you should see a prompt like this:
+
+    Prelude Juli8>
+
+
+## What do I get from Juli8 for Haskell? ##
+
+There are two or three benefits that Juli8 provides for Haskell, and they're not a big deal. If you're already a seasoned Haskell user, you may or may not find them helpful.
+
+First, Juli8 comes with a bunch of extra `:commands` to use at the GHCi prompt. You can see a list of what it installs by typing `:?`. Some of the commands listed in `:?` were already present before Juli8 arrived, and are just here collected and explained in a way I find more helpful. Others are provided by Juli8 itself. Many of these are based on commands already published elsewhere on Haskell wikis and so on, so you may have installed some versions of them already yourself. I'll leave it to you to pick and choose whether anything that comes with Juli8 suits your further needs.
+
+I developed these `:commands` on a Mac, and expect that some of the assumptions I made won't work on other systems. As the library matures, we'll try to make it work for a broader range of systems, or give specific instructions about how to customize it.
+
+The command `:help` will give you the old, official help text, that doesn't show the extra commands installed by Juli8.
+
+Second, Juli8 comes with a module/library that collects together a number of elements from scattered other locations in the Haskell libraries. These include the Semigroup libraries you installed in Step 3 of the above instructions, which you should use in place of Haskell's standard Data.Monoid libraries. Note that the Semigroup library provides `First a` and `Last a` types that differ from the types of the same name in Data.Monoid. Juli8 also provides `OptFirst a` and `OptLast a` types that behave more like the `First a` and `Last a` types from Data.Monoid. It also provides analogous types `OptMax a` and `OptMin a`. (If you don't know what any of this means, don't worry about it.)
+
+Third, Juli8 comes with a module/library `IOPlus` that isn't loaded by default, but which you can load manually by saying `:add IOPlus`. This provides instances to make `IO a` a Monoid when `a` is, and to make `IO` act like an instance of the Alternative and MonadPlus typeclass. This has some limitations, and can't be done perfectly, which is why it isn't done in the standard libraries. There's also an `IOFirst` and an `IOLast`. This is more experimental than the rest of the stuff in Juli8 and may well change or be removed. I'll explain it and refine it another time.
+
similarity index 82%
rename from topics/week9_using_monad_library.mdwn
rename to topics/week9_using_the_monad_library.mdwn
index d9820ac..f3cb414 100644 (file)
@@ -4,7 +4,11 @@
 
 If you want to see the "signature" of some OCaml library or "module" --- that is, a list of the types it exports, and also of the types of function values (and other values) that it exports, you can do this.
 
-First, if you know that the module uses a specific *named* module type, here's what to do. An example is that modules you create using `Monad.Reader(struct type env = ... end).M` --- really you have to do that in two steps, first say `module R_E = Monad.Reader(struct type env = ... end)`, next write `R_E.M`; but for brief reading I'll just write `Monad.Reader(struct type env = ... end).M` --- these modules will all have the module type `Monad.READER`. We can see an expansion of `Monad.READER` by writing:
+If you know the name of the module but not the name of its module type, or if its module type doesn't have a name, you can do this:
+
+    module type SOMETHING = sig include module type of ModuleYouAreInterestedIn end
+
+More commonly, though, you'll know that the module uses a specific *named* module type. In that case, here is what to do. An example is that modules you create using `Monad.Reader(struct type env = ... end).M` --- really you have to do that in two steps, first say `module R_E = Monad.Reader(struct type env = ... end)`, next write `R_E.M`; but for brief reading I'll just write `Monad.Reader(struct type env = ... end).M` --- these modules will all have the module type `Monad.READER`. We can see an expansion of `Monad.READER` by writing:
 
     module type SOMETHING = sig include Monad.READER end
 
@@ -57,14 +61,14 @@ The next block of stuff in the `Monad.READER` module type are functions common t
 
         let letx xint body = shift (insert 'x' xint) body
 
-    will declare an operation that takes an `int` `xint` and a monadic value `body`, and evaluates `body` in the context <code>let x = <i>xint</i> in ...</code>. If we wanted instead to have a version which accepted not an `int` but rather an `int` in a Reader monad box, we could write instead:
+    will declare an operation that takes an `int` `xint` and a monadic value `body`, and evaluates `body` in the context <code>let x = <i>xint</i> in ...</code>. If we wanted instead to have a version which accepted not an `int`, but rather an `int` Reader, we could write instead:
 
         let letx xx body = xx >>= fun xint -> shift (insert 'x' xint) body
 
 
 ### Examples ###
 
-Here are some examples of using the Reader Monad modules to evaluate some simple expressions using bound variables. First, you could look at [[this Haskell code|/code/reader1.hs]]. It `import`s the `Control.Monad.Reader` library, which is where Haskell's Reader monad can be found. It declares an `Env` type that we'll implement as a simple *function* from `Char`s to `Int`s. Then it defines an "empty" environment `env0`, and a function `insert` for adding new bindings to an `env`. Next, we make a general function `getint` that can create monadic values like the `getx` illustrated above. We show how to use `getx` and `gety` to write monadic versions of `y + x` and `3 + x`. Next, we define a `letx` function as illustrated above (the second version, that takes a monadic value `xx` as its argument). We show how to use this to write a monadic version of `let x = 2 in y + x`. The final line of the file applies `runReader` to the monadic value we've built --- this is Haskell's way of doing what we do in OCaml with `run`, namely to remove the abstraction barrier and see what concrete type is really constituting our `Reader Env a`s --- and we supply it with the empty environment, which will be sufficient since the expression we're interpreting has no free variables. Haskell binds the variable `res` to the result. You can run this code inside `ghci` by typing `:load /path/to/reader1.hs`. (You could also say `:add ...` instead of `:load ...`.) Then type `res`, and Haskell will report back `5`.
+Here are some examples of using the Reader Monad modules to evaluate some simple expressions using bound variables. First, you could look at [[this Haskell code|/code/reader1.hs]]. It `import`s the `Control.Monad.Reader` library, which is where Haskell's Reader monad can be found. It declares an `Env` type that we'll implement as a simple *function* from `Char`s to `Int`s. Then it defines an "empty" environment `env0`, and a function `insert` for adding new bindings to an `env`. Next, we make a general function `getint` that can create monadic values like the `getx` illustrated above. We show how to use `getx` and `gety` to write monadic versions of `y + x` and `3 + x`. Next, we define a `letx` function as illustrated above (the second version, that takes a monadic value `xx` as its argument). We show how to use this to write a monadic version of `let x = 2 in y + x`. The final line of the file applies `runReader` to the monadic value we've built --- this is Haskell's way of doing what we do in OCaml with `run`, namely to remove the abstraction barrier and see what concrete type is really constituting our `Reader Env a`s --- and we supply it with the empty environment, which will be sufficient since the expression we're interpreting has no free variables. Haskell binds the variable `res` to the result. You can run this code inside `ghci` by typing `:load /path/to/reader1.hs`. (You may also be able to say `:add ...` instead of `:load ...`.) Then type `res`, and Haskell will report back `5`.
 
 [[This OCaml code|/code/reader1.ml]] does exactly the same thing only using our OCaml monad libraries instead. The biggest difference from the Haskell version is in the first few lines, where we have to generate a Reader monad module parameterized on the `env` type that we intend to work with.
 
@@ -78,11 +82,11 @@ and like this in Haskell:
     data Bound = Int Int | Fun (Reader Env Int -> Reader Env Int)
     type Env = Char -> Bound
 
-There is a tricky issue in the OCaml case, though, in that when working with OCaml, we have to *generate* our R Reader monad module, parameterized on the type of the `env`, but here we see that we need access to the *type* `'a R.t` from the generated R module in order to declare the `env`. Fortunately, it is possible to do this, by having the module that declares the `env` and the module that has our Reader monad in it be mutually recursively defined. The first few lines of [[this OCaml code|/code/reader2.ml]] do the tricky work.
+There is a tricky issue in the OCaml case, though, in that when working with OCaml, we have to *generate* our `R` Reader monad module, parameterized on the type of the `env`, but here we see that we need access to the *type* `'a R.t` from the generated `R` module in order to declare the `env`. Fortunately, it is possible to do this, by having the module that declares the `env` and the module that has our Reader monad in it be mutually recursively defined. The first few lines of [[this OCaml code|/code/reader2.ml]] do the tricky work.
 
-After that, our [[Haskell code|/code/reader2.hs]] and [[OCaml code|/code/reader2.ml]] proceed basically the same, allowing for the difference in syntax and vocabulary between Haskell and OCaml. The `getint` function works like before, except now we have to pull the int out from behind the `Int` constructor of our disjoint sum type `bound`. We have a parallel `getfun` function. Then we interpret the variable `x` using the monadic value `getint 'x'`, and we interpret the variable `f` using the monadic value `getfun 'f'`. The `letx` operation is similarly adjusted, and we also have a parallel `letf`.
+After that, our [[Haskell code|/code/reader2.hs]] and [[OCaml code|/code/reader2.ml]] proceed basically the same, allowing for the difference in syntax and vocabulary between Haskell and OCaml. The `getint` function works like before, except now we have to pull the `int` out from behind the `Int` constructor of our disjoint sum type `bound`. We have a parallel `getfun` function. Then we interpret the variable `x` using the monadic value `getint 'x'`, and we interpret the variable `f` using the monadic value `getfun 'f'`. The `letx` operation is similarly adjusted, and we also have a parallel `letf`.
 
-The really new thing in this code, compared to the previous example, is our definition of a monadic value to interpret the lambda abstract `\y -> y + x`, that `f` gets bound to. And also our interpretation of the expression `f 3`, which looks up a function that the variable `f` is bound to, and then applies it to (a monadically-lifted version of) `3`. (We have the argument be monadically lifted so that we could also say, for example, `f y`.)
+The really new thing in this code, compared to the previous example, is our definition of a monadic value to interpret the lambda abstract `\y -> y + x`, that `f` gets bound to. And also our interpretation of the expression `f 3`, which looks up a function that the variable `f` is bound to, and then applies it to (a monadically-lifted version of) `3`. (We have the argument be monadically lifted so that we could also say, for example, `f y`.) You can examine the code to see how we do these things.
 
 
 ## OK, what else is in the OCaml Monad modules? ##
@@ -136,9 +140,9 @@ Writer is very similar to Reader: first, it is parameterized on something like a
         val shift : (env -> env) -> 'a t -> 'a t
       end
 
-Whereas Writer's `sensor` and Reader's `shift` have isomorphic types, there is some extra complextity to Writer's `listen` and `listens`, compared to `ask` and `asks`. What this extra complexity means is that for `Writer`, listening happens only in a local context. You can't `listen` to what got written to the log before you installed your `listen`ing tap. But you can return payloads that are dependent on what you've heard in the local context.
+Whereas Writer's `censor` and Reader's `shift` have isomorphic types, there is some extra complextity to Writer's `listen` and `listens`, compared to `ask` and `asks`. What this extra complexity means is that for `Writer`, listening happens only in a local context. You can't `listen` to what got written to the log before you installed your `listen`ing tap. But you can return payloads that are dependent on what you've heard in the local context.
 
-Unlike Reader, Writer also has a `tell` operation, which is akin to the `put` operation in the State monad. The difference is that the `tell` function takes a `log` as argument and *appends* that to the existing `log`. You can't erase or overwrite elements already in the `log`; you can only append to it. However, if you like, you can `censor` the log in arbitrary ways and use that when interpreting other monadic values locally. After that local context, though, we return to the original log (
+Unlike Reader, Writer also has a `tell` operation, which is akin to the `put` operation in the State monad. The difference is that the `tell` function takes a `log` as argument and *appends* that to the existing `log`. You can't erase or overwrite elements already in the `log`; you can only append to it. However, if you like, you can `censor` the log generated by any local context. (Inside the local context, the log isn't yet censored; the censoring only affects what's seen downstream as the contributions made by that context to the log.)
 
 Here's a complex example that illustrates this. First we will use the helper function `String.upper` (from "juli8.ml") and a second helper function that we define like this:
 
@@ -157,17 +161,17 @@ Next, we construct some monadic values and reveal them at the end using `run`:
        let zz = tell "before" >> yy >>= fun y -> tell "after" >> mid y in
        ...);;
 
-The monadic value `xx` writes "one" to the log, then discards the resulting `()` payload (it continues `>> ...` rather than `>>= fun var -> ...`). Then we have a use of `listens`. This will evaluate its body `tell "two" >> mid 10` and return as payload a pair of the body's original payload and a bracketed copy of the local log. Thus the payload of `listens bracket (tell "two" >> mid 10)` will be `(10, "{two}")`. The `"one"` that got written to the log earlier isn't accessible to `listens`; however it does stay in the log. Hence the result of `run xx`, showing first the payload and then the log, would be:
+The monadic value `xx` writes `"one"` to the log, then discards the resulting `()` payload (it continues `>> ...` rather than `>>= fun var -> ...`). Then we have a use of `listens`. This will evaluate its body `tell "two" >> mid 10` and return as payload a pair of the body's original payload and a `bracket`ed copy of the local log. Thus the payload of `listens bracket (tell "two" >> mid 10)` will be `(10, "{two}")`. Its log will be `"two"`. The `"one"` that got written to the log earlier isn't accessible to `listens`; however it does stay in the overall log, to which the `listens ...` construction contributes. Hence the result of `run xx`, showing first the payload and then the log, would be:
 
     - : (int * string) W.result = ((10, "{two}"), "one two")
 
 Now `yy` uses that `xx` monadic value to illustrate the use of `censor`. Here we have `censor` apply `String.upper` to the log generated in the local context it's applied to, hence the result of  `run yy` would be:
 
-    - : ((int * string) * string) W.result = (((10, "{two}"), "{one two}"), "ZERO ONE TWO three")
+    - : ((int * string) * string) W.result = (((10, "{two}"), "{one two}"), "ZERO ONE TWO")
 
 The final value `zz` shows what happens to entries written to the log before and after the `censor`ing that occurs in `yy`, namely nothing. That is, `run zz` is:
 
-- : ((int * string) * string) W.result = (((10, "{two}"), "{one two}"), "before ZERO ONE TWO after")
+    - : ((int * string) * string) W.result = (((10, "{two}"), "{one two}"), "before ZERO ONE TWO after")
 
 Let's look at some more familiar monad signatures. Here is one:
 
@@ -188,7 +192,7 @@ This is what's exposed in the `Monad.Option.M` module (with `Option` and `List`,
 
 If `some_bool_expr` is true, then this will ignore its payload and go on to compute `more_monadic_stuff`; if it's false, then the whole chain gets ignored because of the distinctive behavior of `mzero`.
 
-The third special operation in the Option monad is `test`. This lets you supply a function that takes an ordinary `'a option` type (that is, one where the "abstraction curtain" imposed by the `'a O.t` type is not in place) and returns a bool. Then you take an Option monadic value (one where the "abstraction curtain" *is* in place). OCaml will temporarily remove the abstraction curtain on the second argument and see how the function you supplied assesses it. If the result is `true`, then the result is identical to that Option monadic value, unaltered. If the result is `false`, then the result is `mzero`. (For those of you who know Frank Veltman's work on dynamic semantics for epistemic modals, this is a key component.)
+The third special operation in the Option monad is `test`. This lets you supply a function that takes an ordinary `'a option` type (that is, one where the "abstraction curtain" imposed by the `'a O.t` type is not in place) and returns a `bool`. Then you take an Option monadic value (one where the "abstraction curtain" *is* in place). OCaml will temporarily remove the abstraction curtain on the second argument and see how the function you supplied assesses it. If the result is `true`, then the result is identical to that Option monadic value, unaltered. If the result is `false`, then the result is `mzero`. (For those of you who know Frank Veltman's work on dynamic semantics for epistemic modals, this `test` (or the version of it for sets of worlds) is a key component.)
 
 Here is the List monadic interface: