[Page 103]
93 Notes { Note ASET' Is Imperative) It is true that ASET' is an actual imperative which produces a side effect, and is not expressed applicatively. ASET' is used only for two purposes in practice: to initialize global variables (often relating to MacLISP primitives), and to implement objects with state (cells, in the PLASMA sense [Smith and Hewitt] [Hewitt and Smith]). If we were to redesign SCHEME from scratch, I imagine that we would introduce cells as our primitive side-effect rather than ASET'. The decision to use ASET' was motivated primarily by the desire to interface easily to the MacLISP environment (and, as a corollary, to be able to implement SCHEME in three days instead of three years!).
We note that in approximately one hundred pages of SCHEME code written by three people, the non-quoted ASET has never been used, and ASET' has been used only a dozen times or so, always for one of the two purposes mentioned above. In most situations where one would like to write an assignment of some kind, macros which expand into applicative constructions suffice.
[Page 104]
94 (Note Code Pointers) Conceptually a closure is made up of a pointer to some code (a 'script'
[Smith and Hewitt]) and an environment. In e RABBIT-formatted CBETA, the pointer to the code is encoded into two levels: a pointer to a particular piece of l'Iacl.ISP code, plus a tag within that FROG. This implementation was forced upon us by MacLISP. If we could easily create pointers into the middle of a FROG, we could avoid this two-level encoding.
On the other hand, this is not just an engineering kludge, but can be provided with a reasonable semantic explanation: rather than compiling a lot of little functions, we compile a single big function which is a giant CASE statement. Wherever we wish to make a closure of a little function, we actually close a different little function which calls the big function with an extra argument to dispatch on.
(Note Continuation Variable Hack) Since the dissertation was written, a simple modification to the routine which converts to continuation-passing style has eliminated some of the register shuffling. The effect of the change was to perform substitutions of one continuation variable for another, in situations such as:
((CLAHBDA (CONT-3 ...) ...) CONT-Z ...) where CONT-2 would be substituted for CONT-3 in the body of the CLAHBDA-expression. Once this is done, CONT-3 is unreferenced, and so is not really passed at all by virtue of the phase-Z analysis. The result Is that continuations are not copied back and forth from register to register. In the
[Page 105]
iterative factorial example in the text the actual register assignment would be CONT-5 **CONT** VAR-1 **ONE** FNVAR-2 <none> VAR-3 **Two** VAR-4 **THREE** This optimization is discussed more thoroughly in the Appendix near the routine CONVERT-COMBINATION.
{ Note Dijkstra's Opinion) In [Dijkstra] a remark is made to the effect that defining the while gg construct in terms of function calls seems unusually clumsy. In [Steele] we reply that this is due partly to Dijkstra's choice of ALGOL for expressing the definition. Here we would add that, while such a definition is completely workable and is useful for compilation purposes, we need never tell the user that we defined while-gg in this manner! Only the writer of the macros needs to know the complexity involved; the user need not, and should not, care as long as the construction works when he uses it.
[Page 106]
96 (Note Evaluation for Control) It is usual U1 a compiler to distinguish at least three 'evaluation contexts": value, control, and effect. (See [Nulf], for example.) Evaluation for control occurs in the predicate of an IF, where the point is not so much to produce a data object as simply to decide whether it is true or false. The results of AND, OR, and NOT operations in predicates are 'encoded in the program counter". when compiling an AND, OR, or NOT, a flag is passed down indicating whether it is for value or for control; in the latter case, two tags are also passed down, indicating the branch targets for success or failure. (This is called "anchor pointing" in [Allen and Cocke].) In RABBIT this notion falls out automatically without any special handling, thanks to the definition of AND and OR as macros expanding into IF statements. If we were also to define NOT as a macro *(NOT x) => (IF x 'NIL 'T) then nearly all such special "evaluation for control" cases would be handled by virtue of the nested-IF transformation in the optimizer.