`x`_{1}, x_{2}, x_{3}

, and so on? And then the values that get assigned to those variables could just be kept in a stack, with the first value understood to be assigned to `x`_{1}

, and so on. Yes, we could do that. But the advantage of the de Bruijn notation is that we don't have to keep track of which variable is associated with which lambda binder. In the de Bruijn framework, the outermost binder always goes with the top-most member of the stack. In the proposed alternative, that's not so: we could have:
+
+`\x`_{2}. x_{2} (\x_{8}. x_{8} x_{2})
+

+
+In any event, though, we won't make use of de Bruijn notation because, though it makes the semantic algorithms more elegant, it also adds an extra level of conceptual complexity, which we don't want to do.]
+
+Now with the environment-based semantics, instead of evaluating terms using substitution, like this:
+
+ (\x. M) N ~~> M {x <- N}
+
+we'll instead evaluate them by manipulating their environment. To intepret `(\x. M) N` in environment `e`, we'll interpret `M` in an environment like `e {x:= N}` where `x` may have been changed to now be assigned to `N`.
+
+A few comments. First, what the environment associates with the variable `x` will be expressions of the lambda calculus we're evaluating. If we understand the evaluation to be call-by-name, these expressions may be complexes of the form `N P`. If on the other hand, we understand the evaluation to be call-by-value, then these expressions will always be fully evaluated before being inserted into the environment. That means they'll never be of the form `N P`; but only of the form `y` or `\y. P`. The latter kinds of expressions are called "values." But "values" here are just certain kinds of expressions. (They *could* be the denotations of lambda expressions, if one thinks of the lambda expressions as all having normal-form lambda terms as their denotations, when possible.)
+
+Second, there is a subtlety here we haven't yet discussed. Consider what should be the result of this:
+
+ let x = 1
+ in let f = \y. x
+ in let x = 2
+ in f 0
+
+If we interpret the `x` in `\y. x` by the value it had where that function was being declared, and bound to `f`, then this complex expression should return `1`. If on other hand we interpret the `x` to have the value it has where that function is given an argument and reduced, then the complex should return `2`.
+
+There is no right or wrong behavior here. Both are reasonable. The former behavior is called **lexical scoping**, and it's what we get in the untyped lambda calculus, for example. It's also what's most common in functional programming languages. It's easier for programmers to reason about.
+
+The latter behavior is called **dynamic scoping**. Often it's easier for language-designers to implement. It is also useful in certain settings. But we will assume we're dealing with lexical scoping.
+
+To implement lexical scoping, you can't just associate the bare formula `\y. x` to the variable `f` in an environment. You also have to keep track of what is the value in that context of all the free variables in the formula. This combination of a function expression together with the values of its free variables is called a **function closure**. There are different techniques for handling these. The technique we'll use here is conceptually simple: it just stashes away both the formula `\y. x` and a copy of the current environment. So even if we're later asked to evaluate `f` in a different environment, we have the original environment to use to lookup values for`f`'s free variables. This is conceptually simple---but inefficient, because it stashes a copy of the entire current environment, when really we only need that part of the environment relevant to a few free variables. If you care to read up more about
+operational semantics for the lambda calculus, or the underpinnings of how Scheme or OCaml interpreters work under the hood, you can learn about other more elegant techniques. Here we'll keep things conceptually simple.
+
+With these ideas settled, then, we can present an environment-based operational semantics for the untyped lambda calculus. Here is a call-by-value version, which assumes that expressions are always fully evaluated before being inserted into the environment.
+
+1. When `e` assigns some term `v` to `x`, then `x` fully (that is, terminally) reduces in environment `e` to `v`. We write that as: `(e |- x) ==> v`.
+
+2. `(e |- \x. M) ==> closure(e, \x. M)`, where a closure is some data-structure (it might just be a pair of the environment `e` and the formula `\x. M`).
+
+3. If `(e |- M) ==> closure(e2, \x. M2)` and `(e |- N) ==> v` and `(e2 {x:=v} |- M2) ==> u`, then `(e |- M N) ==> u`. Here `e2 {x:=v}` is the environment which is just like `e2` except it assigns `v` to `x`.
+
+
+Explicitly manipulating the environment
+---------------------------------------
+
+In the machinery we just discussed, the environment handling was no part of the language being interpreted. It all took place in the interpreting algorithm. Sometimes, though, it's convenient to explicitly manipulate the environment in your program; or at least, some special portion of the environment, set aside to be manipulated in that way.
+
+For example, a common programming exercise when students are learning languages like OCaml is to implement a simple arithmetic calculator. You'll suppose you're given some expressions of the following type:
+
+ type term = Constant of int
+ | Multiplication of (term * term)
+ | Addition of (term*term)
+ | Variable of char
+ | Let of (char*term*term);;
+
+and then you'd evaluate it something like this:
+
+ let rec eval (t : term) = match t with
+ Constant x -> x
+ | Multiplication (t1,t2) -> (eval t1) * (eval t2)
+ | Addition (t1,t2) -> (eval t1) + (eval t2)
+ | Variable c -> ... (* we'll come back to this *)
+ | Let (c,t1,t2) -> ... (* this too *)
+
+With that in hand, you could then evaluate complex terms like `Addition(Constant 1, Multiplication(Constant 2, Constant 3))`.
+
+But then how should you evaluate terms like `Let('x',Constant 1,Addition(Variable 'x', Constant 2))`? We'd want to carry along an environment that recorded that `'x'` had been associated with the term `Constant 1`, so that we could retrieve that value when evaluating `Addition(Variable 'x', Constant 2)`.
+
+Notice that here our environments associate variables with (what from the perspective of our calculator language are) *real* values, like `2`, not just value-denoting terms like `Constant 2`.
+
+We'll work with a simple model of environments. They'll just be lists. So the empty environment is `[]`. To modify an environment `e` like this: `e {x:=1}`, we'll use:
+
+ ('x', 1) :: e
+
+that is, `e` is a list of pairs, whose first members are `char`s, and whose second members are evaluation results. To lookup a value `'x'` in `e`, we can use the `List.assoc` function, which behaves like this:
+
+ # List.assoc 'x' [('x',1); ('y',2)];;
+ - : int = 1
+ # List.assoc 'z' [('x',1); ('y',2)];;
+ Exception: Not_found.
+
+Then we should give our `eval` function an extra argument for the environment:
+
+ let rec eval (t : term) (e: (char * int) list) = match t with
+ Constant x -> x
+ | Multiplication (t1,t2) -> (eval t1 e) * (eval t2 e)
+ | Addition (t1,t2) -> (eval t1 e) + (eval t2 e)
+ | Variable c ->
+ (* lookup the value of c in the current environment
+ This will fail if c isn't assigned anything by e *)
+ List.assoc c e
+ | Let (c,t1,t2) ->
+ (* evaluate t2 in a new environment where c has been associated
+ with the result of evaluating t1 in the current environment *)
+ eval t2 ((c, eval t1 e) :: e)
+
+Great! Now we've built a simple calculator with let-expressions.
+
+
+Monadizing it
+-------------
+
+As the calculator gets more complex though, it will become more tedious and unsatisfying to have all the clauses like:
+
+ | Addition (t1,t2) -> (eval t1 e) + (eval t2 e)
+
+have to explicitly pass around an environment that they're not themselves making any use of. Would there be any way to hide that bit of plumbing behind the drywall?
+
+Yes! You can do so with a monad, in much the same way we did with our checks for divide-by-zero failures.
+
+Here we'll use a different monad. It's called the **Reader monad**. We define it like this:
+
+ (* we assume we've settled on some implementation of the environment *)
+ type env = (char * int) list;;
+
+ (* here's the type of our Reader monad *)
+ type 'a reader = env -> 'a;;
+
+ (* here's our unit operation; it creates a reader-monad value that
+ ignores the environment and returns x *)
+ let unit x = fun (e : env) -> x;;
+
+ (* here's our bind operation; how does it work? *)
+ let bind (u : 'a reader) (f: 'a -> 'b reader) : 'b reader =
+ (* this can be written more compactly, but having it spelled out
+ like this will be useful down the road *)
+ fun (e : env) -> let a = u e in let u' = f a in u' e
+
+
+ (* we also define two special-purpose operations on our reader-monad values *)
+
+ (* evaluate a reader-monad value in a shifted environment; how does this work? *)
+ let shift (c : char) (v : int reader) (u : 'a reader) =
+ fun (e : env) -> u ((c, v e) :: e)
+
+ (* lookup the value of c in the current environment
+ this will fail if c isn't assigned anything by that environment
+ a fuller solution would return an int option instead of
+ returning an int or failing *)
+ let lookup (c : char) : int reader =
+ fun (e : env) -> List.assoc c e
+
+
+With this in hand, we can then do our calculator like this. Instead of `int`s, evaluating a term now returns an `int reader`, a monadic value of our new reader-monad type:
+
+ let rec eval (t : term) = match t with
+ Constant x -> unit x
+ | Multiplication (t1,t2) -> lift2 ( * ) (eval t1) (eval t2)
+ | Addition (t1,t2) -> lift2 ( + ) (eval t1) (eval t2)
+ | Variable c -> lookup c
+ | Let (c,t1,t2) -> shift c (eval t1) (eval t2);;
+
+Now if we try:
+
+ # let result = eval (Let('x',Constant 1,Addition(Variable 'x',Constant 2)));;
+ - : int reader = + + +Now the normal way in which the nodes of such trees are related to each other is that the semantic value of a parent node is the result of applying the functional value of one of the daughter nodes to the value of the other daughter node. (The types determine which side is the function and which side is the argument.) One exception to this general rule concerns intersective adjectives. (How does \[[complex]] combine with \[[phrases]]?) We'll ignore that though. + +Another exception is that Heim and Kratzer have to postulate a special rule to handle lambda abstraction. (This is their "Predicate Abstraction Rule.") Not only is it a special rule, but it's arguably not even a compositional rule. The basic idea is this. The semantic value of: + + \[[man who(i): Alice spurned i]] + +is the result of combining \[[man]], an `e->t` type predicate value, with the adjective-type value of \[[who(i): Alice spurned i]]. As I said, we'll ignore complexities about their treatment of adjectives. But how is \[[who(i): Alice spurned i]] derived? Heim and Kratzer say this is defined only relative to an +assignment g, and it's defined to be a function from objects x in the domain to the value that \[[Alice spurned i]] has relative to shifted assignment g {i:=x}, which is like g except for assigning object x to variable i. So this is not the result of taking some value \[[who(i)]], and some separate value \[[Alice spurned i]], and supplying one of them to the other as argument to function. + +Getting any ideas? + +Yes! We can in fact implement this as a Reader monad. And in doing so, we *will* get a value \[[who(i)]] which is a function, and another value \[[Alice spurned i]], to be its argument. So the semantics in the first place again becomes compositional, and in the second place doesn't need any special rule for how \[[who(i): Alice spurned i]] is derived. It uses the same composition rule as other complex expressions. + +How does this work? + +We set \[[i]] = the reader-monad value `lookup i`. + +We set \[[Alice]] = the reader-monad value `unit Alice`. + +We have to lift the semantic values of predicates into the Reader monad. So if before we were taking the semantic value of "spurned" to be a function `S` of type `e -> e -> t`, now we set \[[spurned]] = `lift2 S`. + +Finally, we set \[[who(i)]] to be: + + fun (u : bool reader) (v : entity reader) -> + fun (g : env) -> u (g {i:=v g}) + +That is, it takes as arguments a clause-type reader-monad `u`, and an entity-type reader-monad `v`, and returns a reader-monad that evaluates `u` in an environment that's modified to assign `v`'s value in that environment to the variable `i`. In other words, this is: + + fun (u : bool reader) (v : entity reader) -> shift i v u + +You can trace through what happens then if we apply \[[who(i)]] to \[[Alice spurned i]]: + + \[[Alice spurned i]] = \[[spurned]] \[[i]] \[[Alice]] + = (lift2 S) (lookup i) (unit Alice) + = bind (lookup i) (fun x -> bind (unit Alice) (fun y -> unit (S x y))) + +because of the left-identity rule for `unit`, this is the same as: + + = bind (lookup i) (fun x -> unit (S x Alice)) + +Substituting in the definition of `bind` for the reader-monad, this is: + + = fun e -> (fun x -> unit (S x Alice)) (lookup i e) e + = fun e -> unit (S (lookup i e) Alice) e + +Substituting in the definition of `unit`, this is: + + = fun e -> S (lookup i e) Alice + +And now supplying \[[Alice spurned i]] as an argument to \[[who(i)]], we get: + + \[[who(i): Alice spurned i]] = \[[who(i)]] \[[Alice spurned i]] + = (fun u v -> shift i v u) (fun e -> S (lookup i e) Alice) + = fun v -> shift i v (fun e -> S (lookup i e) Alice) + +Substituting in the definition of `shift`, this is: + + = fun v -> (fun c v u e -> u ((c, v e) :: e)) i v (fun e -> S (lookup i e) Alice) + = fun v -> (fun u e -> u ((i, v e) :: e)) (fun e -> S (lookup i e) Alice) + = fun v -> (fun e -> (fun e -> S (lookup i e) Alice) ((i, v e) :: e)) + = fun v -> (fun e -> S (lookup i ((i, v e) :: e)) Alice) + = fun v -> (fun e -> S (v e) Alice) + +In other words, it's a function from entity-Reader monads to a function from assignment functions to the result of applying S to the value of that entity reader-monad under that assignment function, and to Alice. Essentially the same as Heim and Kratzer's final value, except here we work with monadic values, such as functions from assignments to entities, rather than bare entities. And our derivation is completely compositional and uses the same composition rules for joining \[[who(i)]] to \[[Alice spurned i]] as it uses for joining \[[spurned]] to \[[i]] and \[[Alice]]. + +What's not to like? + +Well, some of our semantic values here are assignment-shifters: + + \[[who(i)]] = fun u v -> shift i v u + +and some philosophers count assignment-shifters as "monsters" and think there can't be any such thing. Well, everyone has their own issues they need to work through. + +Later, techniques parallel to what we do here can be used to implement semantics for mutation and dynamic predicate logic. And then again, parallel techniques can be used to implement the "coordinated" semantics that Kit Fine and Jim Pryor favor. We just need different monads in each case. + +Want more right now? Then let's look at doing the same thing for [Intensionality](/intensionality_monad). + diff --git a/topics/_week8_intensionality.mdwn b/topics/_week8_intensionality.mdwn new file mode 100644 index 00000000..4159f845 --- /dev/null +++ b/topics/_week8_intensionality.mdwn @@ -0,0 +1,226 @@ +Now we'll look at using monads to do intensional function application. +This is just another application of the Reader monad, not a new monad. +In Shan (2001) [Monads for natural +language semantics](http://arxiv.org/abs/cs/0205026v1), Ken shows that +making expressions sensitive to the world of evaluation is conceptually +the same thing as making use of the Reader monad. +This technique was beautifully re-invented +by Ben-Avi and Winter (2007) in their paper [A modular +approach to +intensionality](http://parles.upf.es/glif/pub/sub11/individual/bena_wint.pdf), +though without explicitly using monads. + +All of the code in the discussion below can be found here: [[intensionality-monad.ml]]. +To run it, download the file, start OCaml, and say + + # #use "intensionality-monad.ml";; + +Note the extra `#` attached to the directive `use`. + +First, the familiar linguistic problem: + + Bill left. + Cam left. + Ann believes [Bill left]. + Ann believes [Cam left]. + +We want an analysis on which the first three sentences can be true at +the same time that the last sentence is false. If sentences denoted +simple truth values or booleans, we have a problem: if the sentences +*Bill left* and *Cam left* are both true, they denote the same object, +and Ann's beliefs can't distinguish between them. + +The traditional solution to the problem sketched above is to allow +sentences to denote a function from worlds to truth values, what +Montague called an intension. So if `s` is the type of possible +worlds, we have the following situation: + + ++ VP + / \ + / \ + / \ + / \ + / \ + / NP + / / \ + / / \ + V / \ + | / \ +\[[interprets]] AP N + / \ | + \[[complex]] \[[phrases]] +

+Extensional types Intensional types Examples +------------------------------------------------------------------- + +S t s->t John left +DP e s->e John +VP e->t (s->e)->s->t left +Vt e->e->t (s->e)->(s->e)->s->t saw +Vs t->e->t (s->t)->(s->e)->s->t thought ++ +This system is modeled on the way Montague arranged his grammar. +There are significant simplifications: for instance, determiner +phrases are thought of as corresponding to individuals rather than to +generalized quantifiers. + +The main difference between the intensional types and the extensional +types is that in the intensional types, the arguments are functions +from worlds to extensions: intransitive verb phrases like "left" now +take so-called "individual concepts" as arguments (type s->e) rather than plain +individuals (type e), and attitude verbs like "think" now take +propositions (type s->t) rather than truth values (type t). +In addition, the result of each predicate is an intension. +This expresses the fact that the set of people who left in one world +may be different than the set of people who left in a different world. +(Normally, the dependence of the extension of a predicate to the world +of evaluation is hidden inside of an evaluation coordinate, or built +into the the lexical meaning function, but we've made it explicit here +in the way that the intensionality monad makes most natural.) + +The intensional types are more complicated than the extensional +types. Wouldn't it be nice to make the complicated types available +for those expressions like attitude verbs that need to worry about +intensions, and keep the rest of the grammar as extensional as +possible? This desire is parallel to our earlier desire to limit the +concern about division by zero to the division function, and let the +other functions, like addition or multiplication, ignore +division-by-zero problems as much as possible. + +So here's what we do: + +In OCaml, we'll use integers to model possible worlds. 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: + + type s = int;; + type e = char;; + type t = bool;; + + let ann = 'a';; + let bill = 'b';; + let cam = 'c';; + + let left1 (x:e) = true;; + let saw1 (x:e) (y:e) = y < x;; + + left1 ann;; (* true *) + saw1 bill ann;; (* true *) + saw1 ann bill;; (* false *) + +So here's our extensional system: everyone left, including Ann; +and Ann saw Bill (`saw1 bill ann`), but Bill didn't see Ann. (Note that the word +order we're using is VOS, verb-object-subject.) + +Now we add intensions. Because different people leave in different +worlds, the meaning of *leave* must depend on the world in which it is +being evaluated: + + let left (x:e) (w:s) = match (x, w) with ('c', 2) -> false | _ -> true;; + left ann 1;; (* true: Ann left in world 1 *) + left cam 2;; (* false: Cam didn't leave in world 2 *) + +This new definition says that everyone always left, except that +in world 2, Cam didn't leave. + +Note that although this general *left* is sensitive to world of +evaluation, it does not have the fully intensionalized type given in +the chart above, which was `(s->e)->s->t`. This is because +*left* does not exploit the additional resolving power provided by +making the subject an individual concept. In semantics jargon, we say +that *leave* is extensional with respect to its first argument. + +Therefore we will adopt the general strategy of defining predicates +in a way that they take arguments of the lowest type that will allow +us to make all the distinctions the predicate requires. When it comes +time to combine this predicate with monadic arguments, we'll have to +make use of various lifting predicates. + +Likewise, although *see* depends on the world of evaluation, it is +extensional in both of its syntactic arguments: + + let saw x y w = (w < 2) && (y < x);; + saw bill ann 1;; (* true: Ann saw Bill in world 1 *) + saw bill ann 2;; (* false: no one saw anyone in world 2 *) + +This (again, partially) intensionalized version of *see* coincides +with the `saw1` function we defined above for world 1; in world 2, no +one saw anyone. + +Just to keep things straight, let's review the facts: + +

+ World 1: Everyone left. + Ann saw Bill, Ann saw Cam, Bill saw Cam, no one else saw anyone. + World 2: Ann left, Bill left, Cam didn't leave. + No one saw anyone. ++ +Now we are ready for the intensionality monad: + +

+type 'a intension = s -> 'a;; +let unit x = fun (w:s) -> x;; +(* as before, bind can be written more compactly, but having + it spelled out like this will be useful down the road *) +let bind u f = fun (w:s) -> let a = u w in let u' = f a in u' w;; ++ +Then the individual concept `unit ann` is a rigid designator: a +constant function from worlds to individuals that returns `'a'` no +matter which world is used as an argument. This is a typical kind of +thing for a monad unit to do. + +Then combining a predicate like *left* which is extensional in its +subject argument with an intensional subject like `unit ann` is simply bind +in action: + + bind (unit ann) left 1;; (* true: Ann left in world 1 *) + bind (unit cam) left 2;; (* false: Cam didn't leave in world 2 *) + +As usual, bind takes a monad box containing Ann, extracts Ann, and +feeds her to the extensional *left*. In linguistic terms, we take the +individual concept `unit ann`, apply it to the world of evaluation in +order to get hold of an individual (`'a'`), then feed that individual +to the extensional predicate *left*. + +We can arrange for a transitive verb that is extensional in both of +its arguments to take intensional arguments: + + let lift2' f u v = bind u (fun x -> bind v (fun y -> f x y));; + +This is almost the same `lift2` predicate we defined in order to allow +addition in our division monad example. The difference is that this +variant operates on verb meanings that take extensional arguments but +returns an intensional result. Thus the original `lift2` predicate +has `unit (f x y)` where we have just `f x y` here. + +The use of `bind` here to combine *left* with an individual concept, +and the use of `lift2'` to combine *see* with two intensional +arguments closely parallels the two of Montague's meaning postulates +(in PTQ) that express the relationship between extensional verbs and +their uses in intensional contexts. + +

+lift2' saw (unit bill) (unit ann) 1;; (* true *) +lift2' saw (unit bill) (unit ann) 2;; (* false *) ++ +Ann did see bill in world 1, but Ann didn't see Bill in world 2. + +Finally, we can define our intensional verb *thinks*. *Think* is +intensional with respect to its sentential complement, though still extensional +with respect to its subject. (As Montague noticed, almost all verbs +in English are extensional with respect to their subject; a possible +exception is "appear".) + + let thinks (p:s->t) (x:e) (w:s) = + 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. + + bind (unit ann) (thinks (bind (unit bill) left)) 1;; + +So in world 1, Ann thinks that Bill left (because in world 2, Bill did leave). + + bind (unit ann) (thinks (bind (unit cam) left)) 1;; + +But in world 1, Ann doesn't believe that Cam left (even though he +did leave in world 1: `bind (unit cam) left 1 == true`). Ann's thoughts are hung up on +what is happening in world 2, where Cam doesn't leave. + +*Small project*: add intersective ("red") and non-intersective + adjectives ("good") to the fragment. The intersective adjectives + will be extensional with respect to the nominal they combine with + (using bind), and the non-intersective adjectives will take + intensional arguments. + + diff --git a/topics/_week8_monads_in_general.mdwn b/topics/_week8_monads_in_general.mdwn deleted file mode 100644 index 6dfbdeb8..00000000 --- a/topics/_week8_monads_in_general.mdwn +++ /dev/null @@ -1,434 +0,0 @@ -Monads in General ------------------ - -We've just seen 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` -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 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 -we're adding or multiplying, we don't have to worry about generating -any new errors, so we would rather not think about the difference -between `int`s and `int option`s. We tried to accomplish this by -defining a `bind` operator, which enabled us to peel away the `option` -husk to get at the delicious integer inside. There was also a -homework problem which made this even more convenient by defining a -`lift` operator that mapped any binary operation on plain integers -into a lifted operation that understands how to deal with `int -option`s in a sensible way. - -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 - 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 `u` has type `int option`, - then `u` is a box that (may) contain an integer. - - type 'a option = None | Some of 'a;; - -* A way to turn an ordinary value into a monadic value. In OCaml, we - did this for any integer `x` by mapping it to - the option `Some x`. In the general case, this operation is - known as `unit` or `return.` Both of those names are terrible. This - operation is only very loosely connected to the `unit` type we were - discussing earlier (whose value is written `()`). It's also only - very loosely connected to the "return" keyword in many other - programming languages like C. But these are the names that the literature - uses. [The rationale for "unit" comes from the monad laws - (see below), where the unit function serves as an identity, - just like the unit number (i.e., 1) serves as the identity - object for multiplication. The rationale for "return" comes - from a misguided desire to resonate with C programmers and - other imperative types.] - - The unit/return operation is a way of lifting an ordinary object into - the monadic box you've defined, in the simplest way possible. You can think - of the singleton function as an example: it takes an ordinary object - and returns a set containing that object. In the example we've been - considering: - - let unit x = Some x;; - val unit : 'a -> 'a option =