An Introduction to Scheme and its Implementation

(1)

"Ternary" just means "takes three arguments," the ternary operator in C is called that because it's the only ternary operator in C; all the others take fewer than three arguments.

(2)

This also has to do with what you mean by "pointer." I use the word to mean pointers in the sense of building pointer-linked data structures. (Scheme clearly has those.) Some people use "pointers" to mean addresses that are bit patterns you can manipulate directly--the way you can in C, where you can "cast" (coerce) a pointer to an integer and operate on the bits. Some people use "pointer" synonymously with "address", and call what Scheme has object references.

(3)

An unsafe cast is one that the compiler doesn't really understand. A safe cast is one that makes sense to the compiler, such as converting an integer to a floating point number. With unsafe casts, you're essentially telling the compiler "trust me", and bypassing the type systems. This is what happens when you cast a structure pointer to a void* or char* and later cast it back to a structure pointer--you're promising the compiler that the pointer will actually point to a structure of the declared type, and it's up to you to make sure that's true.

(4)

Note that here "variable" just means that it varies, and arity means that what's varying is the number of argument variables. "Arity" comes from words like "nullary," "unary," "binary," and "ternary"---these mean zero-argument, one-argument, two-argument, and three-argument, respectively. (Sometimes people say things like "1-ary" for unary (one argument), "2-ary" for binary, and "n-ary" for "variable arity.")

(5)

Here the analogy may not be very intuitive; it's as though you used a name like x to refer to different "people" at different times, but those people were all alike, and uninteresting except for what you call them and what they point at with their index fingers. In this analogy, set! is like telling one of these very boring people "point at that for now."

(6)

For example, we can call read to read the list (foo 20 (baz)), and then just use that list as a list, rather than interpreting it as a piece of a program.

(7)

Unsurprisingly, a bottom-up parser would do the opposite--it would recognize the smaller constituents first, and then recognize the larger phrases that contain them.

(8)

In the technical terminology of programming language processors, the reader is a predictive parser for an LL(0) grammar. It can parse s-expressions top-down in a single pass through the sequence of tokens, without looking ahead past the current token, because it only needs to see the current token to know what action to take. (E.g., if it sees a left parenthesis, it immediately "knows" that it is parsing a nested list.)

(9)

It's often said that Lisp and Scheme have such a simple syntax that they "don't need a parser," but this is just false. Lisp and Scheme actually have two parsers, because their syntax has a two levels. The "surface" syntax is parenthesized prefix expressions, recognized by the reader and translated into s-expression data structures. There is a "deeper" syntax that is recognized by the interpreter or compiler, which analyzes s-expressions to see what kinds of Scheme program expressions they represent, in the process of evaluating or compiling them. As we'll see when we get to macros, Scheme syntax is even more sophisticated than this, despite its simplicity. Technically, Scheme has a transformational grammar that is not "context-free," but is easy to parse. (If you don't know what that means, don't worry about it. Scheme is easy to understand without knowing the fancy technical terms.)

(10)

The compiler can generally avoid the overhead of closures where it's not actually useful--if you write C-like or Pascal-like programs, the compiler can generate very similar code. And if you really use closures in their full generality, it's not particularly slow--a few extra instructions per procedure call. Creating closures can be surprisingly fast, because Scheme systems typically have allocators several times faster than most C allocators. [should discuss efficiency issues in more detail in a later chapter...]

(11)

This is something of an oversimplification. The quoted expression is generally converted to a list at read time, and the value of the quote expression may be the very same data structure, or it may be a copy made later during compilation or interpretation. At any rate, you should only count on the structure of a quoted data structure, not its identity. A compiler or interpreter is allowed to return the same list for different quoted data structures with the same structure.

(12)

Superclasses can be used to specify a class as a set of differences from an existing class, rather than describing everything about the class from scratch. Here we're saying that the <point> class is just like any other class of objects, and does the normal things classes do, but nothing special except what we specify explictily in the class definition.

(13)

The object <class> is actually the class object of class objects---classes are instances of the class <class>. This will be explained in detail later.

(14)

Evaluating the quasiquote expression `(,<object>) will create a list containing the actual class object that is the value of the variable <object>.

(15)

This is important to understand in being able to read clever macros: a quoted expression isn't just a literal if it has unquoted expressions inside it, because we're generating a literal with the macro. (It's not a literal in the macro, but the resulting s-expression will be a literal when the interpreter or compiler sees it, after the macro has done its transformation--the quoted expression is really a template for a literal, which we can specialize to produce the literal we want.)