author jim Wed, 18 Feb 2015 15:49:18 +0000 (10:49 -0500) committer Linux User Wed, 18 Feb 2015 15:49:18 +0000 (10:49 -0500)

index afb7a9e..f3f61b4 100644 (file)
@@ -1,48 +1,50 @@
-What is `()`?
-=============
+[[!TOC]]

-Recall the notion of "data structures" discussed at the end of this week's [[further notes on lists|week3 lists#v5-lists]]. Our (first model of) "cmyg-colors" included four variants, three of which had no parameters and the last of which had two number parameters (one for *brightness* and another for *glossiness*). (Subsequently, we altered this to replace one of those parameters with another cmyg-color; but for present purposes let's stick with the first model.)
+What is *unit*?
+===============

-In the other notes, we discussed how to encode such data structures in the Lambda Calculus. Now let's consider another notation that's sometimes used to talk about them. We might express the data structure just described like this:
+Recall the notion of **data structures** discussed at the end of this week's [[further notes on lists|week3 lists#v5-lists]]. Our (first model of) "cmyg-colors" included four **variants**, three of which had no **parameters** and the last of which had two number parameters (one for *brightness* and another for *glossiness*). (Subsequently, we altered this to replace one of those parameters with another cmyg-color; but for present purposes let's stick with the first model.)
+
+In the other notes, we discussed how to encode such data structures in the Lambda Calculus. Now let's consider another notation that's sometimes used to talk about them. We might describe such data structures like this:

CMYG_color = Cyan () | Magenta () | Yellow () | Gray (Number, Number)

CMYG_color = Cyan () | Magenta () | Yellow () | Gray (Number, Number)

-The component `Cyan ()` says this is one variant, that we will associate with the *tag* `Cyan`, and this variant has no parameters. Here we write that as `()`, for better consistency with the final case where the variant has multiple parameters, but different notational conventions, such as omitting the `()`, would also be possible. The final component `Gray (Number, Number)` says the last variant is associated with the tag `Gray`, and also specifies that this variant has two parameters, and that we expect each of them to be a number. The vertical bar `|` is just a syntactic separator, like a comma.
+The component `Cyan ()` says here is one variant, that we will associate with the *tag* `Cyan`, and this variant has no parameters. Here we write that as `()`, for better consistency with the final case where the variant has multiple parameters, but different notational conventions, such as omitting the `()`, would also be possible. The final component `Gray (Number, Number)` says the last variant is associated with the tag `Gray`, and also specifies that this variant has two parameters, and that we expect each of them to be a number. The vertical bar `|` is just a syntactic separator, like a comma.

-These "tags" are also called "constructors", or more specifically "data constructors". (Later we will encounter other kinds of constructors. But "constructor" without any modifiers or special context can be assumed to mean these things.) The reason they are called that is that the way to specify some *instance* of this data structure is to write things like:
+These "tags" are also called **constructors**, or more specifically "data constructors". (Later we will encounter other kinds of constructors. But "constructor" without any modifiers or special context can be assumed to mean these things.) The reason they are called that is that the way to specify some *instance* of this data structure is to write things like:

Cyan ()
Gray (5, 0)

Cyan ()
Gray (5, 0)

-These may look like function applications, but they're not. They're canonical "constructions" of those instances of the data structure. Notice that in the `Gray` case, we construct an instance using specific numbers; whereas in the specification of the data structure itself, we used not specific numbers but instead the general type `Number`.
+These may look like function applications, but they're not. They're canonical "constructions" of those instances of the data structure. Notice that in the `Gray` case, we construct an instance using specific numbers; whereas in the description of the data structure itself, we used not specific numbers but instead the general type `Number`.

-(Here too other conventions would be possible, such as, again, omitting the `()` after the `Cyan`. Here we follow the general conventions of Haskell and OCaml in capitalizing the alphabetic names of constructors; whereas, on the other hand, variables bound to functions begin with lowercase letters. In fact, in both languages there are also some constructors written outside this convention. Our friends that we express in Kapulet as `[]` and `&` can be thought of as constructors, too. Haskell expresses those as `[]` and `:` and OCaml as `[]` and `::`. Also, as we said in the other notes, we might think of the boolean truth-values as variants in a data structure. Following the conventions here, the most consistent nomenclature would then be to designate them `True ()` and `False ()`. Haskell sort-of does that, but omits the `()`. OCaml also omits the `()` and in this unique case has the variants be expressed all lower-case rather than capitalized.)
+(Here too other conventions would be possible, such as, again, omitting the `()` after the `Cyan`. Here we also follow the general convention of Haskell and OCaml in capitalizing the alphabetic names of constructors; whereas, on the other hand, variables bound to functions begin with lowercase letters. In fact, in both languages there are also some constructors written outside this convention. Our friends that we express in Kapulet as `[]` and `&` can be thought of as constructors, too. Haskell expresses those as `[]` and `:` and OCaml as `[]` and `::`. Also, as we said in the other notes, we might think of the *boolean truth-values* as variants in a data structure. Following the conventions here, the most consistent nomenclature would then be to designate them `True ()` and `False ()`. Haskell sort-of does that, but omits the `()`. OCaml also omits the `()` and in this unique case expresses the variants all lower-case rather than capitalized.)

We will be working with all these ideas more in later weeks.

We will be working with all these ideas more in later weeks.

-We said that it is also possible to think of triples as a data structure, that has only one variant with three parameters. We could express that in Kapulet like this:
+We said that it is possible to think of *triples* as a data structure, too, that has only one variant with three parameters. We could describe that in Kapulet like this:

... = Triple (Number, Number, Boolean)

... = Triple (Number, Number, Boolean)

-Here I just chose specific types for the three parameters. In later weeks we'll see ways to describe triples more generally, without pre-committing to such specific types. We could also write this in Kapulet:
+Here I just chose specific types for the three parameters. In later weeks we'll see ways to describe triples more generally, without pre-committing to such specific types. We could also write the following in Kapulet:

... = Trio (Number, Number, Boolean)

... = Trio (Number, Number, Boolean)

-and if we had both of these, these two data structures would be regarded as distinct, even though they are exactly isomorphic. I mention this only so that you have some idea of why we're choosing the names we are for the variants. The answer is: No reason, it's arbitrary. Different names for the variants would give us distinct but completely isomorphic structures. Let's move on.
+and if we had both of those, they'd be two distinct data structures, even though they are exactly isomorphic. I mention this only so that you have some idea of why we're choosing the names we are for the variants. The answer is: No reason, it's arbitrary. Different names for the variants would give us distinct but completely isomorphic structures.

-Now what about a data structure that has only one variant, that takes *no* parameters. We could express that like this:
+Now what about a data structure that has only one variant, that takes *no* parameters. We could describe that like this:

... = Unit ()

... = Unit ()

-"Unit" is a standard name for this data structure, for the reason that there can be only one instance of the whole structure. In the case of the Boolean structure, there are two possibilities, and in the case of triples, there are as many possibilities as there are choices of the three parameters. But here there is only one possibility. It may be hard to conceive in advance how or why such a data structure could be useful. But we will see that it is.
+"Unit" is a standard name for this data structure, for the reason that there can be only one instance of the whole structure. In the case of the Boolean structure, there are two possibilities, and in the case of Triples, there are as many possibilities as there are choices of the three parameters. But here there is only one possibility. It may be hard to conceive in advance how or why such a data structure could be useful. But we will see that it is.

-In various programming contexts, the word `void` is sometimes used, with varying meanings. A fair portion of those meanings overlap with the notion we're here calling *unit*. (Though the correspondence isn't perfect or complete.)
+In various programming contexts, the word "void" is sometimes used, with varying meanings. Some of those meanings overlap with the notion we're here calling *unit*. (Though the correspondence isn't perfect or complete.)

-So those are two notions in Kapulet: triples, instances of which we specify as `Triple (5, 0, 'false)` (or maybe it should be `Triple (5, 0, False ())`), and unit(s), instance(s) of which can only be specified as `Unit ()`.
+So, there are two notions in Kapulet: triples, instances of which we specify as `Triple (5, 0, 'false)` (or maybe it should be `Triple (5, 0, False ())`), and unit(s), instance(s) of which can only be specified as `Unit ()`.

-As we've mentioned before, though, Kapulet also has parallel, lighter-weight versions of these. In the length-three case there is just the syntax for supplying three arguments at once to an uncurried function, as in `f (5, 0, 'false)`. In the length-zero case we might have instead just `f ()`. (Again, it may be hard to conceive in advance how or why we'd apply a function to no arguments; but read on.) Indeed, it looks like something of the same sort is also going on in our notation `Triple (5, 0, 'false)` and so on.
+As we've observed before, though, Kapulet also has parallel, *lighter*-weight notion of a tuple or multi-value. In the length-three case, we have the syntax for supplying three arguments at once to an uncurried function, as in `f (5, 0, 'false)`. In the length-zero case we might say instead just `g ()`. (Again, it may be hard to conceive in advance how or why we'd apply a function to no arguments; but read on.) Indeed, it looks like something of the same sort is also going on in our notation `Triple (5, 0, 'false)` and so on.

-It is conceptually helpful to separate some different ideas here, which is why Kapulet has these different notions. One difference is that only the "heavier-weight" triple is a single value that can be bound to a single value. So you can do this:
+It's conceptually helpful to separate some different ideas here, which is why Kapulet has these different notions. One difference is that only the heavier-weight triple is a single value that can be bound to a single variable. So you can do this:

let
x match Triple (5, 0, 'false)

let
x match Triple (5, 0, 'false)
@@ -64,7 +66,7 @@ A second, related difference comes up when we consider how these different thing

Pair (Triple (5, 0, 'false), 12)

Pair (Triple (5, 0, 'false), 12)

-that Triple is just another value that occupies the first position in the Pair, in the same that the number `12` occupies the second position. You could also have sequences or sets of these Triples.
+that Triple is just another value that occupies the first position in the Pair, in the same that the Number `12` occupies the second position. You can also have sequences or sets of such Triples.

On the other hand, you can't say:

On the other hand, you can't say:

@@ -86,25 +88,23 @@ and those would mean the same as:

Similarly, `Quadruple (5, 0, 'false, (), 12)` would also mean the same; though again you may not be able to imagine cases where it's useful to write it in that longer form. On the other hand, you could not say `Quadruple (5, 0, 'false, Unit (), 12)`, because that would be trying to construct a quadruple out of *five* values.

Similarly, `Quadruple (5, 0, 'false, (), 12)` would also mean the same; though again you may not be able to imagine cases where it's useful to write it in that longer form. On the other hand, you could not say `Quadruple (5, 0, 'false, Unit (), 12)`, because that would be trying to construct a quadruple out of *five* values.

-As I said, there is some conceptual benefit to having both the heavy-weight and the light-weight notion of a triple, and the notions of `Unit ()` versus `()` are the corresponding, limiting cases of these. What we will discuss below about the ways in which `()` could be useful really applies to *both* of Kapulet's `Unit ()` and Kapulet's `()`. It's just that which of these notions you focus on will affect how you conceptualize what's going on. If I say:
+As I said, there is some conceptual benefit to having both the heavyweight and the lightweight notion of a triple, and the notions of `Unit ()` versus `()` are the corresponding, limiting cases of these. Our discussion below of the ways in which *unit* could be useful really applies to *both* of Kapulet's `Unit ()` and Kapulet's `()`. It's just that which of these notions you focus on will affect how you conceptualize what's going on. If I say:

-    f ()
+    g ()

-in Kaupulet, we should think of that as *applying* the function `f` somehow, but to no arguments. That's an odd idea; how is it different from just the function `f` unapplied? We'll discuss this below. On the other hand, if I say:
+in Kapulet, we should think of that as *applying* the function `g` somehow, but to no arguments. That's an odd idea; how is it different from just the function `g` unapplied? We'll discuss this below. On the other hand, if I say:

-    g (Unit ())
+    h (Unit ())

-we should think of that as applying the function `g` to a single argument, only here it is an argument that is the only possible instance of its data structure. This might seem more comprehensible than `f ()`, but if we can assume that `g` only accepts arguments from the data structure to which `Unit ()` belongs, it really ought to be just as puzzling. If there's only possible argument that `g` accepts, then again, how or why should applying `g` to that argument be different from just the function `g` unapplied. Indeed, in logic settings this is sometimes how we model *constants*: as functors from singleton domains.
+we should think of that as applying the function `h` to a single argument, only here it is an argument that is the only possible instance of its data structure. This might seem more comprehensible than `g ()`, but if we can assume that `h` only accepts arguments from the data structure to which `Unit ()` belongs, it really ought to be just as puzzling. If there's only possible argument that `h` accepts, then again, how or why should applying `h` to that argument be different from just the function `h` unapplied. Indeed, in logic settings this is sometimes how we model *constants*: as functors from singleton domains.

So we have yet to get a good sense of the usefulness of *unit*. But in Kapulet at least we're getting some handle on how to manipulate and talk about it/them.

So we have yet to get a good sense of the usefulness of *unit*. But in Kapulet at least we're getting some handle on how to manipulate and talk about it/them.

-() in Other Languages
-=====================
-
-Other programming languages make the picture messier.
+Other programming languages make the picture messier
+====================================================

-Haskell isn't so bad. In Haskell the story is simple: there Kapulet's `()` and `(5, 0, 'false)` are not part of the language; only Kapulet's `Unit ()` and `Triple (5, 0, 'false)` are. Awkwardly, though, the way Haskell *expresses* the latter two notions is like this:
+Haskell isn't so bad. In Haskell the story is simple: Kapulet's `()` and `(5, 0, 'false)` are not part of the language; only Kapulet's `Unit ()` and `Triple (5, 0, 'false)` are. Awkwardly, though, the way Haskell *expresses* the latter two notions is like this:

()

()
@@ -112,7 +112,7 @@ Haskell isn't so bad. In Haskell the story is simple: there Kapulet's `()` and `

Still, it is just using the same notions Kapulet expresses more verbosely.

Still, it is just using the same notions Kapulet expresses more verbosely.

-In OCaml, things are somewhat like they are in Haskell. The primary notions are Kapulet's heavier-weight tuples, and they are expressed using sparer syntax:
+In OCaml, things are somewhat as in Haskell. The primary notions are Kapulet's heavier-weight tuples, and they are expressed using sparer syntax:

(* OCaml *)
()

(* OCaml *)
()
@@ -120,7 +120,7 @@ In OCaml, things are somewhat like they are in Haskell. The primary notions are

But there are a few odd quirks in OCaml that only make sense if you posit a tacit distinction between these tuples and a notion like Kapulet's lighter-weight tuples (for which OCaml has no explicit syntax). I don't want to detail those quirks now; I'm just saying they are there.

But there are a few odd quirks in OCaml that only make sense if you posit a tacit distinction between these tuples and a notion like Kapulet's lighter-weight tuples (for which OCaml has no explicit syntax). I don't want to detail those quirks now; I'm just saying they are there.

-Scheme is the most complicated.
+Scheme is much more complicated.

Firstly, Scheme does have notions that systematically parallel Kapulet's lighter-weight tuples. What Kapulet writes as:

Firstly, Scheme does have notions that systematically parallel Kapulet's lighter-weight tuples. What Kapulet writes as:

@@ -140,15 +140,15 @@ most closely corresponds in Scheme to:

(lambda (x) (values x 0 #f))

(lambda (x) (values x 0 #f))

-Following this pattern, Kapulet's `()` most closely corresponds to Scheme's `(values)`. Scheme's `(values 5)` evaluates the same as just the simple `5` (as does Kapulet's `(5)`).
+Following this pattern, Kapulet's `()` most closely corresponds to Scheme's `(values)`. Scheme's `(values 5)` evaluates the same as just bare `5` (as does Kapulet's `(5)`).

-Scheme's handling of these multiple-value returns is not completely and smoothly integrated into the language though. In later versions of the Scheme standard, they are better-integrated, and there are common extensions to make things even smoother, but it's still not perfect. You can't write things like this:
+Scheme's handling of these multi-value returns is not completely and smoothly integrated into the language though. In later versions of the Scheme standard, they are better-integrated, and there are common extensions to make things even smoother, but it's still not perfect. You can't write things like this:

(f (values 5 0) #f)

(f (values 5 0) #f)

-and expect it to work the same as `(f 5 0 #f)`. Chicken will just use the first value, `5`, and discard any subsequent values. Racket will instead complain, that you have it two values in a place where it was expecting only one. So although these light-weight multi-values exist in Scheme, they are only occasionally used.
+and expect it to work the same as `(f 5 0 #f)`. Chicken will just use the first value, `5`, and discard any subsequent values. Racket will instead complain, that you gave it two values in a place where it was expecting only one. So although these lightweight multi-values exist in Scheme, they are only occasionally used.

-Instead, Scheme generally works with heavier-weight collections. But which ones? Let's focus on triples first (or really any *n*-tuple, for *n* at least two), where the answer is already complex. It becomes even moreso in the case of unit(s).
+Instead, Scheme generally works with heavier-weight collections. But which ones? Let's focus on triples first (or really any *n*-tuple, for *n* at least two), where the answer is already complex. It will become even more so in the case of unit(s).

Scheme has two parallel notions for expressing longer *n*-tuples. The more straightforward one is called a *vector*. You can build a vector in Scheme like this:

Scheme has two parallel notions for expressing longer *n*-tuples. The more straightforward one is called a *vector*. You can build a vector in Scheme like this:

@@ -158,13 +158,13 @@ and Scheme will display the result like this:

#(5 0 #f)

#(5 0 #f)

---- perhaps with a further single-quote prefix, depending on your configuration. It's also possible to specify a vector using that latter syntax. Vectors are like tuples in Kapulet, Haskell, and OCaml, in that their different elements can be of heterogenous types. They are like Kapulet's heavy-weigh tuples in that they can be assigned to single variables, can be discrete elements in other structures, including other vectors, and so on. Scheme vectors are *unlike* the tuple structures in the other languages in that they are usually *mutable*: one and numerically the same vector container can contain different elements at different stages in the program's evaluation. But some Scheme implementations also have immutable vectors.
+--- perhaps with a further single-quote prefix, depending on your configuration. It's also possible to specify a vector using that latter syntax. Vectors are like tuples in Kapulet, Haskell, and OCaml, in that their different elements can be of heterogenous types. They are like Kapulet's heavyweight tuples in that they can be assigned to single variables, can be discrete elements in other structures, including other vectors, and so on. Scheme vectors are *unlike* the tuple structures in the other languages in that they are usually *mutable*: one and numerically the same vector container can contain different elements at different stages in the program's evaluation. But some Scheme implementations also have immutable vectors.

-The other notion in Scheme for expressing longer *n*-tuples is what I'll call the possibly-improper list, or *imp*. (This isn't standard Scheme terminology; I think it's conceptually cleaner to start here and work your way forward to the standard Scheme ways of talking.) I won't say yet how you tell Scheme to construct an imp, but they are displayed like this:
+The other notion in Scheme for expressing longer *n*-tuples is what I'll call the possibly-improper list, or *imp*. (This isn't standard Scheme terminology. I think it's conceptually cleaner to start here and work your way torward the standard Scheme ways of talking.) I won't say yet how you tell Scheme to construct an imp, but they are displayed like this:

(5 0 . #f)

(5 0 . #f)

-Again, perhaps with a further single-quote prefix, depending on your configuration. In the special case where the imp is of length 2, these are called "dotted pairs":
+--- or perhaps with a further single-quote prefix, depending on your configuration. In the special case where the imp is of length 2, these are called "dotted pairs":

(0 . #f)

(0 . #f)

@@ -177,13 +177,13 @@ specifies the same length-three imp displayed above. However, if you try this:
(let* ((x 5))
'(x 0 . #f))

(let* ((x 5))
'(x 0 . #f))

-you won't get the same thing. Instead, you'll get a triple whose first element is the *symbol* `'x`. Similarly, if you try this:
+you won't get the same thing. Instead, you'll get a length-three imp whose first element is the *symbol* `'x`. Similarly, if you try this:

'(5 (- 3 y) . #f)

you won't get an imp whose second element is `0`, even when the variable `y` is bound to the value `3`. Instead, you'll get an imp whose second element is *another imp*, that begins with the symbol `'-` and continues with the number 3.

'(5 (- 3 y) . #f)

you won't get an imp whose second element is `0`, even when the variable `y` is bound to the value `3`. Instead, you'll get an imp whose second element is *another imp*, that begins with the symbol `'-` and continues with the number 3.

-(By the way, in all of these languages the initial position in a sequence is called position 0, the next position 1, and so on. Some languages start counting from 0, others start counting from 1. Nowadays, most do the former. When we use English ordinals, though, we will always use "first" to mean the initial position.)
+(By the way, in all of these languages the initial position in a sequence is called position 0, the next position 1, and so on. Some languages start counting from 0, others start counting from 1. Nowadays, most do the former. When we use English ordinals in these web pages, though, we will always use "first" to mean the initial position.)

If you try to write simply:

If you try to write simply:

@@ -195,21 +195,21 @@ There are important semantic generalities about how Scheme works that underwrite

'(- 3 y)

'(- 3 y)

-might be, depending on your configuration, displayed as:
+might, depending on your configuration, be displayed as:

(- 3 y)

(- 3 y)

-But if you submit that expression to Scheme, it gets evaluated to the result of applying the subtraction function (or whatever function `-` is bound to, you can rebind it) to the arguments `3` and whatever value the variable `y` is then bound to. What is going on here is that the Scheme expression most recently displayed is itself an imp. Scheme doesn't think of its programs as flat strings of characters. It thinks of them as already parsed into imps that contain numbers, symbols, and other imps. (You can think of these possibly deeply-embedded imps as representing syntax trees.) Asking Scheme to *evaluate* such an imp means for it to apply the function value it gets by evaluating the imp's head, if there be such, to the values it gets by evaluating the other elements in the imp. When you instead specify to Scheme the *quoted* form:
+But if you submit *that, unquoted* expression to Scheme, it gets *evaluated* to the result of *applying* the subtraction function (or whatever function `-` is bound to, you can rebind it) to the arguments `3` and whatever value the variable `y` is then bound to. What is going on here is that the Scheme expression being evaluated is *itself* an imp. Scheme doesn't think of its programs as flat strings of characters. It thinks of them as already parsed into imps that contain numbers, symbols, and other imps. (You can think of these possibly deeply-embedded imps as constituting syntax trees.) Asking Scheme to *evaluate* such an imp means for it to apply the function value it gets by evaluating the imp's head, if there be such, to the values it gets by evaluating the other elements in the imp. When you instead specify to Scheme the *quoted* form:

'(- 3 y)

that refers to *the imp itself*, rather than to the result of evaluating it in the way just described. Now you see why we call this "quotation", and use the symbol we do for it. You might also see the relation between the *symbol* `'y` (or just `y` when it occurs embedded in the quoted imp above, or even more deeply embedded, as in `'(5 (- 3 y) . #f)`) and the *variable* `y`. For Scheme, symbols just *are* variables, only not yet evaluated. That's why we write the symbol with an initial single-quote.

'(- 3 y)

that refers to *the imp itself*, rather than to the result of evaluating it in the way just described. Now you see why we call this "quotation", and use the symbol we do for it. You might also see the relation between the *symbol* `'y` (or just `y` when it occurs embedded in the quoted imp above, or even more deeply embedded, as in `'(5 (- 3 y) . #f)`) and the *variable* `y`. For Scheme, symbols just *are* variables, only not yet evaluated. That's why we write the symbol with an initial single-quote.

-The major difference in Scheme between vectors and imps is that imps have this special relation to Scheme's own syntax. Scheme treats its code as itself being a complex imp, not as being a complex vector. Another difference is that under the hood, the computer implements vectors and imps differently. Vectors store all of their elements in a contiguous block of memory (an "array"), whereas imps may store them scattered all over the place (as a "linked list"). On early computing hardware, the latter was oftentimes more useful; on contemporary hardware, it's much less so. But neither vectors nor imps are closer models of (heavy-weight) tuples in Kapulet, OCaml, and Haskell than the other. They can both contain type-heterogenous elements, including other vectors and/or imps. Like vectors, but unlike the structures in other languages, imps in Scheme are by default mutable. But many implementations also offer immutable imps.
+The major difference in Scheme between vectors and imps is that imps have this special relation to Scheme's own syntax. Scheme treats its code as itself being complex imps, not as being complex vectors. Another difference is that under the hood, the computer implements vectors and imps differently. Vectors store all of their elements in a contiguous block of memory (an "array"), whereas imps may store them scattered all over the place (as a "linked list"). On early computing hardware, the latter was often-times more useful; on contemporary hardware, it's much less so. But neither vectors nor imps are closer models of (heavyweight) tuples in Kapulet, OCaml, and Haskell than the other. They can both contain type-heterogenous elements, including other vectors and/or imps. Like vectors, but unlike the structures in other languages, imps in Scheme are by default mutable. But many implementations also offer immutable imps.

-Okay, that's the story about longer Scheme containers you should first get your head around. We'll talk about shorter containers, including unit(s), in a moment. I've used idiosyncratic language in talking about these "imp"s, though, and I've suppressed some complications that we should now consider.
+Okay, that's the story you should first get your head around about longer Scheme containers. We'll talk about shorter containers, including unit(s), in a moment. But I've used idiosyncratic language in talking about these "imp"s, and I've suppressed some complications, which we should now consider.

-One complication is that in the special case where the final element in an imp is Scheme's empty list --- that we can specify as `(list)` or `'()` or in some implementations as `null` (with no surrounding parentheses) --- in that case Scheme calls the imp a *(proper) list*. (Hence my term *possibly improper* list for the general notion.) And in that case the list can be written, not only as:
+One complication is that in the special case where the final element in an imp is Scheme's empty list --- that we can specify as `(list)` or `'()` or in some implementations as `null` (with no surrounding parentheses) --- in that case Scheme calls the imp a *(proper) list*. (Hence my term *possibly-improper* list for the general notion.) And in that case the list can be written, not only as:

'(- 3 y . ())

'(- 3 y . ())

@@ -219,38 +219,36 @@ but also as:

with no `.` and final element. The lack of a `.` and final element means that the imp's final element is the empty list, and that this *possibly* improper list is in fact proper. So what Scheme calls its "lists" are in fact a special case of one of its equally-good candidates (vectors and imps) to be identified with Kapulet's, OCaml's, and Haskell's tuples. Scheme doesn't make the sharp distinction between tuples and lists that the other languages do.

with no `.` and final element. The lack of a `.` and final element means that the imp's final element is the empty list, and that this *possibly* improper list is in fact proper. So what Scheme calls its "lists" are in fact a special case of one of its equally-good candidates (vectors and imps) to be identified with Kapulet's, OCaml's, and Haskell's tuples. Scheme doesn't make the sharp distinction between tuples and lists that the other languages do.

-A second complication is that FIXME embedded pairs, `cons`.
-
We asked before how you specify an imp. We've seen one notation, using an initial single-quote. That works if you're ready to construct the imp without the help of any variables. But if you want to use variables, what should you do? (There is in fact a variation on quotation, called *quasi-quotation* and using the initial prefix <code>\`</code> rather than `'` that you could use; but I'm not going to explain that.) If you want to build a *proper* imp, that is, a Scheme list, you can instead use the function/constructor `list`:

(list '- 3 y)

returns the imp whose first member is the symbol `'-` (if you left the quote off, you would instead get the function that this symbol is bound to), whose second member is the number 3, whose third value is whatever value the symbol/variable `y` is bound to, and whose final value is the empty list.

We asked before how you specify an imp. We've seen one notation, using an initial single-quote. That works if you're ready to construct the imp without the help of any variables. But if you want to use variables, what should you do? (There is in fact a variation on quotation, called *quasi-quotation* and using the initial prefix <code>\`</code> rather than `'` that you could use; but I'm not going to explain that.) If you want to build a *proper* imp, that is, a Scheme list, you can instead use the function/constructor `list`:

(list '- 3 y)

returns the imp whose first member is the symbol `'-` (if you left the quote off, you would instead get the function that this symbol is bound to), whose second member is the number 3, whose third value is whatever value the symbol/variable `y` is bound to, and whose final value is the empty list.

-What if you want to instead build an *improper* imp, such as `(5 0 . y)`, again using the value `y` is bound to rather than the quoted symbol? There is no form in standard Scheme to do this *directly*. But many Scheme implementations do have a special function for doing this. Racket calls it `list*`, so if you write in Racket:
+What if you want to instead build an *improper* imp, such as `(5 0 . y)`, again using the value `y` is bound to rather than the quoted symbol? There is no form in *standard* Scheme to do this *directly*. But many Scheme *implementations* do have a special function for it. Racket calls it `list*`, so if you write in Racket:

(let* ((y #f))

(let* ((y #f))
-          (cons* 5 0 y))
+          (list* 5 0 y))

You will get the imp whose first member is `5`, whose second member is `0`, and whose final member is `#f` (*not* the symbol `'y`).

Other Scheme implementations that provide this function call it `cons*`, for reasons that will become clearer in a moment.

You will get the imp whose first member is `5`, whose second member is `0`, and whose final member is `#f` (*not* the symbol `'y`).

Other Scheme implementations that provide this function call it `cons*`, for reasons that will become clearer in a moment.

-I said there was no form in *standard* Scheme to do this *directly*. You can however do it indirectly, and the reason why brings us to our last complication about Scheme imps. This is that in fact, Scheme imps of length greater than two are really all built up out of embedded "dotted pairs" (that is, imps of length two). The length-three imp `'(5 0 . #f)` is really implemented in Scheme as a length-two imp, whose first member is `5`, and whose second member is *another* length-two imp, whose first member is `0` and whose second member is `#f`. It could also be written as `'(5 . (0 . #f))`. (This is *not* the same as the imp `'((5 . 0) . #f)`.) Moreover, standard Scheme *does* have a function/constructor to build the simplest, dotted-pair case of imps. And what it calls this operation is `cons`. Yes, the same name we were using to pronounce our list constructor in the other languages. `cons` does play that role in Scheme, too, given the way that Scheme in fact implements (what it calls) lists. If `ys` is bound to one proper list, of any finite length, and `y` is bound to any value, then `(cons y ys)` will build a new dotted-pair that has these values as elements, and given how Scheme implements proper lists, that just is the longer list whose head is `y`'s value and whose tail is `ys`'s value.
+I said there was no form in *standard* Scheme to do this *directly*. You can however do it indirectly, and the reason why brings us to our last complication about Scheme imps. This is that in fact, Scheme imps of length greater than two are really all built up out of embedded "dotted pairs" (that is, imps of length two). The length-three imp `'(5 0 . #f)` is really implemented in Scheme as a length-two imp, whose first member is `5`, and whose second member is *another* length-two imp, whose first member is `0` and whose second member is `#f`. It could also be written as `'(5 . (0 . #f))`. (This is *not* the same as the imp `'((5 . 0) . #f)`.) Moreover, standard Scheme *does* have a function/constructor to build the simplest, "dotted pair" case of imps. And what it calls this operation is `cons`. Yes, the same name we were using to pronounce our list constructor in the other languages. `cons` does play the list constructor role in Scheme, too, given the way that Scheme in fact implements (what it calls) lists. If `ys` is bound to one proper list, of any finite length, and `y` is bound to any value, then `(cons y ys)` will build a new dotted pair that has those values as elements, and given how Scheme implements proper lists, that *just is* the longer list whose head is `y`'s value and whose tail is `ys`'s value.

-The function that Scheme uses to extract the first element of a dotted-pair (and so also to extract the head of a proper or improper list) is `car`; the function it uses to extract the second element of a dotted-pair (and so also the tail of a proper list) is `cdr`. The reasons for these funny names are historical.
+The function that Scheme uses to extract the first element of a dotted pair (and so also to extract the head of a proper or improper list) is `car`; the function it uses to extract the second element of a dotted pair (and so also the tail of a proper list) is `cdr`. The reasons for these funny names are historical.

-If you grew up learning functional programming and list manipulation from Scheme, all these complexities might seem natural to you, and might shape the way you think about notions like *list* and *ordered pair*. Scheme is a great language, but that would be unfortunate. Conceptually, these quirks about scheme are something of a hack. I think you get a better understanding of the conceptual terrain from the other functional programming languages, and by beginning to think about Scheme's lists and dotted-pairs by starting with the notion of a possibly-improper list, rather than the other way around. Most texts teaching Scheme will go in the other direction, though.
+If you grew up learning functional programming and list manipulation from Scheme, all these complexities might seem natural to you, and might shape the way you think about notions like *list* and *ordered pair*. Scheme is a great language, but that would be unfortunate. Conceptually, these quirks about Scheme are something of a hack. I think you get a better understanding of the conceptual terrain from the other functional programming languages, and by beginning to think about Scheme's lists and dotted pairs by starting with the notion of a *possibly-improper* list, rather than the other way around. Most texts teaching Scheme will go in the other direction, though.

Okay, all of that was just us getting clear about Scheme's *longer* containers, of length at least two. What about the shorter containers?

Okay, all of that was just us getting clear about Scheme's *longer* containers, of length at least two. What about the shorter containers?

-Scheme does have vectors of length one: you can write `(vector 5)` or `#(5)`. And that will be distinct from the number `5`. Kapulet is similar: we could have a heavy-weight one-tuple, perhaps `Single 5` (or `Single (5)`, the parentheses make no difference when they contain exactly one syntactic atom), that would be distinct from the number `5`. But there is no difference among light-weight tuples. In Kapulet, `(5)` and `5` would just be notational variants for the number. Although OCaml and Haskell for the most part have tuples corresponding to Kapulet's heavy-weight tuples, in this case they do not. Some subtleties aside, they don't have any one-tuple `(5)` that's distinct from mere `5`.
+Scheme does have vectors of length one: you can write `(vector 5)` or `#(5)`. And that will be distinct from the number `5`. Kapulet is similar: we could have a heavyweight one-tuple, perhaps `Single 5` (or `Single (5)`, the parentheses make no difference when they contain exactly one syntactic atom), that would be distinct from the number `5`. But there is no difference among lightweight tuples. In Kapulet, `(5)` and `5` would just be notational variants for the number. Although OCaml and Haskell for the most part have tuples corresponding to Kapulet's heavyweight tuples, in this case they do not. Some subtleties about their type systems aside, they don't have any native one-tuple `(5)` that's distinct from mere `5`.

-Scheme doesn't have any *imps* of length one, because it builds its imps out of dotted-pairs, so they can't get any smaller than length two.
+Scheme doesn't have any *imps* of length one, because it builds its imps out of dotted pairs, so they can't get any smaller than length two.

-Now how about unit(s)? Here again, Scheme has vectors of that length: you can write `(vector)` or `#()`. And that will in many ways correspond to the heavy-weight Kapulet length-zero tuple `Unit ()`. And here too, there can be no imps that are this short.
+Now how about unit(s)? Here again, Scheme has vectors of that length: you can write `(vector)` or `#()`. And that will in many ways correspond to the heavyweight Kapulet length-zero tuple `Unit ()`. Here too, there can be no imps that are this short.

-But at this point Scheme has *yet another* special value, that in Scheme documentation is usually called *void*. And in many Scheme implementations, there is a special function that generates this value, expressed as `void`. You can call this function with zero or more arguments; they will all be ignored and you will get the special *void* value as a result. Generally Scheme doesn't display this value, if you say:
+But at this point Scheme has *yet another* special value, that in Scheme documentation is usually called *void*. And in many Scheme implementations, there is a special function that generates this value, expressed as `void`. You can apply this function to zero or more arguments; they will all be ignored and you will get the special *void* value as a result. Generally Scheme doesn't display this value. If you say:

(void)

(void)

@@ -259,18 +257,18 @@ or:
(let* ((x (void)))
x)

(let* ((x (void)))
x)

-your Scheme interpreter will probably just not display anything, not even a blank line. If you say this, however:
+your Scheme interpreter will probably just not show any result, not even a blank line. If you wrote this, however:

(display x)

(display x)

-it might say `#<void>` or `#<unspecified>`.
+it might have said `#<void>` or `#<unspecified>`.

-Even if your Scheme implementation lacks the `void` function, though --- as the official standard permits it to do --- you can still generate these special values in some ways. One example is if a `cond` expression has no `else` clause, but none of the clauses that are supplied fail. So this will also generate the special *void* value:
+Even if your Scheme implementation lacks the `void` function, though --- as the official standard permits it to do --- you can still generate the special *void* value in other ways. One example is if a `cond` expression has no `else` clause, but none of the clauses that *are* supplied succeed. So this will also generate the special *void* value:

(cond
(#f 'impossible))

(cond
(#f 'impossible))

-Pulling this all together, finally: Scheme has two heavy-weight values corresponding to Kapulet's `Unit ()`, namely its length-one vector (expressible as `(vector)` or `#()`) and its special *void* value (expressible in various ways). It has two heavy-weight values corresponding to Kapulet's `Triple (0, 5, #f)`, namely a length-three vector and a length-three imp. Corresponding to the *light-weight* Kapulet tuples or multi-values are the Scheme idioms:
+Ok. Let's pull this all together. Scheme has two heavyweight values corresponding to Kapulet's `Unit ()`, namely its length-one vector (expressible as `(vector)` or `#()`) and its special *void* value (expressible in various ways). It has two heavyweight values corresponding to Kapulet's `Triple (0, 5, #f)`, namely a length-three vector and a length-three imp. Corresponding to the *lightweight* Kapulet tuples or multi-values are the Scheme idioms:

; when calling Scheme functions
(f 0 5 #f)

; when calling Scheme functions
(f 0 5 #f)
@@ -285,3 +283,15 @@ and:
I told you the correspondences between all these languages was complex. But now, finally, you should have some handle on how to manipulate and talk about unit(s) in languages besides Kapulet. We still have to figure out what these are good for.

I told you the correspondences between all these languages was complex. But now, finally, you should have some handle on how to manipulate and talk about unit(s) in languages besides Kapulet. We still have to figure out what these are good for.

+Using *unit* as a parameter in a data structure
+===============================================
+