MDL Primer And Manual

MDL_Primer_and_Manual

User Manual: Pdf

Open the PDF directly: View PDF PDF.
Page Count: 270

DownloadMDL Primer And Manual
Open PDF In BrowserView PDF
:" \
.
,/

'

:" .

..

\

.

MDL
Primer and Manual
(for versions 54 and 104)

S. W. Galley and Greg Pfiater

Laboratory for Computer Science

Massachusetts Institute of Technology
Cambridge

Massachusetts 02139

2

Abstraot
MOL began existence in late 1970 (under the name Muddle) as a successor to Lisp (ref 6), a candidate
vehicle for the Dynamic Modeling S.ystem, and a possible base for implementation of Planner (ref
5). The original design goals included an interactive integrated environment for programming,
debugging, loading, and editing; ease in learning and use; facilities for structured, modular. shared
programs; extensibility of syntax, data types and operators; data-type checking for debugging and
optional data-type declarations for compiled efficiency; associative storage, multiprocessing. and
graphics. Along the way to reaching those goals, it developed flexible input/output (including the
ARPA Network). and flexible interrupt and signal handling. It now serves as a base for software
prototyping. research. development. education. and implementation of the majority of programs at
MIT ·DMS: a Jibrary of sharable modules, a coherent user interf.'lce, special research projects.
autonomous daemons. etc.
This document was originally intended to be a simple low-level introduction to MOL. It has.
ho\vever. acquired a case of elephantiasis and now amounts to a discursive description of the whole
interpreter. as realized in MOL version number 54 (ITS) or 104 (TENEX). A low.level introduction
may still be had by restricting one's attention to specially-marked sections only. The scope of the
document is confined as much as possible to the interpreter itself. Other adjuncts (compiler.
assembler. preloaded user programs, library) are mentioned as little as possible, despite their vahll" in
promoting the language seen by a user from "basic survival" to "comfortable living". Indeed. MOL
could not fulfill the above design goals without the compiler, assembler. structure editor. controlstack printer. context printer. pretty-printer, dynamic loader. and library system .. all of which are
not part of the interpreter but programs written in MOL and symbiotic with one another. Further
information on these adjuncts can be found in the references.

i~ '.1 .~.

, J·.\1.

0

(c) Copyright 1977 Massachusetts Institute of Technology. All rights reserved.

Aoknowledgements
I was not a member of the original group which labored for two years in the design and initial
implementation of Muddle; that group was composed principally of Gerald Sussman. Carl Hewitt.
Chris Reeve. Dave Creuey, and later Bruce Daniels. I would therefore like to take this opportunity
to thank my Muddle mentors, chiefly Chris Reeve and Bruce Daniels, for remaining civil through
several months of verbal badgering. I believe that I learned more than "just another programming
language- in learning Muddle, and I am grateful for this opportunity to pass on some of that
knowledge. What I cannot pass on is the knowledge gained by using Muddle as a system: that I can
only ask you to share.
For editing the content of this document and correcting some misconceptions, I would like to thank
Chris Reeve. Bruce Daniels and especially Gerald Sussman, one of whose good ideas I finally did use.
Greg Pfister
December 15, 1972
Since Greg Jeft the fold, I have taken up the banner and updated his document. The main sources
for small revisions have been the on·line file of changes to MDL, for which credit goes to Neal
Ryan as well as Reeve and Daniels, and the set of on·line abstracts for interpreter Subroutines.
contributed by unnamed members of the Programming Technology Division. Some new sections
were written almost entirely by others: Dave Lebling wrote chapter 14 and appendix 3, Jim Michener
section 14.3. Reeve chapter 19 and appendix I, Daniels and Reeve appendix 2. Brian Berkowitz
section 22.7. Tak To section 17.2.2. and Ryan sectio,n 17.1.3. Sue Pitkin did the tedious task
of marking phrases in the manuscript for indexing. PiUs JarViS and Jack Haverty advised on the
use of PUB and the XCP. Many PTD people commented helpfully on a draft version.
.
.
.
My task has been to impose some uniformity and structure on these diverse sources (50 that the
result sounds less like a dozen hackers typing at a dozen consoles for a dozen days) and to enjoy
some of the richness of MDL from the inside. I especially thank Chris Reeve ("the oracle") for the
patience to answer questions and resolve doubts, as he no doubt has done innumerable times before.
S. W. Galley
May 16.1977

This work was supported by the Advanced Research Projects Agency of the Department of Defense
and was monitored by the Office of Naval Research under contract N00014.75·C·0661.
This document was prepared using the PUB system (originally from the Stanford Artificial
Intelligence Laboratory) and printed on the Xerox Craphics Printer of the M.I.T. Artificial
Intelligence Laboratory.

4

Foreword
Trying to explain MDL to an uninitiate is somewhat like trying to untie a Gordian knot. Whatever
topic one chooses to discuss first, full discussion of it appears to imply discussion of everything
else. What follows Is a discursive presentation of MOL in an order hopefu)]y requiring the fewest
forward references. It is not perfect in that regard; however, if you are patient and willing to
accept a few. stated things as "magic" until they can be explained better. you will probably not have
too many problems understanding what is going on.
There are no "practice problems"; you are assumed to be learning MDL for some purpose. and your
work in achieving that purpose will be more useful and motivated than artificial problems. In
several cases. the examples contain illustrations of important points which are not covered in the
text. Ignore examples at your peril.
This document does not assume knowledge of any specific programming language on the part of
the reader. However, "computational literacy" is assumed: the reader should have written at least one
program before. Also, very little familiarity with ITS or TENEX - the time-sharing operating
systems under which MDL runs -- is assumed. namely just file and user naming conventions.

Notation:
Sections marked [1] are recommended for an uninitiate's first reading, in lieu of a separate
introduction or primer for MDL. [On first reading. text within brackets like these should be
ignored.]
Most specifically indicated examples herein are composed of pairs of lines. The first line of a pair
always ends in $ (which is how the ASCII character ESC is represented, and which always represents
it); this is the input. The second line is the result of MOL's groveling over the first. If you were to
type all the first lines at MOL. it would respond with all the second lines. (More exactly. the "first
line" is an object in MOL (sometimes with an associated comment) followed by $. and the "second
line" is everything up to but not including the next "first line".)
Anything which is written in the MDL language or which is typed on a computer console appears
herein in a gothic font, as in ROOT. A metasyntactic variable - something to be replaced in actual
use by something else - appears as /ooJ$
where file is the name of the file. in standard operating.system syntax. enclosed in "5 (double.
quotes). Omitted parts of the file name are taken from the default: DSK: INPUT > (ITS) or OSK:
INPUT .MUD (TEN EX) in the current disk directory.
Once you type S. MDL wiJI process the text in the file (including FLOADs) exactly as if you had
typed it on a consoJe and followed it with $. except that ·values· produced by the computations are
not printed. When MDL is finished processing the file, it will print "DONE".

1.4. Errors - Simple Considerations. [1]
When MDL decides for some reason that something is wrong, the standard sequence of evaluation is
interrupted and an error function is caUed. This produces the following console output:
IIIERRORIII
often-h yphenated-reason
function-in-which-error-occurred
LISTENING-AT-LEVEL integer PROCESS integer

You can now interact with MDL as usual, typing expressions and having them evaluated. There
exist facUities (built.in functions) allowing you to find out what went wrong. restart, or kiJJ
whatever was going on. In particular. you can recover from an error •• that is, undo everything but
side effects and return to the initial typing phase - by typing the foUowing first line. to which
MDL will respond with the second Jine:
$
LISTENING-AT-LEVEL 1 PROCESS 1

1.2 • 1.4

Basic Interaction

19

If you type the following first line while still in the error state (before $
[

NgulMnts to unh.pp y function

]

This will be explained by and by.

1.4

Basic Interaction

20

Chapter 2. Read, Evaluate, and Print

2.1. General [I]
Once you type $ and all brackets are correctly paired and nested. the current contents of the input
buffer go through processing by three functions successively: first READ, which passes its output to
EVAL (-evaluate"), which passes its output to PRINT. whose output i5 typed on the console.
[Actually, the sequence is more like READ, TERPRI, EVAL, PRIN1, TERPRI (explained in chapter 11);
MDL gives you a carriage-return line-feed when the READ is complete. that is, when all brackets are
paired.]
Functionally,
READ: printable representatiolls

-> MDL objects

EVAL: MDL objects -> MDL objects
PRINT: MDL objects -> printable representations
That is. READ takes ASCII text. such as is typed in at a console, and creates the MDL objecu
represented by that text. PRINT takes MDL objects. creates ASCII text representations of them. and
types them out. EVAl, which is the realIy important one, performs transformations on MDL objects.

2.2. Philosophy (TYPEs) [1]
In a general sense. when you are interacting with MDL. you are dealing with a world inhabited only
by a particular set of objects: MDL objects.

MDL objects are best considered as abstract entities with abstract properties. The properties of a
particular MDL object depend upon the class of MDL objects to which it belongs. This class is the
TYPE of the MDL object. Every MDL Object has a TYPE, and every TYPE has its own peculiarities.
2 - 2.2

Read, Evaluate, and Print

21

There are many different TYPEs in MOL; they will gradually be introduced below. but in the
meantime here is a representative sample: SUBR (the TYPE of READ. EVAL and PRINT), FSUBR, LIST,
VECTOR, FORM, FUNCTION, etc. Since every object has a TYPE, one often abbreviates "an object of
TYPE type" by saying "a type".
The laws of the MDL world are defined by EVAL In a very real sense, EVAL is the only MOL object
which -acu-, which "does something". In "acting", EVAL is always "following the directions" of some
MDL object. Every MDL object should be looked upon as supplying a set of directions to EVAL;
what these directions are depends heavily on the TYPE of the MDL object.
Since EVAL is so ever-present, an abbreviation is in order: "evaluates to something" or "EVALs to
something- should be taken as an abbreviation for "when given to EVAL. causes EVAL to return
something",

As abstract entities, MDL Objects are, of course, not "visible". There is, however, a standard way of
representing abstract MDL objects in the real world. The standard way of representing any given
TYPE of MDL object will be given below when the TYPE is introduced. These standard
representations are what READ understands, and what PRINT produces.

2.1. Example (TYPE FIX) [ll

IS
1

The following has occurred:
First, READ recognized the character 1 as the representation for an object of TYPE FIX, in particular
the one which corresponds to the integer one. (FIX means integer, because the decimal point is
understood always to be in a fixed position: at the right-hand end.) READ built the MDL object
corresponding to the decimal representation typed. and returned it.
Then EVAL noted that its input was of TYPE FiX. An object of TYPE FIX evaluates to itself. so EVAL
returned its input undisturbed.
Then PRINT saw that its input was of TYPE FIX, and printed on the console the decimal character
representation of the corresponding integer.

2.2 - 2.3

Read, Evaluate, and Print

22

2.4. Example (TYPE FLOAD [1]

LOS

1.0
What went on was entirely analogous to the preceding example. except that the MDL object was of
TYPE FLOAT. (FLOAT means a real number (of limited precision). because the decimal point can float
around to any convenient position: an internal exponent part tells where it "really" belongs.)

2.5. Example (TYPE ATOM. PNAME) [1]
GEORGES
GEORGE
This time a lot more happened.
READ noted that what was typed had no special meaning. and therefore assumed that it was the
representation of an identifier. that is. an object of TYPE ATOM. ("Atom" means more or leu
indivisible.) READ therefore attempted to look up the representation in a table it keeps for such
purposes [a LIST of OBLISTs, available as the local value of the ATOM OBLIST]. If READ finds an
ATOM in its table corresponding to the representation. that ATOM is returned as READ's value. If READ
fails in the lookup. it creates a new ATOM, puts it in the table with the representation read [INSERT
into <1 .OBLIST) usually]. and returns the new ATOM. Nothing which could in any way be
referenced as a legal "value" is attached to the new ATOM. The initially-typed representation of an
ATOM becomes its PNAME. meaning its name for PRINT. One often abbreviates "object of TYPE ATOM
with PNAME name" by saying "ATOM name".
EVAL. given an ATOM. returned just that ATOM.
PRINT. given an ATOM. typed out its PNAME.
At the end of this chapter. the question "what is a legal PNAME" will be considered. Further on. the
methods used to attach values to ATOMs will be described.

2.6. FIXes, FLOATs. and ATOMs versus READ: Specifics

2.6.1. READ and FIXed-point Numbers
READ considers any grouping of characters which are solely digits to be a FIX, and the default radix

2.4 - 2.6.1

Read. Evaluate, and Print

23
of the representation is decimal. A - (hyphen) immediately preceding such a grouping represents a
negative FIX. The largest FIX representable on the PDp·lO is two to the 95th power minus one. or
34 359 738 367 (decimaJ); the smallest is one less than the negative of that number. If you attempt
to type in a FIX outside that range. READ converts it to a FLOAT; if a program you write attempts to
produce a FIX outside that range. you will get an overflow error (unless disabled~
The radix used by READ and PRINT is changeable by the user; however. there are two formats for
representations of FIXes which cause READ to use a specified radix independent of the current one.
These are as follows:
(I) If a group of digits is immediately followed by a . (period), READ interprets that group as the
decimal representation of a FIX. For example. 10. is always interpreted by READ as the decimal

representation of ten.
(2) If a group of digits is immediately enclosed on both sides by asterisks (-), READ interprets

that group as the octal representation of a FIX. For example. -10- is always interpreted by
READ as the octal representation of eight.

2.6.2. READ and PRINT versus FLOATing·point Numbers
PRINT can produce. and READ can understand. two different formats for objects of TYPE FLOAT. The
first is "decimal-point" notation. the second is "scientific· notation. Decimal radix is always used
for representations of FLOATs.
"Decimal-point" notation for a FLOAT consists of an arbitrarily long string of digits containing one
• (period) which is followed by at least one digit. READ will make a FLOAT out of any such object.
with a Jimit of precision of one part in 2 to the 27th power.
"Scientific" notation consists of:
(I) a number.
(2) immediately followed by E or e (upper or lower case letter E),
(3) immediately followed by an exponent.

where a "number" is an arbitrarily long string of digits. with or without a decimal point (see
following note); and an "exponent" is up to two digits worth of FIX. This notation represents the
"number" to the "exponent" power of ten. Note: if the "number" as above would by itself be a FIX.
and if the "exponent" is positive. and if the result is within the allowed range of FIXes. then the
result will be a FIX. For example. READ understands 10E1 as 100 (a FIX). but 10E-1 as 1.0000000 (a
FLOAT).
The largest-magnitude FLOAT which can be handled without overflow i5 1. 7014118E+38 (decimal
radix). The smalJest-magnitude FLOAT which can be handled without underflow is . 14693679E-38 .

2.6.1 - 2.6.2

Read. Evaluate. and Print

24

2.6.3. READ and PNAMEs
The question "what is a legal PNAME?" is actually not a reasonable one to ask; any non-empty string
of arbitrary characters can be the PNAME of an ATOM. However, some PNAMEs are easier to type to
READ than others. But even the question "what are easily typed PNAMEs?" is not too reasonable.
because: READ decides that a group of characters is a PNAME by default; if it can't possibly be
anything else. it's a PNAME. So. the rules governing the specification of PNAMEs are messy. and best
expressed in terms of what is not a PNAME. For simplicity, you can just consider any uninterrupted
group of upper- and lower-case letters and (customarily) hyphens to be a PNAHE; that will always
work. If you are neither a perfectionist nor a masochist. skip to the next chapter.
2.6.3.1. Non-PNAHEs
A group of characters is !!2!. a PNAME if:
(1) It represents a FLOAT or a FIX, as described above - that is, it is composed wholly of digits,
or digits and a single • (period). or digits and a . and the letter E or e (with optional minus
signs in the right places).
(2) It begins with a . (period).
(3) It contains - if typed interactively -- any of the characters which have special interactive
effects: ..... @, ..... 0, ..... L, .....G, .....S, S (ESC), rubout.
(4) It contains a format character -- space, carriage-return. line-feed. form-feed. horizontal tab,
vertical tab.
(5) It contains a , (comma) or a I (number sign) or a
(percent sign).

I

(single quote) or a ; (semicolon) or a %

(6) It contains any variety of bracket -- ( or ) or [ or ] or

or

{ or } or ".

In addition. the character \ (backslash) has a special interpretation. as mentioned below. Also, the
pair of characters ,- (exclamation-point hyphen) has an extremely special interpretation, which you
will reach at chapter 15.
The characters mentioned in cases 4 through 6 are "separators" - that iI. they signal to READ that
whatever it was that the preceding characters represented, it's done now. They can also indicate the
start of a new object's representation (all the opening "brackets" do just that).
2.6.3.2. Examples
The foUowing examples are not in the "standard format" of "/ins typed inS result printed', because
they are not, in 50me cases, complete objects; hence, READ would continue waiting for the brackets to

2.6.3 - 2.6.3.2

Read, Evaluate, and Print

25

be clond. In other cases, they will produce errors in EVAL if other - currently irrelevant -conditions are not met. Instead, the right-hand column will be used to state just what READ thought
the input in the left-hand column really was.
ASCS

an ATOM of PNAME ABC

abcS

an ATOM of PNAHE abc

ARSITRARILY-LONG-PNAMES

an ATOM of PNAME ARBITRARILY-LONG-PNAI1E

1.2345S

a FLOAT, PRINTed as 1. 2345000

1.2.345S

an ATOM of PNAME 1.2.345

A.or.SS

an ATOM of PNAME A.or.S

.A.or.aS

not an ATOM, but (as explained later) a FORM containing an
ATOM of PNAME A.or.S

HORE THAN ONES

three ATOM" with PNAMEs MORE, and THAN, and ONE

ab(cdS

an ATOM of PNAME ab, followed by the start of something
else (The something else will contain an ATOM of PNAME
beginning cd. )

12345A34S

an ATOM of PNAME 12345A34 (If the A had been an E. the
object would have been a FLOAT. )

2.6.3.3. \ (Backslash) in ATOMs
If you have a strange. uncontrollable compulsion to have what were referred to as "separators" above
as part of the PNAMEs of your ATOMs. you can do 50 by preceding them with the character \
{backslash}. \ will also magically turn an otherwise normal FIX or FLOAT into an ATOM if it appears
amongst the digits. In fact. backslash in front of any character changes it from something special
to "just another character" (including the character \). It is an escape character.
When PRINT confronts an ATOM which had to be backslashed in order to be an ATOM. it will dutifully
type out the required \5. They will not. however. necessarily be where you typed them; they will
instead be at those positions which will cause READ the least grief. For example. PRINT will type out
a PNAHE which consists wholly of digits by first typing a \ and then typing the digits - no matter
where you originally typed the \ (or \5).

2.6.3.2 • 2.6.3.3

Read, Evaluate. and Print

26

2.6.3.4. Examples of Awful ATOMs
The fonowing examples iUustrate the amount of insanity that can be perpetrated by using \. The
format of the examples is again non-standard, this time not because anything iI unfinished or in
error, but because commenting is needed; PRINT doesn't do it full justice.

a\ one\ and\ a\ twoS

one ATOM, whose PNAME has four spaces in it

1234\56789$

an ATOM of
\123456789

123\ S

an ATOM of PNAME 123space, which PRINTs as \123\ •
with a space on the end

\\$

an ATOM whose PNAME is a single backslash

2.6.3.4

PNAHE

123456789,

which

PRINTs

as

Read, Evaluate. and Print

27

Chapter 3. Built-in Funotions

3.1. Representation [1]
Up to this point, all the objects we have been concerned with have had no internal structure
discernible in MDL. While the characteristics of objects with internal structure differ greatly. the
way READ and PRINT handle them is uniform, to wit:
READ. when applied to the representation of a structured object. builds and returns an object of
the indicated TYPE with elements formed by applying READ to each of their representations in
turn.
PRINT, when applied to a structured object. produces a representation of the object. with its
elements represented as PRINT applied to each of them in turn.

A MDL object which is used to represent the application of a function to its arguments is an object
of TYPE FORM. Its printed representation is

< func

arg 1 arg2 ••• argn

>

where func is an object which designates the function to be applied. and argl through argn are
objects which designate the arguments or "actual parameters" or "inputs". A FORM is just a
structured object which is stored and can be manipulated like a LIST (its "primitive type" is LIST -chapter 6). The application of the function to the arguments is done by EVAL The usual meaning
of "function" (uncapitalized) in this document will be anything applicable to arguments.

3.2. Evaluation [1]
EVAL applied to a FORM acts as if following these directions:

First. examine the func (first element) of the FORM. If it is an ATOM. look at its "value" (global or
Jocal. in that order - see next chapter). If it is not an ATOM. EVAL it and Jook at the result of the

3 - 3.2

Built-in Functions

28
evaluation. If what you are looking at is not something which can be applied to arguments,
generate an error. Otherwise, follow its directions in evaluating or not evaluating the arguments
(chaptl!r,; 9 and 19) and then "apply the function" - that is, EVAL the body of the object gotten from
func.

8.8. Built-in Functions (TYPE SUBR, TYPE FSUBR) [1]
The built-in functions of MDL come in two varieties: those which have all their arguments EVAled
before operating on them (TYPE SUBR. for "subroutine", pronounced "subber; and those which have
none of their arguments EVALed (TYPE FSUBR. historicaUy from Lisp. pronounced "effsubber").
Collectively they will be called F/SUBRs, although that term is not meaningful to the interpreter.
See appendix 2 for a listing of all F/SUBRs and short descriptions. The term "Subroutine" will be
used herein to mean both F/SUBRs and compiled user programs (RSUBRs and RSUBR-ENTRYs chapter 19).
Unless otherwise stated, every MOL built-in Subroutine mentioned is of TYPE SUBR. Also, when it is
stated that an argument of a SUBR must be of a particular TYPE. note that this means that EVAl of
what is there must be of the particular TYPE.
Another convenient abbreviation which will be used is "the SUBR pname" in place of "the SUBR which
is initially the 'value' of the ATOM of PNAME pnsme". "The FSUBR pname" will be used with a similar
meaning.

3.4. Examples (+ and FIX: Arithmetic) [lJ

<+

2 4 6)$

12
The SUBR + adds numbers. Most of the usual arithmetic functions are MDL SUBRs: +, -, -, I,
HIN, HAX. HOD, SIN, COS, ATAN, SQRT, LOG, EXP, ASS. (See appendix 2 for short descriptions
of these.) All except MOD. which wants FIXes. are indifferent as to whether their arguments are
FLOAT or FIX or a mixture. In the last case. they exhibit "contagious FLOATing": one argument of
TYPE FLOAT forces the result to be of TYPE FLOAT.
$
5.0
<- 5 3 2>$

o
<-

5)$

-5
$
1.0
$
0.5
Note this last result: the division of two FIXes gives a FIX with truncation, not rounding. of the
remainder; the intermediate result remains a FIX until a FLOAT argument is encountered.

8.5. Arithmetic: Details
+. -. -. I. MIN. and MAX all take any number of arguments, doing the operation with the first

argument and the second. then with that result and the third argument. etc. If called with no
arguments. each returns the identity for its operation (0, O. 1. 1. the greatest FLOAT. and the
least FLOAT, re.spectively); if caUed with one argument. each acts as if the identity and the argument
had been supplied. They all wiJ) cause an overflow or underflow error if any result, intermediate or
final, is too large or too sma)) for the machine's capacity. (That error can be disabled. if necessary
- section 16.9).
One arithmetic function that always requires some discu55ion is the p5eudo-random-number
generator. MDL's i5 named RANDOM, and it always returns a FIX, uniformly distributed over the
whole range of FIXes. If RANDOM is never called with arguments. it always returns the exact same
sequence of numbers. for convenience in debugging. "Debugged" programs should give RANDOM two
argument.s on the first ca)). which become the seeds for a new sequence. Popular choices of new
.seeds are the numbers given by TIME (which see), possibly with biu modified (chapter 18). Example
rpick a number from one to ten"):
<+ 1  10»$
4

8.4 - 8.5

Built-in Functions

30

Chapter 4. Values of Atoms

4.1. General [1]
There are two kinds of "value" which can be attached to an ATOM. An ATOM can have either. both. or
neither. They interact in no way (except that alternately referring to one and then the other is
inefficient). These two values are referred to as the local value and the global value of an ATOM.
The terms "local" and "global" are relative to processes, not functions or programs. The SUBRs which
reference the local and global values of an ATOM, and some of the characteristics of local versus
global values. follow.

4.2. Global Values

4.2.1. SETG [1]
A global value can be assigned to an ATOM by the SUBR SETS tset globali. as in

where atom must EVAL to an ATOM. and any can EVAL to anything. EVAL of the second argument
becomes the global value of EVAL of the first argument. The value returned by the SETG is its
second argument. namely the new global value of atom.
Examples:

returns as a value the global value of atom. If atom does not evaluate to an ATOM. or if the ATOM to
which it evaluates has no global value, an error is generated.
GVAL applied to an ATOM anywhere. in any process, in any function, will return the same value. Any
SETG anywhere changes the global value fot everybody. Global values are context-independent.
READ understands the character , (comma) as an abbreviation for an application of GVAL to
whatever follows it. PRINT always translates an application of GVAL into the comma format. The
foUowing are absolutely equivalent:

,atom



Assuming the examples in section 4.2.1 were carried out in the order given, the following will
evaluate as indicated:
,FOOS

500
S

500
,BARS
FOO
, ,BARS

500
4.2.1. Note on SUBRs and FSUBRs
The initial GVALs of the ATOMs used to refer to MDL "built-in" Subroutines are the SUBRs and FSUBRs
which actually get applied when those ATOMs are referenced. If you don't like the way those
supplied routines work. you are perfectly free to SETG the ATOMs to your own versions.

4.2.1 - 4.2.3

Values of Atoms

32

4.2.4. GUNASSIGN

("global unassign") causes atom to have no assigned global value. whether or not it had one
previously. The storage used for the global value can become free for other uses.

4.3. Local Values

4.3.1. SET [1]
The SUBR SET is used to assign a local value to an ATOM. Applications of SET are of the form
(SET atom any>
SET returns EVAL of any just like SETG.
Examples:
(SET BAR (SET FOO 100»$
100
Both BAR and FOO have been given local values equal to the FIXed-point number 100.
(SET FOO BAR>S
BAR
FOO has been given the local value BAR.
Note that neither of the above did anything to any global values FOO and BAR might have had.

4.3.2. LvAL [1]
The SUBR used to extract the local value of an ATOM is named LVAL As with GVAL. READ understands
an abbreviation for an application of LVAL: the character • (period). and PRINT produces it. The
following two representations are equivalent. and when EVAL operates on the corresponding MOL
Object. it returns the current local value of atom:
(LVAL atom>

•atom

4.2.4 - 4.3.2

Values of Atoms

33

The local value of an ATOM is unique within a PROCESS. SETting an ATOM in one PROCESS has 110
effect on its LVAL in another PROCESS. because each PROCESS has its own "control stack" (chapters 20
and 22).
Assume a)) of the previous examples in this chapter have been done. Then the fo))owing evaluate as
indicated:
.BARS
100
S
100
.FOOS

BAR
•• FOOS

FOO
4.3.3. UNASSIGN

causes atom to have no assigned local value, whether or not it had one previoully.

4.4. VALUE
VALUE is a SUBR which takes an ATOM as an argument. and then:
(1) if the ATOM has an LVAl, returns the lVAL;
(2) if the ATOM has no LVAL but has a GVAl, returns the GVAL;
(3) if the ATOM has neither a GVAL nor an LVAl, error.
This order of seeking a value is the opposite of that used when an ATOM il the first element of a
FORM. The latter will be called the GIL VAL, even though that name is not used in MDL.
Example:
S
A

S
1

S
1

S
4.3.2·4.4

Values of Atoms

34

2

 A C)

-----

~

used:

no argument LIST or body
non·ATOM in argument LIST
no body
no argument LIST

These FUNCTIONs wilJ never cause errors because of format:
'FUNCTION
'FUNCTION
'FUNCTION
'FUNCTION
IFUNCTION

«) 1 2 3 4 5)
«A) A)
«)()()()()()()(»
«A BCD EE F G H HIYA) <+ .A .HIYA»
«Q)  .Q»

and the last two actually do something which might be useful. (The first three are rather
pathological, but Jegal.)

5.3. Application of FUNCTIONs; Binding [1]

FUNCTIONs. like SUBRs and FSUBRs, are applied using FORKs. So,
<'FUNCTION «X) <- .X .X»
25

5>$

applied the indicated FUNCTION to 5 and returned 25.
What EVAL doe. when applying a FUNCTION i. the following:

5.2·5.S

Simple Functions

37

(1) Create a "world" in which the ATOMs of the argument LIST have been SET to the values
applied to the FUNCTION, and all other ATOMs have their original values. This is called
"binding".
- In the above. this is a "world" in which X is SET to 5.
(2) In that new "world", evaluate all the objects in the body of the FUNCTION, one after the
other. from first to last.

- In the above, this means evaluate  in a "world" where

X is SET to 5.

(3) Throwaway the "world" created, and restore the LVALs of all ATOMs bound in this
application of the FUNCTION to their originals (if any). This is calIed "unbinding".

- In the above, this simply gives X back the local value, if any, that it had before binding.

(4)

Return as a value the last value obtained when the FUNCTION's body was evaluated in step

(2).

- In the above, this means return 25 as the value.
The "world" mentioned above is actualIy an object of TYPE ENVIRONMENT. The fact that such
"worlds" are separate from the FUNCTIONs which cause their generation means that all MDL
FUNCTIONs can be used recursively.

The only thing that is at all troublesome in this sequence is the effect of creating these Ilew
·worlds·, in particular. the fact that the previous world is completely restored. This means that if,
inside a FUNCTION. you SET one of its argument ATOMs to something, that new LVAL will not be
remembered when EVAL leaves the FUNCTION. However, if you SET an ATOM which is not in the
argument LIST (or SETG any ATOM) the new local (or global) value will be remembered. Examples:
(SET X 0)$

o
('FUNCTION «X) $

.X$

o

5.3

Simple Functions

as

On the other hand,
$

.X$
5
The fir.st number after the application FORM was typed out by the PRINT; the ,second is the value of
the application.
Remembering that LVALs of ATOMs
within FUNCTIONs. as in

~

in argument LISTs are not changed, we can reference them

(SET Z 100)$

100
('FUNCTION «Y)
20

$

ATOMs used like Z or Y in the above examples are referred to as "free variables-. The use of free
variables, while often quite convenient, i5 rather dangerous unless you know exactly how a

FUNCTION will always be used: if a FUNCTION containing free variables is used within a FUNCTION
within a FUNCTION within .. one of those FUNCTIONs might just happen to use your free variable
in its argument LIST. binding it to some unknown value and possibly causing your use of it to be
erroneous. Please note that "dangerous". as used above, really means that it may be effectively
impossible (1) for other people to use your FUNCTIONs, and (2) for you to use your FUNCTIONs a
month (two weeks?) later.
'0

5.3

Simple Function$

39

5.4. Defining FUNCTIONs (FUNCTION and DEFINE) [1]
Obviously. typing #FUNCTION ( ••• ) all the time is neither reasonable nor adequate for many
purposes. Normally. you just want a FUNCTION to be the GVAL of some ATOM •• the way SUBRs and
FSUBRs are - so you can use it repeatedly (:md recursively). Note that you generally do not want a
FUNCTION to be the LVAL of an ATOM; this has the same problems as free variables. (Of course, there
are always cases where you are being clever and !:!!!l the ATOM to be re-bound ....)
One way to "name" a FUNCTION is
(SETG SQUARE IFUNCTION «X)
IFUNCTION «X) <* .X .X»

<* .X .X»>S

So that
$

25
$

FUNCTION is an FSUBR which simply makes a FUNCTION out of its arguments and returns the created
FUNCTION.
This. however, is generally the best way:
 >. See
chapter 8.]

5.5. Examples (Comments) [Il
Using SQUARE as defined above:
(DEFINE HYPOT (SIDE-l SIDE-2)
,-This is a comment. This FUNCTION finds the
length of the hypotenuse of a right triangle
of sides SIDE-l and SIDE-2.N
 5
5.0
Note that carriage-returns, line-feeds, tabs, etc. are just separators, Uke spaces. A comment is ~
single MDL object which follows a ; (semicolon). A comment can appear between any two MDL
Objects. A comment is totally ignored by EVAL but remembered and associated by READ with the
place in the FUNCTION (or any other structured object) where it appeared. (This will become clearer
after chapter 13.) The ., (double.quotes) serve to make everything between them a single MDL
object. whose TYPE 15 STRING (chapter 7). (SQRT is the SUBR which return. the square root of its
argument. It always returns a FLOAT.)
A whimsical FUNCTION:
(DEFINE ONE (THETA) ;"This FUNCTION always returns 1..
<+ $

0.99999994
5

0.99999999
ONE always returns (approximately) one, since the sum of the squares of sin(x) and cos(x) is unity
for any x. (SIN and COS always return FLOATs, and each takes its argument in radians. ATAN
(arctangent) returns its value in radians. Any other trigonometric function can be compounded
from these three.)

5.4 - 5.5

Simple Functions

41

MDL doesn't have a general "to the power" SUBR, so let'. define one using LOG and EXP (log base e.
and e to a power. respectively; again. they return FLOATs).
$

4.0000001
$
125.00000
$
5.0000001
Two FUNCTIONs which use a single global variable (Since the GVAL is used, it cannot be rebound.):
$
STEP
$
2

$
INC
$

o
(INC A)S
1

$
2

.AS
2

INC takes an 8!Q.t1 as an argument, and SETs that ATOM to its current LVAL plus 1. Note that inside
INC. the ATOM ATM is SET to the ATOM which i. its argumentl thus •• ATM returns the lVAL of the
argument. However. there is a problem:

5.5

Simple Functions

42

(SET ATH 0>$

o
(INC ATH)S

-ERRORARG-WRONG-TYPE
+

LISTENING-AT-LEVEL 2 PROCESS 1
(ARGS (FRAME (FRAHE»)$
[ATH 1]

The error occurred because .ATH was ATM. the argument to INC, and thus •• ATH was ATM also. We
really want the outermost • in •• ATM to be done in the ·world" (ENVIRONHENn which existed just
before INC was entered - and this definition of INC does both applications of LVAL in its own
·world-, Techniques for doing INC "correctly" will be covered below. Read on.

5.5

Simple Functions

43

Chapter 8. Data Types

6.1. General [ll
A MDL object consists of two parts: its TYPE and its "data part" (appendix I). The interpretation of
the "data part" of an object depends of course on its TYPE. The structural organization of an object.
that is. the way it is organized in storage, is referred to as its "primitive type". While there are
many different TYPEs of objects in MOL, there are fewer primitive types.

AJJ structured objects in MDL are ordered sequences of elements. As such. there are SUBRs which
operate on all of them uniformly, as ordered sequences. On the other hand. the reason for having
different primitive types of structured objects is that there are useful qualities of structured objects
which are mutually incompatible. There are, therefore, SUBRs which do not work on all structured
objects; these SUBRs exist to take full advantage of those mutually incompatible qualities. The
most-commonly-used primitive types of structured objects are discussed in chapter 7. along with
those special SUBRs operating on them.
It is very easy to make a new MOL object that differs from an old one only in TYPE, as long as the
primitive type is unchanged. It is relatively difficult to make a new structured object that differs
from an old one in primitive type, even if it has the same elements.
Before talking any more about structured objects, some information needs to be given about TYPEs
in general.

6.2. Printed Representation [1]
There are many TYPEs for which MDL has no specific representation. There aren't enough different
kinds of brackets. The representation used for TYPEs without any special representation is
#type representation-as-if-it-were-its-primitive-type

READ will understand that format for any TYPE, and PRINT will use it as a default.

6 - 6.2

This

Data Types

44
representational format will be referred to below as "I notation". It was used above to represent
FUNCTIONs.

6.S. SUBRs Related to TYPEs

6.S.1. TYPE [1]
$
1.0)$
+)$

.+)$
GEORGE)$

6.3.2. PRIMTYPE [1]
< PRIHTYPE any)

evaluates to the primitive type of any. The PRIMTYPE of any is an ATOH which also represents a
TYPE. The wayan object can be manipulated depends solely upon its PRIHTYPE; the way it is
evaluated depends upon its TYPE.
Examples:
$
WORD

An error is generated if the PRIMTYPE of any is not the same as the TYPEPRIM of type. An error will
also be generated if the attempted CHTYPE is dangerous and/or senseless, for example. CHTYPEing a
FIX to a SUBR. Unfortullately. there are few useful examples we can do at this point.
[CHTYPEing a FIX to a FLOAT or vice versa produces. in general. nonsense. since the bit formats for
FIXes and FLOATs are different. The SUBRs FIX and FLOAT convert between those formats. Useful

6.3.2 • 6.3.4

Data Types

46

obscurity: because of their internal representations on the PDP-10,  gives the
least possible FIX, and analogously for MIN.]
Passing note: ., notation" is just an instruction to READ saying "READ the representation of the
PRIMTYPE normally and (literally) CHTYPE it to the specified TYPE". [Or, if the PRIHTYPE is
TEMPLATE. -apply the GVAL of the TYPE name (which should be a TEMPLATE constructor) to the given
elements of the PRIHTYPE TEMPLATE as arguments"]

6.4. More SUBRs Related to TYPEs

6.4.1. ALL TYPES

returns a VECTOR (chapter 7) containing just those ATOMs which can currently be returned by TYPE
or PRIHTYPE. This is the very -TYPE vector (section 22.1) that the interpreter uses: look, but don't
touch. No examples; try it, or see appendix 3.
N

6.4.2. VALID-TYPE?

returns IFALSE () if stom is not the name of a TYPE, and the same object that 
(section 19.5) returns if it is.

6.4.3. NEWTYPE

returns atom, after causing it to become the representation of a brand-new TYPE whose PRIMTYPE is
$
GARGLE
S
WORD
$
#GARGLE ·000000000144$
GARGLE
$
WORD

6.4.4. PRINTTYPE, EVALTYPE and APPLYTYPE



all return type, after specifying how MDL is to deal with it.
These three SUBRs can be used to make newly-generated TYPEs behave in arbitrary ways, or to
change the characteristics of standard MDL TYPEs. PRINTTYPE tells MDL how to print type,
EVAL TYPE how to evaluate it, and APPL YTYPE how to apply it in a FORM.
how can be either a TYPE or something that can be applied to arguments.

If how is a TYPE, MDL will treat type just like the TYPE given as how. how must have the same
TYPEPRIH as type.
If how is applicable, it will be used in the following way:

For PRINTTYPE. how should take one argument: the object being output. how should output
something without formatting (PRINI-style)j its result is ignored. (Note: how cannot use an output
SUBR on how's own type: endless recursion will result. OUTCHAN is bound during the application to
the CHANNEL in use, or to a pseudo-internal channel for FLATSIZE - chapter 11.) If how is the SUBR
PRINT. type will receive no special treatment in printing, that is, it will be printed as it was in an
initial MDL or immediately after its defining NEWTYPE.
For EVAL TYPE, how should take one argument: the Object being evaluated. The value returned by
how will be used as EVAL of the object. If how is the SUBR EVAL. typtl will receive no special
treatment in evaluation.
6.4.3 - 6.4.4

Data Types

48
For APPLYTYPE. how should take at least one argument. The first argument will be the object being
applied; the relt will be the objects it was given al argument•. The result returned by how will be
used as the result of the application. If how is the SUBR APPLY. type will receive no special
treatment in application to arguments.

SUBRs is given only one argument. that i5 if how i5 omitted. it returns the currently
active how (a TYPE or an appJicable object). or else 'FALSE () if ty". i. receiving no 5pecial
treatment in that operation.

If any of these

Unfortunately. these examples are fully understandable only after you have read through chapter H.

(DEFINE ROMAN-PRINT (NUMB)
(CONO «OR  
       
 
I![!\C !\D !\M]>
I![I\X I\L I\e]>
11[1\1 I\V I\X]»»S

<1 .V»)
<1 · V» S

(PRINTTYPE TIME FIX> ;"fairly harmless but necessary here"S
TIME
(PRINTTYPE FIX ,ROMAN-PRINT>
;"hee heel"S
FIX·
(+ 2 Z>S
IV
6.4.4

Data Types

49

1984$
MCMLXXXIV
$
FIX
$
IFALSE ()
 :"evaluated like a LISTII$
GRITCH
 :"a new TYPE of PRIMTYPE VECTORII$
HARRY

,·When a HARRY is EVALed. return its first element.IIS
HARRY
'HARRY [1 Z 3 4]$
1

 : Iia TYPE with funny applicationllS
WINNER
S
IFALSE ()
S
WINNER
 q>$
(A B C 3 q)

The following sequence makes MDL look just like Lisp. (This example is understandable only if
you know Lisp; it is included only because it is so beautifuJ.)

$
LIST


[However. the compiler (ref 7) is happier with the former (section 9.10). The two constructs are not
identical even to EVAL. if the order of evaluation is significant; for example. these two:
(NTH .X 

«LENGTH 
Structured Objects

52
are

!l2l identical.]

7.1.3. REST [1]

evaluates to structured without its first fix elements. fix is optional, with 1 assumed.
Obscure but important side effect: REST actually returns structured "CHTYPEd" (but not through
application of CHTYPE) to its PRIMTYPE. For example, REST of a FORM is a LIST. REST with an
explicit second argument of 0 has no effect except for this TYPE change.

7.1.4. PUT [1]
< PUT structured fix anything-legal>
first makes anything-legal the firth element of structurlKi, then evaluates to structured. anything-legal
is anything which can legally be an element of structured; often, this is synonymous with "any MDL
object", but see below. Error if fix is 0 or less, or greater than . (PUT is actually
more general than this - chapter 13.)

7.1.5. GET

evaluates the same a$ . It is more general than NTH, however (chapter 13), and is
included here only for symmetry with PUT.

7.1.6. SUBSTRUC
SUBSTRUC ("substructure") facilitates the construction of structures that are composed of sub-parts of
existing structures. A special case of this would be a "substring" function.

copies the first amount elements of  into another object and returns the latter. All
arguments are optional except obj. which must be of PRIHTYPE LIST. VECTOR. TUPLE (treated like
a VECTOR). STRING. BYTES, or UVECTOR. rest defaults to 0, and amount defaults to all the elements.
Where-to, if given, receives the copied elements, starting at its beginning; it must be an object whose
TYPE is the PRIMTYPE of obj (a VECTOR if obj is a TUPLE). If where-to is not given, a new object is

7.1.2 • 7.1.6

Structured Objects

53

returned, of TYPE  (a VECTOR if obj is a TUPLE). which!!!!£!. shares with obi. The
copying is done in one feU swoop, not an element at a time. Note: due to an implementation
restriction. if obj is of PRIMTYPE LIST, it must not share any elements with where-fo.

7.2. Representation of Basic Structures
7.2.1. LIST [1]
( element-l elemenf-2 •••

element-N' )

represents a LIST of N elements.
7.2.2. VECTOR [1]
[ .I.ment-l elemenf-2 •••

element-N

J

represents a VECTOR of N elements. [A TUPLE is just like a VECTOR, but it Jives on the control stack.]
7.2.8. UVECTOR [1]
I ( tHement-l element-2 •••

element-N! ]

represenu a UVECTOR (uniform vector) of N elements. The second ! (exclamation-point) is optional
for input. [A STORAGE is an archaic kind of UVECTOR that iJ not garbage-coUected.]
7.2.4. STRING [1]
II

characters II

represents a STRING of ASCII text. A STRING containing the character .. (double-quote) is
represented by placing a \ (backs lash) before the double-quote inside the STRING. A \ in a STRING is
represented by two consecutive backslashes.
7.2.5. BYTES
In {element-l element-2 ••• element-N}

represents a string of N uniformly-sized bytes of size n bits.

7.1.6 - 7.2.5

Structured Objects

54
7.2.6. TEMPLATE
{ element-l element-2 ••• element-N }

represents a TEMPLATE of N elements when output. not input - when input, a I and a TYPE must
precede it.

7.S. Evaluation of Basic Structures [1]
This section and the next two describe how EVAL treats the basic structured TYPEs [in the absence of
any modifying EVAL TYPE calls (section 6.4.4)].
EVAL of a STRING [or BYTES or TEMPLATE] is just the original object.
EVAL acts exactly the same with LISTs. VECTORs, and UVECTORs: it generates a ~ object with
elements equal to EVAL of the elements it is given. This is one of the simplest means of
constructing a structure. However. see section 7.7 below.

7.4. Examples [1]
(1 2 <+ 3 4»$
(127)

 ]>$
[5 -3 STRING]
<2 .FOO>S
-3
S
![("meow") ([5 -3 STRING])!]
(LENGTH .BAR>$
2

S
[-3 STRING]
[]S

[[5 -3]]


;"Watch out for .BAR I"S
[SNEAKY -3 STRING]
.BARS
![("meow") ([SNEAKY -3 STRING])!]
(SET FOO $
3

1<+ 1 2>$
<+ 1 2>
Any LIST, VECTOR. or UVECTOR in a program that is constant and need not have its elements
evaluated should be represented directly and inside a call to QUOTE. This technique prevents the
structure from being copied each time that portion of the program is executed. Examples hereafter
will adhere to this dictum. (Note: one should !!!ill modify a QUOTEd object. The compiler will one
day put it in read-only (pure) storage.)

7.4 - 7.5.2

Structured Objects

56
7.5.3. LIST. VECTOR, UVECTOR, and STRING (the SUBRs) [1]
Each of the SUBRs LIST. VECTOR, UVECTOR. and STRING takes any number of arguments and returns
an object of the appropriate TYPE whose elements are EVAL of its arguments. There are Jimitations
on what the arguments to UVECTOR and STRING may EVAL to. due to the nature of the objects
generated. See sections 7.6.5 and 7.6.6 below.
LIST. VECTOR, and UVECTOR are generally used only in special cases, since Direct Representation
produces exactly the same effect (in the absence of errors). and the intention is more transparent.
STRING. on the other hand. produces effects very different from Hteral STRINGs.
Examples:
, even if the LVAL of X evaluates to itself. so that
the I could be omitted without changing the result. the compiler is much happier with the I in
place.]

7.5.3 - 7.5.4

Structured Objects

57
IUVECTOR and ISTRING again have limitations on what sKpression may EVAL tOj again, see sections
7.6.5 and 7.6.6 below.
Examples:
 is tail (actually ). then evaluates to head. Note
that this actuaIJy changes held; it also changes anything having head as an element or a value. For
example:
S
(B W)]


rconstructj adds new to the front of list. without copying list. and returns the resulting LIST.
References to list are not affected.
[Evaluating (CONS • E • LIST) is equivalent to evaluating (. E I. LIST) (section 7.7) but is len
preferable to the compiler (ref 7).]

7.6.2. "Array· PRIHTYPEs [1]
VECTORs. UVECTOR.s, and STRINGs [and BYTESes and TEI1PLATEs] may be considered as "arrays"
(appendix 1). It is easy to access the Nth element irrespective of how large N is. and it is relatively
difficult to add and delete elements. The following SUBRs can be used only with an object of
PRIMTYPE VECTOR. UVECTOR, or STRING [or BYTES or TEMPLATE]. (In thi. 5ection ,rray represents an
object of such a PRIHTYPE.)
7.6.2.1. BACK [1]
$
![234!]
$

7.6.1.1 . 7.6.2.1

Structured Objects

59
"mi ght."
7.6.2.2. TOP [1]
$

![O 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
o 0 0 0 0 0 0 000 0 0 000 0 0 0 000 0

7.6.2.1 . 7.6.S.1

Structured Objects

60

o0

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 000 11]

.AS
! [11]
7.6.S.!. SORT
This SUBR will sort PRIHTYPEs VECTOR, UVECTOR and TUPLE (section 9.2). It works most efficiently
if the sort keys are of PRIMTYPE WORD, ATOM or STRING. However, the key. may be of any TYPE.
and SORT will still work. SORT acts on fixed-length records which consist of one or more contiguous
elements in the structure being sorted. One element in the record is declared to be the sort key.
Also. any number of additional structures can be rearranged based on how the main structure is
sorted.

where:
pred is either (see chapter 8 for information about predicates):
(1) TYPE FALSE, in which case the TYPEs of all the sort keys must be the same; they must be of

PRIHTYPE WORD. STRING or ATOM; and a radix-exchange sort is used; or
(2) something applicable to two sort keys which returns TYPE FALSE if the first is not bigger

than the second, in which case a shell sort is used. For example • G? sorts numbers in ascending
order•• L? in descending order. Note: if your pr6d is buggy, the SORT may never terminate.
sl ••• sn are the (PRIMTYPE) VECTORs. UVECTORs or TUPLEs being sorted, and 51 contains the sort

keys;
11 ••. In are the corresponding lengths of sort records (optional, default 1); and

off is the offset from start of record to sort key (optional, default 0).

SORT returns the sorted 51 as a value.
Note: the SUBR SORT calls the RSUBR (chapter 19) SORTX; if the RSUBR must be loaded, you may see
some output from the loader on your console.
Examples:
 S
[
]
 .V Z 1>$
[4 GO 1 MONEY 3 READY Z SHOW]

...

S
[4 GO 3 READY Z SHOW 1 MONEY]
.VS
[4 GO 3 READY Z SHOW 1 MONEY]
 ![Z 1 4 3 6 5 8 7] 1 0 .V>$

1[1 234 5 6 7 81]
.VS
[GO 4 READY 3 SHOW 2 MONEY 1]
The first sort was based on the ATOMs' PNAMEs, considering records to be two elements. The second
one sorted based on the FIXes. The third interchanged pairs of elements of each of its structured
arguments.

7.6.4. VECTOR (the PRIM TYPE) [1]

Any MDL object may be an element of a PRIMTYPE VECTOR. A PRIHTYPE VECTOR takes two words
of storage more than an equivalent PRIMTYPE LIST, but takel it all in a contiguoul chunk, whereu
a PRIHTYPE LIST may be physically spread out in storage (appendix 1). There are no SUBR.5 or
FSUBRs which operate only on PRIMTYPE VECTOR.

7.6.5. UVECTOR (the PRIHTYPE) [1]

The difference between PRIMTYPE5 UVECTOR and VECTOR i5 that every element of a PRII1TYPE
UVECTOR must be of the same TYPE. A PRIMTYPE UVECTOR takes approximately half the storage of a
PRIMTYPE VECTOR or PRIMTYPE LIST and, like a PRIMTYPE VECTOR, takes it in a contiguous chunk
(appendix 1).
[Note: due to an implementation restriction. PRIMTYPE STRINGs, BYTESes, LOCOs (chapter 12). and
objects on the control stack (chapter 22) may not be elements of PRII1TYPE UVECTORs.]
The "same TYPE" restriction causes an equivalent restriction to apply to EVAL of the arguments to
either of the SUBRs UVECTOR or IUVECTOR. Note that attempting to say
![1 .A!]

wiIl cause READ to produce an error, since you're attempting to put a FORM and a FIX into the same
UVECTOR. On the other hand.

7.6.3.2 - 7.6.5

Structured Objects

62

is legal. and will EVAL to the appropriate UVECTOR without error if .A EVAls to a TYPE FIX.
The following SUBRs work on PRIMTYPE UVECTORs alone.

7.6.5.1. UTYPE [1]

(·uniform type") evaluates to the TYPE of every element in ita argument. Example:
S
ATOM

7.6.5.2. CHUTYPE [1]

rchange uniform type") changes the UTYPE of uv to type, simultaneously changing the TYPE of all
elements of uv. and returns the new. changed. uv. This works only when the PRIMTYPE of the
elements of uv can remain the same through the whole process. (Exception: a uv of UTYPE LOSE can
be CHUTYPEd to any type (legal in a UVECTOR of course); the resulting elements are "empty". as for
GROW.)

CHUTYPE actually changes uv; hence all references to that object will reflect the change. This is
quite different from CHTYPE.
Examples:
$
![<><>I]
• LOST
1[<><>1]
$
![() ()!]

7.6.5 • 7.6.5.2

Structured Objects

63

7.6.6. STRING (the PRIMTYPE) and CHARACTER [1]
The best mental image of a PRIMTYPE STRING is a PRIMTYPE UVECTOR of CHARACTERs -- where
CHARACTER is the MDL TYPE for a single ASCII character. The representation of a CHARACTER, by
the way. is
! \any-ASCII-character

That is, the characters ! \ (exclamation-point backslash) preceding a single ASCII character
represent the corresponding object of TYPE CHARACTER (PRIMTYPE WORD). (The characters !.
(exclamation-point double-quote) preceding a character are also acceptable for inputting a
CHARACTER, for historical reasons.)
The SUBR ISTRING will produce an error if you give it an argument that produces a non-CHARACTER.
STRING can take either CHARACTERs or STRINGs.
There are no SUBRs which uniquely manipulate PRIMTYPE STRINGs. but some are particularly useful
in connection with them:
7.6.6.1. ASCII [1]
(ASCII fix-or-character>
If its argument is of TYPE FIX, ASCII evaluates to the CHARACTER with the 7-bit ASCII code of its
argument. Example: (ASCII 65> evaluates to ! \A.
If its argument is of TYPE CHARACTER, ASCII evaluates to the FIXed-point number which is its
argument's 7·bit ASCII code. Example: (ASCII ! \Z> .evaluates to 90.

[Actually. a FIX can be CHTYPEd to a CHARACTER (or vice versa) directly. but ASCII check! in the
former case that the FIX is within the permissible range.]
7.6.6.2. PARSE [1]
(PARSE string radix:fix>
PARSE applies to its argument READ's algorithm for converting ASCII representations to MDL
Objects and returns the first object created. The remainder of string. after the first object
represented, is ignored. radix (optional, default ten) is used for converting any FIXes that occur.
[See also sections 15.7.2 and 17.1.3 for additional arguments.]

7.6.6 - 7.6.6.2

Structured Objects

64

7.6.6.S. lPARSE [1]
LPARSE ("list parse") is exactly like PARSE (above), except that it parses the entire string and returns a
LIST of aU objects created. If given an empty STRING or one containing only separators, LPARSE
returns an empty LIST. whereas PARSE gets an error.
7.6.6.4. UN PARSE [1]

UNPARSE applies to its argument PRINT's algorithm for converting MDL objects to ASCII
representations and returns a STRING which contaius the CHARACTERs PRINT would have typed out.
[However. this STRING will not contain any of the gratuitous carriage-returns PRINT adds to
accomodate a CHANNELl, finite line-width (section 11.2.7).] radix (optional. default ten) is used for
converting any FIXes that occur.

7.6.7. BYTES
A (PRIMTYPE) BYTES is a string of uniformly.sized bytes. The bytes can be any size between 1 and
36 bits inclusive. A BYTES is similar in some ways to a UVECTOR of FIXes and in some ways to a
STRING of non-seven-bit bytes. The elements of a BYTES are always of TYPE FIX.
The SUBRs BYTES and IBYTES are similar to STRING and ISTRING, respectively, except that each of
the former takes a first argument giving the size of the bytes in the generated BYTES. BYTES takes
one required argument which is a FIX specifying a byte size and any number of PRIMTYPE WORDs.
It returns an object of TYPE BYTES with that byte size containing the objects as elements. These
objects will be ANDBed with the appropriate mask of I-bits to fit in the byte size. IBYTES takes two
required FIXes and one optional argument. It uses the first FIX to specify the byte size and the
second to specify the number of elements. The third argument is repeatedly evaluated to generate
FIXes that become elements of the BYTES (if it is omitted, bytes fiJJed with zeros are generated). The
analog to UTYPE is BYTE-SIZE. Examples:
 9 -1>$
#3 {4 1 7}

$

o
$

#3 {I 2 3 4 5 6 7 0 I}
$
113 {O 0 0 O}



! ( func arg I arg2 •••

where the second ! (exclamation-point) is optional. and func and argl through argn are any legal
consituents of a FORM (that is, anything). The pointed brackets can be implicit. as in the period and
comma notation for LVAL and GVAL
All of the folJowing are SEGMENTs:
1(3 .FOO)

I.FOO

I.FOO

7.7.2. Evaluation [1]
A SEGMENT is evaluated in exactly the same manner u a FORM, with the following three exceptions:

(1) It had better be done inside an EVAL of a structure, else error. (See special case of FORMs in
section 7.7.5 below.)
(2) It had better EVAL to a structured object. else error.
(3) What actually gets inserted into the structure being built are the elements of the structure
returned by the FORM-like evaluation.

7.6.8 - 7.7.2

Structured Objects

66
7.7.a. Examples [l]
(SET ZOP '![2 3 4])$
![2 34!]
(SET ARF (B 3 4»S
(B 3 4)

(.ARF ! .ZOP)S
«B 3 4) 2 3 4)
![! .ZOP !(REST .ARF>!]$
![23434!]
(SET S "STRUNG.">$
·STRUNG.( ! .S)$
(!\S !\T I\R !\U !\N !\G !\.)
(SET NIL (»$
()

[ ! .NIL]$

[]
7.7.4. Note on Efficiency [1]
Most of the cases in which it is possible to use SEGMENTs require EVAL to generate an entire new
object. Naturally. this uses up both storage and time. However. there is one case which it is
possible to handle without copying. and EVAL uses it. When the structure being built is a PRII1TYPE
LIST. and the segment value of a PRIMTYPE LIST is the last (rightmost) element being concatenated.
that last PRIMTYPE LIST is not copied. This case is similar to CONS and is the principle reason why
PRIM TYPE LISTs have their structures more easily varied than PRIMTYPE VECTOR or UVECTOR.
Examples:
.ARFS
(B 3 4)

This does not copy ARF:
(1 2 I.ARF)$
(1 2 B 3 4)

These do:
(1 I.ARF 2)
(1 B 3 4 2)

;"not last element"$
7.7.8 • 7.7.4

Structured Objects

67

[1 Z I.ARF]
[1 2 B 3 4]

t "not PRIM TYPE LISPS

(1 Z I.ARF I
If head is a subset of tail, that is. if (REST tail fix> is the same object as (REST head

0) for some fix.
then both head and tail will be "circular" (and thus self-referencing) after the PUTREST. Example:

(SET WALTZ (1 2 3»$
(l 2 3)

(PUTREST (REST .WALTZ Z> .WALTZ>S
(3 1 Z 3 1 Z 3 123 123 ...

7.8.2. Self-element
(PUT s 1:structured fix s2:structured)

If sl is the same Object as s2. then it will "contain" itself (and thus be self-referencing) after the
PUT. Examples:
$
I [ I [ I] I]

(PUT .U 1 .U>S
![![![![![![ ...
Test your reaction time or your console's bracket-maker. Amaze your friends.

7.8 - 7.8.2

Structured Objects

69

Chapter 8. Truth

S.l. Truth Values [I]
MDL represents "false" with an object of a particular TYPE: TYPE FALSE (un5urpri5ingly). TYPE
FALSE is structured; iu PRIMTYPE is LIST. Thus, you can iive reasons or excuses by making them
eltments of a FALSE. (Again, EVAUni a FALSE neither copies it nor EVALs its elements, 50 it is not
necessary to QUOTE a FALSE appearini in a proiram.) Objecu of TYPE FALSE are represented in "#
notation":
IF ALSE /ist-of-its-elements

The empty FORM evaluates to the empty FALSE:

<>$
IFAlSE ()
Anything which b not FALSE, is, reasonably enouih, true. In this document the "data type" false-orliMy in metasyntactic variables means that the only significant attribute of the object in that
context is whether its TYPE is FALSE or not.

8.2. Predicates [I]
There are numerous MDL F/SUBRs which can return a FALSE or a true. See appendix 2 to find
them aU. Most return either #FALSE () or the ATOM with PNAME T. (The latter is for historical
reasons, namely Lisp.) Some predicates which are meaningful now are described next.

8.2.1. Arithmetic (1]



evaluates to T only if its ariument is identically equal to 0 or 0.0.
8 - 8.2.1

Truth

70

<11

fix-or-float>

evaluates to T only if its argument is identically equal to lor 1.0.

$

T
N=? is the Boolean complement of =1.


runs down structured from first to last element. comparing each element of structured with object.
If it finds an element of structured which is =7 to object. it returns (REST structured i> (which is of
TYPE (PRIMTYPE structured». where the (i+1)th element of structured is =1 to object. That is. the
first element of what it returns is the first element of structured that is = 7 to object.

If no element of structured is -1 to object. MEMBER returns IFALSE ().
The search is more efficient if structured is of PRIMTYPE VECTOR (or UVECTOR, if possible) than if it
is of PRIHTYPE LIST. As usual. if structured is constant. it should be QUOTEd.

If object and structured are of PRIMTYPE STRING [or BYTES]. MEMBER does a substring search.
Example:
(MEMBER "PART" "SUM OF PARTS">$
• PARTS"
(MEMQ object:any structured>
('"member quick; is exactly the same as MEMBER, except that the comparison test is ==7 .
(STRCOMP sl s2>
("string comparison") can be given either two STRINGs or two ATOMs as arguments. In the latter case
the PNAMEs are used. It actually isn't a predicate. since it can return three possible values: 0 if s1 is
=? to sa 1 if 51 sorts aJphabeticalJy after 52; and -1 if 51 sorts alphabetically before 52.
"Alphabetically" means. in this case. according to the numeric order of ASCII. with the standard
alphabetizing rules.
[A predicate suitable for an ascending SORT (which see) is (G7 (STRCOMP .ARGI .ARGZ) 0).]

8.2.3. Boolean Operators [1]
(NOT e:faI5e-or-any>
evaluates to T only if e evaluates to a FALSE. and to IFALSE () otherwise.

8.2.2 • 8.2.3

Truth


AND is an FSUBR. It evaluates its arguments from first to last as they appear in the
as one of them evaluates to a FALSE, it returns that FALSE, ignoring any remaining
none of them evaluate to FALSE, it returns EVAL of its last argument. 
OR is an FSUBR. It evaluates its arguments from first to last as they appear in the FORM. As soon as
one of them evaluates to a non-FALSE, OR returns that non-FALSE value, ignoring any remaining
arguments. If this never occurs, it returns the last FALSE it saw.  returns IFALSE (). OR? is
the SUBR equivalent to OR.

8.2.4. Object Properties [1]

evaluates to type-i only if <==? type-i 
evaluates to 'FALSE () only if NTH and REST (with nonzero second argument) can be performed on
its argument without error. An unstructured or empty structured object will cause MONAD? to return
T.

evaluates to T only if its argument, which must be a structured object. has no elements.
(LENGTH? structured fix)

8.2.3 - 8.2.4

Truth

73

evaluates to (LENGTH structured> only if that is less than or equal to fiX; otherwise, it evaluates to
flFALSE ().
[If structured is a circular PRIMTYPE LIST, LENGTH? wi)) return a value, whereas LENGTH will execute
forever. To see if you can do (REST structured (+ 1 fix» without error, do the test (NOT (LENGTH?
structured fix». MnemonicaJJy, you can think of the first two letters of LENGTH? as signifying the
"Jess than or equal to" sense of the test.]

8.S. COND [Il

The MDL Subroutine which is most used for varying evaluation depending on a truth value is the
FSUBR COND ("conditional'. A can to COND has this format:
(COND clause-l:list ••• c/ause-N:list>

where N is at least one.
COND always returns the result of the!!!!. evaluation it performs. The fonowing rules determine the
order of evaluations performed.

(I) Evaluate the first element of each clause (from first to last) until either a non-FALSE object
results or the clauses are exhausted.
(2) If a non-FALSE object is found in (I), immediately evaluate the remaining elements (if any)
of that clause and ignore any remaining clauses.

In other words. COND goes walking down its clauses, EVAUng the first element of each clause. looking
for a non-FALSE result. As soon as it finds a non-FALSE, it forgets about an the other clauses and
evaluates. in order, the other elements of the current clause and returns the last thing it evaluates.
If it can't find a non-FALSE, it returns the last FALSE it saw.

8.3.1. Examples [1]
(SET F

I (l ) >S
(1)
(COND «EMPTY?
ONE

.F>

EMP) «I? (LENGTH .F»

ONE»S

()
(COND «EMPTY? .F> EMP) «I? (LENGTH .F»

ONE»S

(SET F (»S

EMP
(SET F '(1 2 3»S
8.2.4 - 8.3.1

Truth

74
(l 2 3)

 EMP) «I?  SMALL) (BIG»$
BIG
(DEFINE FACT (N)
;"the standard recursive factorial 1)
(ELSE <* .N $
120

8.4. Shortcuts with Conditionals

8.4.1. AND and OR as Short CONOs

Since AND and OR are FSUBRs. they can be used as miniature CONOs. A construct of the form
 .FLAG (FeN .ARG»
applies FCN only if someone else has SET FLAG to true. (ASSIGNED? is true if its argument ATOM has
an LVAL) No error can occur in the testing of FLAG because of the order of evaluation.
(AND (SET C (OPEN "READ" "A FILE"»

(LOAD .C> (CLOSE .C»

effectively FLOADs the file (chapter 11) without the possibility of getting an error if the file cannot
be opened.

8.3.1 - 8.4.1

Truth

75
8.4.2. Embedded Unconditionals
One of the disadvantages of COND is that there is no straightforward way to do things
unconditionally in between tests. One way around this problem is to insert a dummy clause that
never succeeds, because its only LIST element is an AND that returns a FALSE for the test. Example:

   I[])
(T    Ie])
(T  1)
;"If N is 1, return 1st arg, i.e .•• N,
i.e., 1. Note that (11 .N> would be
true even if .N were 1.0."
«L1 (LENGTH .T> (SET N (- .N 1»>
'FALSE ("DUMMylI»
;"Check to see if there is an Nth arg,
and make N a good index into T while
you're at it.
If there isn't an Nth arg, complain."
(ELSE (NTH .T .N»»
NTHARG. above. takes any number of arguments. Its first argument must be of TYPE FIX. It returns
EVAL of its Nth argument. if it has an Nth argument. If it doesn't, it returns 'FALSE ("DUMMY").
(The ELSE is not absolutely necessary in the last clause. If the Nth argument is a FALSE. the COND
will return that FALSE.) Exercise for the reader: NTHARG will generate an error if its first argument
is not FIX. Where and why? (How about (NTHARG 1.5 Z 3>?) Fix it.

9.2.2. TUPLE (the SUBR) and ITUPLE
These SUBRs are the same as VECTOR and IVECTOR. except that they build TUPLEs (that is, vectors on

9.2.1 - 9.2.2

Functions

79
the control stack). They can be used only at top level in an "OPTIONAL" list or "AUX" li5t (see
below). The clear advantage of TUPLE and ITUPLE ("implicit tuplej is in storage.management
efficiency. They produce no garbage. since they are flushed automatically upon function return.
Examples:

These are valid uses of TUPLE and ITUPLE. However, the following i, ru!!. a valid use of TUPLE,
because it is not caUed at top level of the "AUX":
(DEFINE NO (A B "AUX" (C (REST (TUPLE I.A»»

••• )

However, the desired effect could be achieved by


-AUX· (or "EXTRA" - they're totally equivalent) are STRINGs which, placed in an argument LIST,

serve to dynamically allocate temporary variables for the

Ule

of a Function.

aAUX" must appear in the argument LIST after any information about explicit arguments. It is
followed by ATOMs or two-element LISTs as if it were "OPTIONAL". ATOMs in the two-element LISTs
are bound to EVAL of the second element in the LIST. Atoms not in such LISTs are initially
unassigned; they are explicitly given "no" LVAL

AU binding specified in an argument LIST is done sequentially from first to last. 10 initialiution
expressions for "AUX" (or "OPTIONAL") can refer to objects which have jU5t been bound. For
example. this works:
S
AUXEX

with an appropriate fix. The TYPE change to LIST is a result of the REST. Since the LIST shares
all its elements with the original FORM. PUTs into the LIST wiJI change the calling program.
however dangerous that may be.
Examples:
S

$
3
EVAl can take a second argument, of TYPE ENVIRONMENT (or others, see section 20.8 below). An
ENVIRONMENT consists basically of a state of ATOM billdings; it is the "world" mentioned in chapter 5.
Now, since binding changes the ENVIRONMENT, if you wish to use EVAL within a FUNCTION, you
probably want to get hold of the environment which existed before that FUNCTION's binding took
place. The indicator "BIND''. which must, if it is used, be the first thing in an argument LIST,
provides this information. It binds the ATOM immediately following it to the ENVIRONMENT existing
-at call time" - that is, just before any binding is done for its FUNCTION. Example:
$

o

1»

$
1
(DEFINE RIGHT (IIBIND" E 'B "AUX" (A
$

1»

o

9.6 • 9.7

Functions

82
9.7.1. Local Values versus ENVIRONMENTs
SET, LVAL, VALUE, BOUND?, ASSIGNED? and UNASSIGN all take a final optional argument which
has not previously been mentioned: an ENVIRONMENT (or other TYPEs, see section 20.8 below). If
this argument is given, the SET or LVAL is done in the ENVIRONMENT specified. LVAL cannot be
abbreviated by • (period) if it is given an expJicit second argument.
This feature is just what is needed to cure the INC bug mentioned in chapter 5. A "correct" INC can
be defined as follows:
 
(SET H <+ .M 
S

o
Note: suppose an ACTIVATION of one Function (call it Fl) is passed to another Function (call it F2) for example, via an application of F2 within Fl with Fl's ACTIVATION as an argument. If F2
RETURNs to FI's ACTIVATION. F2 and Fl terminate immediately. and Fl returns the RETURN's first
argument. Good for error exits. AGAIN can clearly pull a similar trick. In the following example,
Fl computes the sum of F2 applied to each of its arguments; F2 computes the product of the
elements of its structured argument, but it aborts if it finds an element that is not a number.
 .ACT»
 FIX FLOAT»

S
F2

9.8

Functions

84

 .ARGTYPES»>
.ARG>
caUs a function to analyze .ARG. Which function is called depends on the TYPE of the argument;
this represents the idea of a dispatch table.

9.11. CLOSURE
(CLOSURE function .1 ... • N>
where function is a FUNCTION, and a1 through aN are any number of ATOHs. returns an object of TYPE
CLOSURE. This can be applied like any other function, but. whenever it is applied. the ATOMs given
in the call to CLOSURE are first bound to the VALUEs they had when the CLOSURE was generated. then
the function is applied as normal. This is a "poor man's funarg".
A CLOSURE is useful when a FUNCTION must have state information remembered between calls to it,
especially in these two cases: when the LVALs of external state ATOMs might be compromised by other
programs, or when more than one distinct sequence of calls are active concurrently. Example of the
latter: each Object of a structured NEWTYPE might have an associated CLOSURE that coughs up one
element at a time. remembering between calls how far it got. Often only one ATOM will be included
in the CLOSURE, with a value in the CLOSURE that is a structure containing all the relevant
information.

9.10 - 9.11

Functions

87

Chapter 10. Looping

10.1. PROG and REPEAT [1]
PROG and REPEAT are almost identical FSUBRs which make it possible to vary the order of EVALuation
arbitrarily - that is, to have "jumps". The syntax of PROG ("program' is


LP
(COND «EMPTY? ;TUP> 
(SET SUM <+ .SUM <1 .TUP»>


10.1.1 - 10.1.3

Looping

89

;"MDL style"
(DEFINE MY+ ("TUPLE" TUP)
, then to
. It can even PUT a new element farther down the
structure, which will be seen by the function on subsequent applications.
Now suppose. in addition to applying a function to a structure, you want to record the results •• the
values returned by the function - in another structure. Both MAPF and MAPR can do this: they both
take an additional function as an argument. and. when the looping is over, apply the additional
function to all the results. and then return the result of that application. Thus. if the additional
function is • LIST, you get a LIST of the previous results; if it is • VECTOR, you get a VECTOR of
results; etc.
Finally, it might be the case that you really want to loop a function over more than one structure
simultaneously. For instance, consider creating a LIST whose elements are the element-by-element
sum of the contents of two other LISTs. Both MAPF and MAPR allow this; you can. in fact, give each
of them any number of structures full of arguments for your looping function.

10.1.3 • 10.2

Looping

90
This was all mentioned because MAPF and MAPR appear to be complex when seen baldly. due to the
fact that the argument descriptions must take into account the general can. Simpler. degenerate
cases are usually the ones used.

10.2.1. MAPF [1]
(HAPF finalf loopf sl s2 .•• sN>
where (after argument evaluation)
finalf is something applicable that evaluates an its arguments. or a FALSE;
loopf is something applicable to N arguments that evaluates all its arguments; and
s1 through sN are structured objects (any TYPE)

does the fonowing:
(1) First. it appJies loopf to N arguments: the first element of each of the structures. Then it

RESTs each of the structures. and does the application again. looping until any of the structures
runs out of elements. Each of the values returned by loopf is recorded in a TUPLE.
(2) Then. it appJies fina/f to all the recorded values simultaneously. and returns the result of that
application. If finalf is a FALSE. the recorded values are "thrown away" (actually never recorded
in the first place) and the MAPF returns only the last value returned by loopf. If any of the si
structures is empty. so that loopf is never invoked. fina/f is applied to no arguments; if finalf is a
FALSE. HAPF returns IF ALSE ().

10.2.2. "APR [1]
(HAPR finalf loopf sl s2 ••• sN>
acts just like MAPF. but. instead of applying loopf to the elements of the structures. it applies it to
RESTs of the structures.

10.2.3. Examples [1]
Make the element·wise sum of two LISTs:
(MAPF ,LIST .+ '(1 2 34) '(10 11 12 13»1
(11 13 15 17)

10.2 • 10.2.3

Looping

91

Change a UVECTOR to contain double its values:

(SET UV 11[5 6 789]>$
!(5 6 7 8 9!]

(MAPR <>
'FUNCTION «L)  2»)
.UV>S
! [18! ]

.UVS
![10 12 14 16 18!]
Create a STRING from CHARACTERs:

(MAPF ,STRING 1 I(IIMODELLINGII IIDEVELOPMENTAMDLII

II

LIBRARY- ]>$

Sum the squares of the elements of a UVECTOR:

(MAPF
25

,+

'FUNCTION «N) <- .N .N»

A parallel assignment

11[3 4]>S

FUNCTION (Note that the arguments to MAPF are of different lengths.):

(DEFINE PSET (IITUPLEII TUP)
(MAPF <>
.SET
.TUP
(REST .TUP  2»»S
PSET
(PSET ABC 1 2 3>$
3

.AS
1

.BS
2

.C$
3

Note: it is easy to forget that (inalf must evaluate its arguments. which precludes the use of an
FSUBR. It is primarily for this reason that the SUBRs AND? and OR? were invented. As an example.
the predicate =? could have been defined this way:

10.2.3

Looping

92
 <==7 .A .B»
«AND   
 'I[LIST VECTOR UVECTOR STRING]>
 ,NOT .S>
>
It works because the ATOHs that name the common STRUCTURED PRIMTYPEs (LIST. VECTOR,
UVECTOR and STRING) have as GVALs the corresponding SUBRs to build objects of those TYPEs.]

10.3. More on HAPF and MAPR

10.3.1. HAPRET
HAPRET is a SUBR that enables the loopf being used in a MAPR or MAPF (and lexically within it. that is.
not separated from it by a function call) to return from zero to any number of values as opposed to
just one. For example. suppose a MAPF of the foUowing form is used:
 ••• >
Now suppose that the programmer wants to add no elements to the final LIST on some calls to the
FUNCTION and add many on other calls to the FUNCTION. To accomplish this. the FUNCTION simply
calls MAPRET with the elements it wants added to the LIST. More generally. MAPRET causes its
arguments to be added to the final TUPLE of arguments to which the finalf will be applied.
Warning: MAPRET is guaranteed to work only if it is caJled from an explicit FUNCTION which is the
second argument to a MAPF or MAPR. In other words. the second argument to HAPF or MAPR must be
#FUNCTION ( ••• ) or  if MAPRET is to be used.
Example: the following returns a LIST of aU the ATOMs in an OBLIST (chapter 15):
 
.E>
.STRUC»
10.~.~.

MAPLEAVE

HAP LEAVE is analogous to RETURN, except that it works in (lexically within) HAPF or "APR instead of
PROG or REPEAT. It flushes the accumulated TUPLE of results and returns its argument (optional.
default T) as the value of the MAPF or MAPR. (It finds the MAPF/R that should return in the current
binding of the ATOM LMAP\ ! -INTERRUPTS ("last mapj.) Example: the following finds and returns
the first non-zero element of its argument, or IFALSE () if there is none:
(DEFINE FIRST-NO (STRUC)
("APF <>
  

10.3.2 - 10.3.4

Looping

94

10.4. STACKFORM
STACKFORM is archaic. due to improvements in the implementation of MAPF/R (above). In fact.
(STACKFORM function arg pred)
is exactly equivalent to
(MAPF function
(FUNCTION () (COND (pred srg) (T (MAPSTOP»»)
In fact MAPF/R is more powerful. because MAP RET • MAPSTOP. and HAP LEAVE provide flexibility not
available with STACKFORM.
This FSUBR is used to apply a function to a variable number of arguments. as determined by an
arbitrary predicate. This could also be done by constructing the appropriate FORM and EVALing it.
STACKFORM is more efficient: it builds the "FORM" on the control stack (section 22.1). which produces
no "garbage". This is where STACKFORM obtains its name. One principle use involves processing
input characters. in cases where you don't know how many characters are going to arrive. The
example below demonstrates this, using SUBRs which are more fully explained in chapter 11.
Another example can be found in chapter 13.
An application of STACKFORM looks like this:
(STACKFORM function arg pred)
where
function must EVAL to a function which evaluates all its arguments;
arg is an arbitrary expression; and
pred is another arbitrary expression which should be capable of returning a FALSE when

EVALed.
Evaluation of an application of a STACKFORM proceeds as follows (it is vaguely like a "While" loop):
(1) Evaluate function and place the result on the control stack.
(2) Evaluate pred, and then:
(2.1) If pred evaluated to non.FALSE, evaluate srg and place the result on the control stack.
Then go back to the start of (2).
(2.2) If pred evaluated to a FALSE. apply the stacked function to the stacked args and return
the relult.

10.4

Looping

95

Example: the following FUNCTION reads characters from the current input channel until an $ (ESC)
is read, and then returns what was read as one STRING. (The SUBR READCHR reads one character from
the input channel and returns it. NEXTCHR returns the next CHARACTER which READCHR will return chapter 11.)


This returns the entire MDL object whose character representation il next in the input stream.
Successive s return successive objects. This is precisely the SUBR READ mentioned in chapter
2. See also sections 11.3. 15.7.1. and 17.1.3 for optional arguments.
11.1.1.2. READCHR
(READCHR>
("'read characterj returns the next CHARACTER in the input stream. Successive (READCHR>s return
successive CHARACTERs.
11.1.1.S. NEXTCHR

M

(-next character returns the CHARACTER which READCHR wiJ) return the next time READCHR is called.
Multiple (NEXTCHR>s. with no input operations between them. all return the same thing.
)

11.1.2. Output

If an object to be output requires (or can tolerate) separators within it (for example. between the
elements in a structured object or after the TYPE name in '" notation;. these conversion-output
SUBRs will use a carriage-returnlline-feed separator to prevent overflowing a line. Overflow is
detected in advance from elements of the CHANNEL in use (section 11.2.7).
11.1.2.1. PRINT
(PRINT any>
This outputs. in order.
(1) a carriage-return line-feed.
(2) the character representation of EVAL of its argument (PRINT is a SUBR). and
(3) a space
and then returns EVAL of its argument. This is precisely the SUBR PRINT mentioned in chapter 2.
11.1.2.2. PRIN 1
(PRINl any)
outputs just the representation of, and returns, EVAL of any.

11.1.1.1 - 11.1.2.2

Input/Output

99
11.1.2.3. PRINC

("print charactersj acts exactly like PRINI. except that
(I) if its argument is a STRING or a CHARACTER, it suppresses the surrounding ·s or initial ! \
respectively; or.
(2) if its argument is an ATOM. it suppresses any \s or OSLIST trailers (chapter 15) which would
otherwise be necessary.
If PRINC's argument is a structure containing STRINGs. CHARACTERs. or ATOMs. the service mentioned
will be done for all of them. Ditto for the ATOM used to name the TYPE in -, notation-.

11.1.2.4. TERPRI
(TERPRI>
,terminate printing) outputs a carriage-return line-feed and then returns IFALSE ()J
11.1.2.5. CRLF
(CRLF)
(-carriage-return line-feed") outputs a carriage-return line-feed and then returns T.
11.1.2.6. FLATSIZE

does not actually cause any output to occur and does not take a CHANNEL argument. In$lead. it
compares max with the number of characters PRINl would take to print any. If max is Jess than the
number of characters needed (including the case where any is self-referencing), FLATSIZE returns
IFALSE (); otherwise. it returns the number of characters needed to PRINl any. radix (optional.
default ten) is used for converting any FIXes that occur.
This SUBR is especially useful in conjunction with (section 11.2.7) those elements of a CHANNEL
which specify the number of characters per output line and the current position on an output line.

11.1.2.3 - 11.1.2.6

Input/Output

100
11.2. CHANNEL (the TYPE)

1/0 channels are dynamically assigned in MOL, and are represented by an object of TYPE CHANNEL.
which is of PRIHTYPE VECTOR. The format of a CHANNEL will be explained later, in section 11.2.7.
First, how to generate and use them.

11.2.1. OPEN
(OPEN mods fils-spsc)

or
(OPEN mode namel name2 dev dir>
OPEN is a SUBR which creates and returns a CHANNEL All its arguments must be of TYPE STRING.
and aJl are optional. The preceding statement is false when the device is "INT" or "NET"; see
sections 11.9 and 11.11. If the attempted opening of an ITS or TENEX I/O channel fails, OPEN
returns IFALSE (reason:string file-spec:string status:fix), where the rsason and the status are supplied
by the operating system, and the file-spec is the name of the file that MDL was trying to open.

The first argument to OPEN tells the mode, as understood by ITS, in which the CHANNEL will be used,
for a typical device:

"READ"

unit ASCII input (default)
unit ASCII output
"READB" block image input (not from a console)
"PRINTB" block image output (not to a console)
"PRINTO" block image update in place (only on liDS"", file must exist)
"DISPLAY" graphics
"PRINT"

The above modes are significant only under ITS, and only for devices about which MDL has no
special knowledge or which it does not treat speciaUy. The programmer's choice of mode is usually
determined by which SUBRs will be used 011 the CHANNEL The foUowing table tells which SUBRs can
be used with which modes, where 0" indicates an allowed use:

11.2 • 11.2.1

Input/Output

101

"READU "PRINP "READSII "PRINTB" "DISPLAY u mode I SUBRs
"PRINTO"
OK
OK
READ READCHR NEXTCHR READSTRING FILECOPY
FILE-LENGTH LOAD
OK
PRINT PRINl PRINC I"AGE TERPRI FILECOPY
PRINTSTRING BUFOUT RENAME
OK
READS GC-READ
OK
PRINTS GC-OUMP
OK
OK
OK
ACCESS
OK
OK
OK
OK
RESET
OK
OK
ECHOPAIR
OK
TTYECHO TYI
OK
DISPLAY ERASE
• PRINTing (or PRINling) an RSUSR (chapter 19) on a "PRINTS" or "PRINTO" CHANNEL has special
effects.
The next one to four arguments to OPEN specify the file involved. If only one STRING is used, it
can contain the entire specification, according to standard operating-system syntax. Otherwise, the
string{s) are interpreted as follows:
nameJ is the first file name, that part to the left of the space (ITS) or period (TENEX).

Default:

(VALUE NMl>, if any, otherwise "INPUT".
name2 is the second file name, that part to the right of the space (ITS) or period (TEN EX). Default:
(VALUE NMZ>. if any. otherwise ">" (ITS) or "MOL" and highest version number (TENEX).
dev is the device name. Default: , if any. otherwise IIOSK".
dir is the disk-directory name.

Default: . if any. otherwise the "working-directory"
name as defined by the operating system.
Examples:
(OPEN ·PRINP "TPL:II> opens a conversion-output CHANNEL to the TPL device under ITS.
(OPEN ·PRINT" IIDUMMY u UNAMES" "TPL") does the same.
(OPEN IIPRINP "TPL"> opens a CHANNEL to the file DSK :TPL
(OPEN IIREAD" UFOO"

")11

>(ITS) or DSK :TPL.HOL (TEN EX).

"DSKu "GUESP) opens a conversion-input CHANNEL to that file.

(OPEN "READII "GUEST.FOO") does the same under ITS.

11.2.1

Input/Output

102

11.2.2. OPEN-NR
OPEN-NR is the same as OPEN, except that the date of last reference of the opened file is not changed.

11.2.5. CHANNEL (the SUBR)
CHANNEL is called exactly like OPEN, but it always returns an unopened CHANNEL. which can later be
opened by RESET (below) just as if it had once been open.

11.2.4. CLOSE
(CLOSE channel)
closes channel and returns its argument, with its "state" Changed to ·closed". If channel is for output.
all buffered output is written out first. No harm is done if channel is already CLOSEd.

11.2.5. CHAN LIST
(CHANLIST)
returns a LIST whose elements are all the currently open CHANNEls. The first two elements are
usually .INCHAN and ,OUTCHAN (see below). A CHANNEL not referenced by anything except
(CHANLIST> will be CLOSEd during garbage collection.

11.2.6. INCHAN and OUTCHAN
The default channel for input SUBRs is the local value of the ATOM INCHAN. The default channel for
output SUBRs is the local value of the ATOM OUTCHAN.
You can direct 1/0 to a CHANNEL by SETting INCHAN or OUTCHAN (remembering their old values
somewhere), or by giving the SUBR you wish to use an argument of TYPE CHANNEL (These actually
have the same effect. because READ binds INCHAN to an explicit argument, and PRINT binds OUTCHAN
similarly. Thus the CHANNEL being used is available for READ macros (section 17.1) and PRINTTYPEs
(section 6.4.4).)
By the way. a good trick for playing with INCHAN and OUTCHAN within a function is to use the ATOMs
INCHAN and OUTCHAN as "AUX" variables, re.blndlng their local values to the CHANNEL you want.
When you leave, of course, the old LVALs are restored (which is the whole point). The ATOMs must be
declared SPECIAL (chapter 14) for this trick to compile correctly.

11.2.2 • 11.2.6

Input/Output

103

INCHAN and OUTCHAN also have global values. initia11y the CHANNEls directed at the console running
MDL. Initially, INCHAN's and OUTCHAN's local and global values are the same.

11.2.7. Contents of CHANNEls
The contents of an object of TYPE CHANNEL are accessed by the I/O SUBRs each time such a SUBR is
used. If you change the contents of a CHANNEL (for example. with pun. the next use of that
CHANNEL will be changed appropriately. Some elements of CHANNEls. however. should be played with
seldom. if ever. and only at your peril. These are marked below with an (asterisk). Caveat user.

*

There follows a table of the contents of a CHANNEL. the TYPE of each element. and an interpretation.
The format used is the following:
element-number: type interpretation

11.2.7.1. Output CHANNEls
The contents of a CHANNEL used for output are as follows:

·1: LIST
• 0: varies
• 1: FIX
.2: STRING
.. 3: STRING
.4: STRING
• 5: STRING
• G: STRING
.7: STRING
.8: STRING
.9: STRING
$10: STRING
$II: FIX
.12: FIX
13: FIX
14: FIX
15: FIX
16: FIX
17: FIX
18: FIX
19: FIX

transcript channel(s) (see below)
device.dependent information
channel number (ITS) or jfn (TENEX), 0 for display or internal or closed
mode
first file name argument
second file name argument
device name argument
directory name argument
real first file name
real second file name
real device name
real directory name
various status bits
PDP·10 instruction used to do one I/O operation
number of characters per line of output
current character position on a line
number of lines per page
current line number on a page
access pointer for file-oriented devices
radix for FIX conversion
information needed when outputting to the display

N.B.: The elements of a CHANNEL below number 1 are ulually invisible but are obtainable via  fix>. for some appropriate fix.

11.2.6 - 11.2.7.1

Input/Output

104

The transcript-channels slot has this meaning: if this slot contains a LIST of CHANNEls. then
anything input or output on the original CHANNEL is output on these CHANNEls. Caution: do 110t use
a CHANNEL as its own transcript channel; you probably won't Jive to tell about it.
11.2.7.2. Input CHANNEls

The contents of the elements up to number 12 of a CHANNEL used for input are the same as that for
output. The remaining elements are as fonows «same) indicates that the use is the same as that for
output):
13: varies
.14: FIX
.15: FIX

16:
17:
18:
19:

LIST
FIX
FIX
STRING

Object evaluated when end of file is reached
one "look·ahead" character. used by READ
PDp·I0 instruction executed waiting for input
queue of buffers for input from a console
access pointer for file-oriented devices (same)
radix for FIX conversion (same)
buffer for input

11.3. End-of-File "Routine"

As mentioned above, an explicit CHANNEL is the first optional argument of an SUBRs used for
conversion 1/0. The second optional argument for conversion.input SUBRs is an "end·of·file
routine· - that is, something for the input SUBR to EVAL and return. if it reaches the end of the file
it is reading. A typical end·of·file argument is a QUOTEd FORM which applies a function of yours.
The default value of this argument is a call to ERROR. Note: the CHANNEL has been CLOSEd by the
time this argument is evaluated.

Example: the following FUNCTION counts the occurrences of a character in a file. according to its
arguments. The file names. device, and directory are optional, with the usual defaults.
>

11.2.7.1 - 11.3

Input/Output

105

11.4. Imaged 110

11.4.1. Input
11.4.1.1. READS
(READS buffer:uvector-or-storage channel eof:any>
The channel must be open in "READS" mode. READS will read as many 36·bit binary words as
necessary to fill the buffer (whose UTYPE must be of PRIMTYPE WORD), unless it hits the end of file.
READS returns the number of words actually read, as a FIXed·point number. This will normally be
the length of the buffer, unless the end of file was read, in which case it will be less, and only the
beginning of buffer will have been filled (SUBSTRUC may help). An attempt to READS again, after
buffer is not filled, will evaluate the end·of·file routine eof, which is optional, default a call to
ERROR.
11.4.1.2. READSTRING
(READSTRING buffer:string channel stop:fix-or-string eof>
is the STRING analog to READB, where buffer and eof are as in READB, and channel is any input
CHANNEL (default .INCHAN). stop tells when to stop inputting: if a FIX, read this many CHARACTERs
(default: fill up buffer); if a STRING, stop reading if any CHARACTER in this STRING is read (don't
include this CHARACTER in final STRING).

11.4.2. Output
11.4.2.1. PRINTS
(PRINTB buffer:uvector-or-storage channel>
This call writes the entire contents of the buffer into the specified channel open in -PRINTS- or
"PRINTO" mode. It returns buffer.
11.4.2.2. PRINTSTRING
(PRINTSTRING buffer:string channel count:fix>
is analogous to READSTRING. It outputs buffer on channel, either the whole thing or the first count
characters. and returns the number of characters output.

11.4 • 11.4.2.2

Input/Output

106

11.4.2.3. IMAGE
< IMAGE fix channel>
is a rather special-purpose SUBR. When any conversion-output routine outputs an ASCII control
character (with special exceptions like carriage-returns. line-feeds. etc.). it actually outputs two
characters: A (circumflex). fonowed by the upper-case character which has been control-shifted.
IMAGE. on the other hand. always outputs the real thing: that ASCII character whose ASCII 7-bit
code is fix. It is guaranteed not to give any gratuitous line-feeds or such. channel is optional. with
default .OUTCHAN, and its slots for current character position (number 14) and current line number
(16) are not updated. IMAGE returns fix.

11.5. Dumped I{O

11.5.1. Output: GC-DUMP
(GC-DUMP any printb:channel-or-false>
dumps any on printb in a clever format so that GC-READ (below) can reproduce any exactly. including
sharing. any cannot live on the control stack. nor can it be of PRIMTYPE PROCESS or LOCO or ASOC
(which see). any is returned as a value.
If printb is a CHANNEL, it must be open in "PRINTS" or "PRINTO· mode. If printb is a FALSE. GCDUMP instead returns a UVECTOR (of UTYPE PRIMTYPE WORD) that contains what it would have output
on a CHANNEL This UVECTOR can be PRINTBed anywhere you desire. but. if it is changed in any way.
GC-READ will not be able to input it. Probably the only reason to get it is to check its length before
output.
Except for the miniature garbage collection required. GC-DUMP is about twice as fast as PRINT. but
the amount of external storage used is two or three times as much.

11.5.2. Input: GC-READ

returns one object from the channel. which must be open in "READB" mode. The file must have been
produced by GC-DUMP. eof is optional. GC-READ is about ten times faster than READ.

11.4.2.3 - 11.5.2

Input/Output

107

11.6. SAVE Files
The entire state of MDL can be saved away in a file for later restoration: this is done with the SUBRs
SAVE and RESTORE. This is a very different form of I/O from any mentioned up to now; the file
used contains an actual image of your MDL address space and is not. in general. "legible" to other
MDL routines. RESTOREing a SAVE file is much faster than re-READing the objects it contains.
Since a SAVE file does not contain all extant MDL objects. only the impure and PURIFYed (section
22.9.2) ones, a change to the interpreter has the result of making all previous SAVE files unusable.
To prevent errors from arising from this, the interpreter has a version number. which is
incremented whenever changes are installed. The current version number is printed out on initially
starting up the program and is available as the GVAL of the ATOM MUDDLE. This version number is
written out as the very first part of each SAVE file. If RESTORE attempts to re-Ioad a SAVE file
whose version number is not the same as the interpreter being used, an ERROR is produced. If
desired, the version number of a SAVE file can be obtained by doing a READ of that file. Only that
initial READ will work; the rest of the file is 110t ASCII.

11.6.1. SAVE
 and
renamed to the argument(s) only when complete. to prevent losing a previous SAVE file if a crash
occurs. Under TENEX, version numbers provide the same safety.
Example:

11.6 • 11.6.1

Input/Output

108

(DEFINE SAVE-IT ("OPTIONAL"
(FILE '("PUBLIC" "SAVE" "DSK" "GUEST"»
"AUX" (DIR ,SNM»
(SETUP>
(SETG SNM 1111>
(COND «=7 "SAVEDH 

"Saved.")
(T

(TERPRI>
(PRINC "Amazing program at your service.">
(TERPRI>
(START-RUNNING»»

11.6.2. RESTORE
(RESTORE file-spec>
or
(RESTORE namel name2 dev dir>
replaces the entire current state of your MDL with that SAVEd in the file specified. All arguments
are optional. with defaults as in SAVE.
RESTORE completely replaces the contents of the MDL. including the state of execution existing
when the SAVE was done and the state of al1 open I/O CHANNEls. If a file which was open when the
SAVE was done does not exist when the RESTORE is done. you will get a message to that effect.
A RESTORE!:!!!£! returns (unless it gets an error): it causes a SAVE done some time ago to return

again (this time with the value "RESTORED"). even if the SAVE was done in the midst of running a
program. In the latter case. the program will continue its execution upon RESTOREation.

11.7. Other I/O Functions

11.7.1. LOAD
(LOAD input:chsnnel oblist>

11.6.1 - 11.7.1

Input/Output

109

eventually returns "DONE". First, however, it READs and EVAls every MDL object in the file pointed
to by input, and then CLOSEs input. Any occurrences of rubout, A@, AD, AL, etc., in the file are
given no special meaning; they are simply ATOM constituents.
oblist ioS optional, used to specify a LIST of OBLISTs for the READ. Its default is .OBLIST (chapter
15).

11.7.2. FLOAD
(FLOAD fils-spec oblist>
or
(FLOAD namel name2 dsv dir oblist>
("file load") acts just like LOAD, except that it takes arguments (with defaults) like OPEN. OPENs the
CHANNEL itself for reading, and CLOSEs the CHANNEL when done. oblist is optional, as in LOAD. If the
OPEN fails. an ERROR occurs, giving the reason for failure.

11.7.3. SNAME
(SNAME string> ("system name", a hangover from ITS) is identical in effect to (SETG SNM string>.
that ii, it caules the default dir argument to aU SUBRI which want fUe specifications to become iu
argument, in the absence of a local value for SNM, and it returns its argument.
(SNAME> is the same in effect as , that is, it returns the current default dir.

11.7.4. ACCESS
(ACCESS channel fix>
returns channel, after making the next character or binary word (depending on the mode of channel,
which should not be "PRINP) which wiIJ be input from or output to channel the (fix+l)st one from
the beginning of the file. channel must be open to a randomly accessible device ("DSK". "USR",
etc.). A fix of 0 positions channel at the beginning of the file.

11.7.5. FILE-LENGTH
(FILE-LENGTH input:channel)

11.7.1 • 11.7.5

Input/Output

110
returns a FIX, the length of the file open on input. This information is supplied by the operating
6ystem. and it may not be available, for example, with the IINET" device (section 11.11). If input's
mode is uREADu. the length is in characters (rounded up to a multiple of five); if "READS'" in binary
words. If ACCESS is applied to input and this length or more. then the next input operation will
detect the end of file.

11.7.6. FlLECOPV
(FILECOPV input:channel output:channel)
copies character, from input to output until the end of file on input (thus closing input) and returns
the number of characters copied. Both arguments are optional. with respective defaults • INCHAN
and .OUTCHAN. The operation is essentially a REAOSTRING - PRINTSTRING loop. Neither CHANNEL
need be freshly OPENed. and output need not be immediately CLOSEd. Restriction: internally a
 (-I and control-Z). the latter being the TENEX end-of-file signal.

11.S.1 - 11.9

Input/Output

113

For a "PRINT" CHANNEl, the function must take one argument. which will be a CHARACTER. It can
dispose of its argument in any way it pleases. The value returned by the function is ignored.
Example: (OPEN "PRINT" IIINT:II .FCN> opens an internal output CHANNEL with .FCN as its
character-gobbler.

lLl~

"DISPLAY"

CHANNE~

"DISPLAY" CHANNE~ are used to display graphical information. of TYPE PICTURE. The
manipulation of useful PICTUREs is covered in ref S. The following two SUSRs are not available
under TEN EX.

11.10.1. DISPLAY
(DISPLAY display:channel picture>
displays picture on the device associated with channel. If picture is not given. the display on the
device is refreshed. which is useful only for display consoles.

lLIO.2. ERASE
(ERASE display:channel picture>
removes picture from the display on channel. If picture is not given, all PICTUREs displayed on
ch.nnel will be removed. leaving an empty display.

11.11. The "NET" Device: the ARPA Network
The "NET" device is different in many ways from conventional devices. Under ITS, it is the only
device besides "INT" that does not take all strings as its arguments to OPEN, and it must take an
additional optional argument to specify the byte size of the socket. The format of a call to open a
network socket is
(OPEN modll:strinl locsl-socl
accepts a connection to a socket that is open for listening and returns its argument. It will return a
FALSE if the connection is in the wrong state.
11.11 - 11.11.2

Input/Output

115

11.11.3. NETS


returns its argument. after forcing any system-buffered network output to be sent. ITS normally
does this every half second anyway. TENEX does not do it unless and until NETS is caJled. NETS is
similar to BUFOUT for normal CHANNEls, except that even operating-system buffers are emptied !!2~...

11.11.3

Input/Output

116

Cha.pter 12. Looa.tives
There is in MDL a facility for obtaining and working directly with objects which roughly
correspond to "pointers" in assembly language or "lvals" in BePL or PAL. In MOL, these are
generically known as locatives (from "location") and are of several TYPEs, as mentioned below.
Locatives exist to provide efficient means for altering structures: direct replacement as opposed to
re-copying.
Locatives always refer to elements in structures. It is not possible to obtain a locative to something
(for example, an ATOM) which is not part of any structure. It is possible to obtain a locative to any
element in any structured object in MOL •• even to associations (chapter IS) and to the values of
ATOMs. structurings which are normally "hidden".
In the following. the object occupying the structured position to which you have obtained a loative
will be referred to as the object pointed to by the locative.

12.1. Obtaining Locatives

12.1.1. LLOC

returns a locative (TYPE LOCO, "locative to iDentifier") to the LVAL of atom in snv. If atom is not
bound in env, error. env is optional, with default being the current ENVIRONMENT. The locative
returned by LLOC is independent of future re.bindings of atom. That is, IN (see below) of that
locative will return the same thing even if atom is re·bound to something else; SETLOC (see below)
wiIJ affect only that particular binding of atom.
Since bindings are kept on a stack (tra la), any attempt to use a locative to an LVAL which has
become unbound will fetch up an error. (It breaks just like a TUPLE ••.. ) LEGAL 1 can, once again.
be used to see if a LOCO is valid. Caution: (SET A (LLOC A» creates a self·reference and can make
PRINT very unhappy.

12·12.1.1

Locatives

117
12.1.2. GLOC


returns a locative (TYPE LOCO) to the GVAL of atom. If atom has no GVAL slot, an ERROR occurs, unless
prlJd (optional) is given and not FALSE. in which case a sJot is created (chapter 22). Caution: 

returns a locative to the firth element in structured. fix is optional, default 1. The exact TYPE of the
locative returned depends on the PRIMTYPE of structured: LOCL for LIST, LOCV for VECTOR, LOCU for
UVECTOR, LOCS for STRING, LOCB for BYTES, LOCT for TEMPLATE, and LOCA for TUPLE. If fix is
greater than  or less than I. error. The locative is unaffected by applications of
RES T. BACK. TOP, GROW, etc. to structured.
12.1.4. GETPL and GETL
 I! [LOCO LOCL "' J>.

12.1.2 • 12.2

Locatives

118

12.3. Using Locatives
The following two SUBRs provide the means for working with locatives. They are independent of
the specific TYPE of the locative. The notation locative indicates anything which could be returned
by LLOe. GLOe. AT. GETPL or GETL

12.3.1. IN



returns the object to which locative points. The only way you can get an error using IN is when
locative points to an LVAL which has become unbound from an ATOM. This is the same as the
problem in referencing TUPLEs as mentioned in section 9.2. and it can be avoided by first testing
< LEGAL? locd>.

Example:
$
1

returns any, after having made any the contents of that position in a structure pointed to by
locative. The structure itself is not otherwise disturbed. Error if IOCltivtl is to a non-LEGAL? LVAL or
if you try to put an object of the wrong TYPE into a PRIMTYPE UVECTOR, STRING, BYTES, or
TEMPLATE.

Example:
(SET A (1 Z 3»$
(l 2 3)
(SETLOC (AT .A Z> HI>$
HI
.A$
(l HI 3)

12.3 • 12.3.2

Locatives

119

12.4. Note on Locatives
You may have noticed that locatives are, strictly speaking. unnecessary; you can do everything
locatives allow by appropriate use of. fot example. SET, LVAL. PUT, NTH. etc. What locatives provide
is generality.
Basically. how you obtained a locative is irrelevant to SETLOC and IN; thus the same program can
play with GVALs. LVALs. objects in explicit structures, etc., without being bothered by what function
it should use to do so. This is particularly true with respect to locatives to LVALsj the fact that they
are independent of changes in binding can save a lot of fooling around with EVAL and

ENVIRONMENTs.

12.4

Locatives

120

Chapter 13. Assooiation (Properties)
There is an "associative" data storage and retrieval system embedded in MDL which allows the
construction of data structures with arbitrary selectors. It is used via the SUBRs described in this
chapter.

13.1. Associative Storage
13.1.1. PUTPROP
(PUTPROP item:any indicator:any valus:any>

("'put property' returns item, having associated value with item under the indicator indicator.

13.1.2. PUT
(PUT item:any indicator:any value:any>

is identical to PUTPROP, except that, if item is structured and indicator is of TYPE FIX, it does
(SETlOe (AT item indicator> valus>. In other words. an element with an integral selector is stored
in the structure itself. instead of in association space. PUT (like AT) will get an error if indicator is
out of range; PUTPROP will not.

13.1.3. Removing Associations
If PUTPROP is used without its value argument, it removes any association existing between its item

argument and i15 indicator argument. If an association did exist, using PUTPROP In this way returns
the value which was associated. If no association existed, it returns IFALSE ().
PUT, with arguments which refer to association, can be used in the same way.

13 • 13.l.3

Association (Properties)

121

If either item or indicator cease to exist (that is, no one was pointing to them, so they were garbagecollected), and no locatives to the association exist, then the association between them ceases to exist
(is garbage-coJ)ected).

13.2. Associative Retrieval

15.2.1. GETPROP

(-get property; returns the value associated with item under indicator, if any. If there is no such
association, GETPROP returns EVAL of exp (that is, eKp gets EVALed both at call time and later).
exp is optional. If not given, GETPROP returns IFALSE () if it cannot return a value.

Note: item and indicator in GETPROP must be the !!ill! MOL ob iects used to establish the association;
that is. they must be aa 7 to the objects used by PUTPROP or PUT.

13.2.2. GET

is the inverse of PUT. using NTH or GETPROP depending on the test outlined in section lS.l.2. exp il
optional and used as in GETPROP.

13.3. Examples of Association
$
(1 Z 3 4)

$
(1 2 3 4)

S
"list on a zero"
works because <==1 .N 0) is true.
To associate something with the Nth position in a structure, as opposed to its Nth element. associate
it with (REST structure N-l). as in the following:

$
(3 4)

 PERCENT)$
IFALSE ()

(GET (REST .L 2) PERCENT>$
0.30000000
Remember comments?

$
![A BCD E!]

(GET (REST .N 2) COMMENT>$
"third element"
The I in the  is to keep EVAL from generating a new UVECTOR ("Direct
Representation"). which would not have the comment on it (and which would be a needless
duplicate). A "top-level" comment -- one attached to the entire object returned by READ -- is PUT on
the CHANNEL in use, since there is no position in any structure for it. If no top.level comment
follows the object, READ removes the value «PUT channel COMMEND); so anybody that wants to see a
top-level comment must look for it after each READ.
If you need to have a structure with selectors in more than one dimension (for example, a sparse

matrix that does not deserve to be linearized), associations can be cascaded to achieve the desired
result. In effect an extra level of association maps two indicators into one. For example. to
associate value with item under indicator-l and indicator-2 simultaneously:

(PUTPROP

indicator-l indicator-2

D
13.3

Association (Properties)

123

(PUTPROP

item

(GETPL

indicator-l indicator-2) value)

13.4. Examining Associations
Associations (created by PUT and PUTPROP) are chained together in a doubly-linked list. internal to
MDL. The order of associations in the chain is their order of creation. newest first. There are
several SUBRs for examining the chain of associations. ASSOCIATIONS returns the first association
in the chain. or IFALSE () if there are none. NEXT takes an association as an argument and returns
the next association in the chain. or IfIFALSE () if there are no more. ITE"" INDICATOR and AVALUE
all take an association as an argument and return the item. indicator and value. respectively.
Associations print as:

#ASOC

(item indicator value)

(sic: only one S). Example: the following gathers all the existing aNociations into a LIST.

(PROG «A (ASSOCIATIONS»)
(CONO «NOT .A) Ie»~
(T (.A I(MAPF .LIST
(FUNCTION () (COND «SET A (NEXT .A» .A)
(T (MAPSTOP»»»»)

13.3 -13.4

Association (Properties)

124

Ohapter 14. Data-type Deolaratlons
In MDL. it i. p05lible to declare the permiuibJe range of "tYPei" and/or 5tructures that an ATOM's
values or a function's arguments or value may have. This is done using a special TVPE, the DECL
rdeclaration·~ A OECL is of PRIMTYPE LIST but has a complicated internal structure. OECLs are
used by the interpreter to find TYPE errors in function calling and by the compiler to generate more
efficient code.
There are two kinds of DECLs. The first kind of OECL is the most common. It is called the ATOM
DECL and is used most commonly to specify the type/structure of the LVALs of the ATOMs in the
argument LIST of a FUNCTION or aux LIST of a PROG or REPEAT. This OECL has the form:
IDECL (atoms:list Pattern ••• )
where the pairing of a LIST of ATOMs and a "Pattern" can be repeated indefinitely. This declares the
ATOMs in a list to be of the type/structure specified in the fonowing Pattern. The special ATOM
VALUE. if it appears, decJares the result of a FUNCTION call or PROG or REPEAT evaluation to satisfy
the Pattern specified. An ATOM DECL is useful in only one place: immediately following the
argument LIST of a FUNCTION, PROG or REPEAT. It normally includes ATOHs in the argument LIST
and ATOMs whose LVALs are otherwise used in the Function body.
The second kind of OECL is rarely seen by the casual MDL user. except in appendix 2. It is caJJed
the RSUBR OECL It is used to specify the type/structure of the arguments and result of an RSUBR or
RSUBR-ENTRY (chapter 19). It is of the fonowing form:
IOECL ("VALUE" Pattern Pattern ••• )
where the STRING "VALUE" precedes the specification of the type/structure of the value of the ca11 to
the RSUBR, and the remaining Patterns specify the arguments to the RSUBR in order. The full
specification of the RSUBR OECL will be given in section 14.8. The RSUBR OECL is useful in only
one place: as an element of an RSUBR or RSUBR-ENTRY.

14

Data-type DecJarations

125

14.1. Patterns
The simplest pOS5ible Pattern is to $11 that a vaJue i5 exactJy $orne other object. by giving that
obJect.OUOTEd. For example, to declare that a variable 1. a particular ATOM:
IDECL e eX) IT)
declares that .X is always the ATOM T. When variables are DECLed al "being" lome other object in
this way. the test used is -? and not ... ? The distinction is usuaJJy not important. as ATOMs. which
are most commonly used in this construction. are u1 to each other if =1 anyway.
It is more common to want to specify that a value must be of a given TYPE. This is done with the
simplest non-specific Pattern. a TYPE name. For example.
IDECL (eX) FIX (Y) FLOAT)
declares. X to be of TYPE FIX. and. Y of TYPE FLOAT. In addition to the names of all of the built-in
and created TYPE6. such as FIX, FLOAT and LIST, a few "compound" type namel are allowed:
ANY allows any TYPE.
STRUCTURED allows any structured TYPE, such as LIST, VECTOR,
(appendix 3).

FALSE,

CHANNEL. etc.

LOCATIVE allows any locative TYPE. such as are returned by LLOC, GLOC, AT. and so on (chapter

12).
APPLICABLE allows any applicable TYPE. such as FUNCTION, SUBR, FIX (I). etc. (appendix 3).
Any other ATOM can be used to stand for a more complex construct. if an association is
established on that ATOM and the ATOM OECL A common example i5 to  (Y) 

14.1

Data-type Declarations

126
where there is a one-to-one correspondence between the P,tterns and the elements of the structure.
For example:
lOECL «X)  LIST»

declares • X to be a VECTOR containing another VECTOR of at least two elements, and • Y to be of
PRIMTYPE LIST. containing a LIST. In the case of a BYTES, the individual elemenu cannot be
declared (they must be FIXes anyway), only the size and number of the bytes:
IDECL «B)  (Y) (LIST [3 FIX FLOAT]»
• X is declared to contain three FIXes and a FLOAT, perhaps followed by other elements. • Y is
declared to repeat the sequence FIX-FLOAT three times. Note that there may be more repetitions of
the seq uence in • Y (but not in . X): the OECL specifies only the first six elements.
For indefinite repetition, the same cOllstruction is used, but, instead of the number of repetitions of
the sequence of Patterns. the ATOM REST is given. This allows any number of repetitions, from ~ero
on up. For example:
IDECL «X)  (Y) (LIST [3 FIX] [REST FIX]>

14.1

Data-type Declarations

127

A "REST construction" can contain any number of Patterns, just like an NTH construction:
IDECL «X)  is an even multiple of three; the VECTOR can end at any point.
A variation on REST is OPT (or OPTIONAL), which is similar to REST except that the construction is
scanned once at most instead of indefinitely, and further undeclared elemenu can follow. For
example:

IDECL (eX) 

Any non-compound Pattern can be included as one of the elements of the compound Pattern.
Finally. compound Patterns can be used as Patterns for elements of structures, and so on.
IDECL (eX) 
(Y) ]»)
The OR construction can be extended to any level of ridiculousness, but the higher the level of
complexity and compoundedness the less likely the compiler will find the DECL useful.
At the highest level, any Pattern at top level in an ATOM DECL can be enclosed in the construction
( speciaJity:atom Pattern

>

which expJicitly declares the speciality of the ATOM(s) in the preceding LIST. speciality can be either
SPECIAL or UNSPECIAL. Speciality is important only when the program is to be compiled. The word
comes from the control stack. Which is called "special" in Lisp because the garbage collector finds
objects on it and modifies their internal pointers when storage is compacted. (An internal stack is
used within the interpreter and is not accessible to programs - section 22.1.) In an interpreted

14.1

Data-type Declarations

128

program all local value. are initially SPECIAL. because all bindings are put on the control stack (but
see SPECIAL-MODE below). When the program is compiled, only values declared SPECIAL (which may
or may not be the default) remain in bindings on the control stack. All others are taken care of
simply by storing objects on the control stack: the ATOMs involved are not needed and are not
created on loading. So, a program that SETs an ATOM's local value for another program to pick up
must declare that ATOM to be SPECIAL If it doesn't, the ATOM's binding will go away during
compiling, and the program that needs to access the ATOH will get a no-value error or access an
erroneous binding. Usually only ATOMs which have the opposite speciality from that of the current
SPECIAL-HaDE are explicitly declared. The usual SPECIAL-MODE is UNSPECIAL. so typically only
SPECIAL declarations use this construction:
lOECL «X)  [4 ]»
declares • LL to be of PRIMTYPE LIST, and to have at least four elements. each of which are LISTs of
unspecified length (possibly empty) containing FIXes.
IDECL «VV) 

declares • X to be a VECTOR containing at least one FIX. The more restrictive [REST FIX] would take
excessive checking time by the interpreter. because the REST of the VECTOR would be checked on
each iteration of the MAPR. In this case both DECLs are equally powerful. because checking the first
element of all the RESTs of a structure eventually checks all the elements. Also, since the FUNCTION
refers only to the first element of X, this is as much declaration as the compiler can effectively use.
(If this VECTOR always contains only FIXes. it should be a UVECTOR instead. for space efficiency.
Then a [REST FIX] DECL would make the interpreter check only the UTYPE. If the FIXes cover a
small non-negative range, then a BYTES might be even better, with a DECl of .)
 1) (ELSE <*

.N

 


«+

The above declares land N to be UNSPECIAL. says that .N is a FIX, and says that. L. along with the
value returned. is a lIST of any length composed entirely of FIXes.

14.3. The DECl Syntax
This section gives quasi.BNF productions for the MDL DECl syntax. In the following table MDL
type-specifiers are distinguished in this w'y.

14.2 - 14.3

Data-type Declarations

130

decl

:;=

declprs ::atlist

·...--

IOECL (declprs)
(atl1st) pattern

atom I atom at 1 i st

pattern ::a

pat

pat

: :=

unit

unit

......-

type

I
I
I
I

I  I 
I 

I  J atom I lany
ANY I STRUCTURED I LOCATIVE I APPLICABLE
 I «OR strue ••• strue> elts>
! I 1«OR strue ••• strue) alts>
 I ! 

struc

·..-

structured-type

elts

::=

pat

~

deelprs deelprs

I

I

I 

I pat elts

[fix pat ••• pat]
[fix pat ••• pat] elts

I [opt pat
pat] I [REST pat ••• pat]
I [opt pat ••. pat] [REST pat ••• pat]
opt

· .-

OPT

I OPTIONAL

14.4. Good DECls
There are ,orne rules of thumb concerning "good" OECLs. A "good" OECL i5 one that is minimally
offensive to the OECL-checldng mechanism and the compiler. but that gives the maximum amount
of information. It is simple to state what gives offense to the compiler and DECL-checking
mechanism: complexity. For example, a large compound DECL like:
IDECL «X) . maybe you should really say STRUCTURED. Also. a very general DECL
indicates a very general program. which is not likely to be efficient when compiled (of course there
i.s a tradeoff here). Narrowing the DECl to one PRIMTYPE gives a great gain in compiled efficiency.
to one TYPE still more.

14.3 - 14.4

Data-type Declarations

131

Another situation to be avoided is the ordinary large DECL. even if it is perfectly straightforward.
If you have created a structure which has a very specific OECL and is used all over your code. it
might be better as a NEWTYPE (see below). The advantage of a NEW TYPE over a large explicit OECL is
twofold. First. the entire structure must be checked only when it is created. that is. CHTYPEd from
its PRIHTYPE. As a full OECL, it is checked completely on entering each function and on each
reassignment of ATOMs OECled to be it. Second, the amount of storage saved in the OECLs of
FUNCTIONs and so on is large. not to mention the effort of typing in and keeping up to date several
instances of the full OECL

14.5. Global OECLs

14.5.1. GOECL and MANIFEST
There are two ways to declare GVALs for the OECL-checking mechanism. These are through the
FSUBR GDECL ("global declaration") and the SUBR MANIFEST,
(GOECL atoms:list Pattern ..• >
GOECL allows the type/structure of global values to be declared in much the same way as local
values. Example:
(GDECL (X) FIX (Y) (LIST FIX»
declares. X to be a FIX, and. Y to be a LIST containing at least one FIX.
(MANIFEST atom atom , .• >
MANIFEST takes as arguments ATOMs whose GVALs are declared to be constantl, It is used most
commonly to indicate that certain ATOMs are the names of offsets in structures. For example:
(SETG X 1>
(MANIFEST X>
allows the compiler to confidently open-compile applications of X (getting the first element of a
structure). knowing that • X will not Change. Any sort of object can be a MANIFEST value; if it does
not get embedded in the compiled code. it is included in the RSUBR's "reference vector", for fast
access. However. as a general rule. structured objects should not be made MANIFEST: the SETG will
survive in the compiled version (for the use of new uncompiled programs). but uses of GVAL will
instead refer to a distinct copy of the object in each RSUBR that does a GVAL A structured object
should instead be GOECled.
An attempt to SETG a MANIFEST ATOM will cause an ERROR, unless either:
14.4 - 14.5.1

Data-type Declarations

132

(l) the ATOM was previously globally unassigned;

(2) the old value is ==? to the new value; or
(3) .REDEFINE is not FALSE.

14.5.2. MANIFEST? and UNMANIFEST

returns T if .tom is MANIFEST, 'FALSE () otherwise.

("globally bound?·) returns T if ,tom has a global value slot {that is. if it hal ever been SETGed.
MANIFEST. GDECLed. or GLOCed (chapter 12) with a true second argument), 'FALSE () otherwise.

14.6. NEWTYPE (again)
NEWTYPE gives the programmer another way to OECL objects. The third (and optional) argument of
NEWTYPE is a QUOTEd Pattern. If given, it will be saved as the value of an association (chapter 13)
using the name of the NEWTYPE as the item and the ATOM OECL as the indicator. and it will be used to
check any object that is about to be CHTYPEd to the NEWTYP£. For example:
 FLOAT FLOAT}>
creates a new TYPE, with its first two elements declared to be FLOATs. If later someone types:
#COMPLEX -NUMBER [1. 0 2]
an ERROR wj)) result (the second element is not a FLOAn. The Pattern can be replaced by doing
another NEWTYPE for the same TYPE. or by putting a new value in the association. Further examples:

 BAR [REST FIX FLOAT ATOM]»
This is an example of a recursively OECLed TYPE. Note that (I .A> does not satisfy the DEClo
because it is empty. but it was CHTYPEd before the OECL was associated with BAR. Now. even
  will cause an error.
In each of these examples. the «PRIMTYPE ••• ) ••• > construction was used. in order to permit
CHTYPEing an object into itself. See what happens otherwise:
 wiJ1 cause an error. Unfortunately. you must
 OOPS)S
lOOPS (E 2.71828)

14.7. Controlling DECL Checking
There are several SUBRs and FSUBRs in MDL that are used to control and interact with the DECLchecking mechanism.

14.7.1. DECL-CHECK
This entire complex checking mechanism can get in the way during debugging. As a result. the
most commonly used DECL-oriented SUBR is OECL-CHECK. It i, used to enable and disable the entire
DECL-checking mechanism.


If its single argument is non-FALSE. OECL checking is turned on; if it is FALSE. DECL checking is
turned off. The previous state is returned as a value. If no argument is given. DECL-CHECK returns
the current state. In an initial MDL OECL checking is on.
When DECL checking is on. the OECL of an ATOM is checked each time it is SET. the arguments and

14.6 - 14.7.1

Data-type Declarations

134

results of calls to FUNCTIONs. RSUBRs. and RSUBR-ENTRYs are checked. and the values returned by
PROG and REPEAT are checked. The same is done for SETGI and. In particular. attempts to change
MANIFEST global values. Attempts to CHTYPE an object to a NEWTYPE (if the NEWTYPE has the
optional DECL) are also checked. When DECL checking is off. none of these checks is performed.
14.7.2. SPECIAL-CHECK and SPECIAL-MODE
(SPECIAL-CHECK false-or-any)
controls whether or not SPECIAL checking is performed at run time by the interpreter. It is initially
off. Failure to declare an ATOM to be SPECIAL when it should be will produce buggy compiled code.
(SPECIAL-HODE speeiality:atom)
sets the default mode (for ATOMs not declared either way) and returns the previous default. or the
current default if no argument is given. The initial default is UNSPECIAL

14.7.S. GET-DECL and PUT-DECL
GET -DECL and PUT -DECL are used to examine and change the current DECL (of either the global or
the local value) of an ATOM.
(GET -DECL loed)
returns the DECL Pattern (if any. otherwise IFALSE ()) associated with the global or local value slot
of an ATOM. For example:
(PROG (X)
IDECL «X) (OR FIX FLOAT»)
(GET-DECL (LLOC X»

••• >
would return (OR FIX FLOAT) as the result of the application of GET-DECL. Note that because of
the use of LLOC (or GLOC. for globals) the ATOM being examined must be bound. otherwise you will
get an errorl This can be gotten around by testing first with BOUND? (or GBOUND? or by giving GLOC
a second argument which is not FALSE).
'

If the slot being examined is the global slot and the value is MANIFEST. then the ATOM MANIFEST is
returned. If the value being examined is not DEC Led. IFALSE () 11 returned.
(PUT-DECL loed 'Pattern)

14.7.1 • 14.7.3

Data-type Declaration5

135

makes Pattern be the DECL for the value and returns loed. If (OECL-CHECK> is true. the current value
must satisfy the new Pattern. PUT-DECL is normally used in debugging, to change the OECL of an
object to correspond to changes in the program. Note that it is not legal to PUT -DECL a "Pattern" of
MANIFEST or IFALSE ().

14.7.4. DECL 7

(OECL? any 'Pattern>
specifically checks any against Pattern. For example:
(OECL7 '(I 2 3] '
or

or

returns the OBLIST whose name is oblist-name.
Since there is nothing special about the association of OBLISTs and their names. the name of an
OBLIST can be changed by use of PUTPROP, both on the OBLIST and its name. It is not wise to
change the OBLIST association without changing the name association. as you are likely to confuse
READ and PRINT terribly.
You can also use PUT or PUTPROP to remove the association between an OBLIST and its name
completely. If you want the OBLIST to go away (be garbage coJJected). and you want to keep its
name around. this must be done; otherwise the association wi)) force it to stay. even if there are no
other references to it. (If you have no references to either the name or the OBLIST (an ATOM -including a TYPE name -- points to its OBLIST). both of them - and their association -- will go away
without your having to remove the association. of course.) It is not recommended that you remove
the name of an OBLIST without having it go away. since then ATOMs in that OBLIST wi)) PRINT the
same as if they were in 110 OBLIST •• which is defeating the purpose of this whole exercise.

15.2 - 15.2.1

Lexical Blocking

139

15.2.2. MOBLIST

("make oblist") creates and returns a new OBLIST. containing no ATOMs. whose name is atom. unless
there already exists an OBLIST of that name. in which case it returns the existing OBLIST. fix is the
size of the OBLIST created - the number of hashing buckets. fix is optional (ignored if the OBLIST
already exists). with default 13. If specified, fix should be a prime number, since that allows the
hashing to work better.

15.2.S. OB LIS T?


returns #FALSE () if IItom is not in any OBLIST. If atom is in an OBLIST. it returns that OBLIST.

15.3. READ and OBLISTs

15.3.1. Trailers

READ can be explicitly told to look up an ATOM in a particular OBLIST by giving the ATOM a trailer.
A trailer consists of the characters ! - (exclamation-point dash) following the ATOM. immediately
followed by the name of the OBLIST. For example.
A!-OB
specifies the unique ATOM of PNAME A which is in the OBLIST whose name is the ATOM OB.
Note that the name of the OBLIST must follow the ! - with ill!. separators (like space. tab. carriagereturn. etc.). There is a default name (section 15.5) which types and is typed as ! -separator.
Trailers can be used recursively:
B! -A! -OB
specifies the unique ATOM of PNAME B which is in the OBLIST whose name is the unique ATOM of
PNAME A which is in the OBLIST whose name is OB. (Whewl) The recursion il terminated via the
defaults described below.
If an ATOM with a given PNAME is not found in the OBLIST specified by a trailer. a new ATOM with

that PNAME is created and inserted into that OBLIST.

15.2.2 • 15.8.1

Lexical Blocking

140

If an OBlIST whose name is given in a trailer does not exist, READ creates one, of length 13 buckets.

15.3.2. READ and Defaults

If trailer notation is not used (the "normal" case), and for an ATOM that terminates a trailer. READ
looks up the PNAHE of the ATOM in a LIST of OBlISTs, with default the lVAl of the ATOM OBLIST.
This lookup starts with (I .OBLIST> and continues until .OBlIST is exhausted. If the ATOM is not
found. READ usually inserts it into < 1 .OBlIST>. (It is possible to force READ to use a different
element of the lIST of OBlISTs for new insertions. If the ATOM DEFAULT is in that lIST. the OBLIST
following that ATOM will be used.)

15.4. PRINT and OBlISTs
\Vhen PRINT is given an ATOM to output. it outputs as little of the trailer as is necessary to specify
the ATOM uniquely to READ. That is, if the ATOM is the first ATOM of that PNAME which READ would
find in its normal lookup in the current .OBlIST, no trailer is output. Otherwise, 1- is output and
the name of the OBlIST is recursively PRINled.
Warning: there are obscure cases, which do not occur in normal practice, for which the PRINT trailer
recursion does not terminate. For instance, jf an ATOM must have a trailer printed, and the name of
the OBLIST is an ATOM in that very same OBLIST, death. Any similar circular case will also give
PRINT a hernia.

15.5. Initial State
In an initial MDL, .OBlIST contains two OBLISTs. < 1 .OBlIST> initially contains no ATOMs. and <2
.OBLIST) contains all the ATOMs whose GVAl.J are SUBRs or FSUBIU. as well as OBLIST, DEFAULT, T,
etc. It is difficult to lose track of the latter; the specific trailer ! -separator will always cause
reference to that OBLIST. In addition, the SUBR ROOT. which takes no arguments. always returns
that OBLIST.
The name of (ROOT) is ROOT; this ATOM is in  and would cause infinite PRINT recursion were
it not for the use of ! -separator. The name of the initial <1 .OBLIST) is INITIAL (really
INITIAL! - ).
The ATOM OBLIST also has a GVAL ,OBlIST is initially the same a5 .OBLIST; however. ,OBlIST is
not affected by the SUBRI used to manipulate the OBlIST structure. It i. instead used only when
errors occur.

15.3.1 • 15.5

Lexical Blocking

141

In the case of an ERROR, the current .OBLIST is checked to see if it is "reasonable" -- that is. contains
nothing of the wrong TYPE. (It is reasonable. but not standard. for .OBLIST to be a single OBUST
instead of a LIST of them.) If it is reasonable. that value stays current. Otherwise. OBLIST is SET to
,OBLIST. Note that changes made to the OBLISTs on ,OaLIST -- for example. new ATOMs added -remain. If even ,OBLIST is unreasonable. OBLIST is SET and SETGed to its initial value. 
(.section 16.4) always assumes that .OBLIST is unreasonable.
Three other OBLISTs exist in a virgin MOL; their names and purposes are as follows:
ERRORS! - contains ATOMs whose PNAMEs are used as error messages. It is returned by .
INTERRUPTS! - is
(INTERRUPTS>.

used

by

the

interrupt system

(section

21.2.1).

It

is

returned

by

HUDDLE 1- is used infrequently by the interpreter when loading compiled programs to fix up
references to locations within the interpreter.
The preJoading of compiled programs may create other OBLISTs in an initialized MOL (ref 4).

15.6. BLOCK and ENOBLOCK
These SUBRs are analogous to begin and end in Algol, etc., in the way they manipulate static
blocking (and in !!!!. other way).
(BLOCK list-of-oblists>
returns its argument after "pushing" the current LVAL of the ATOM OaLIST and making its argument
the current LVAL You usuany want (ROOT> to be an element of list-of-oblists. normally its last.
(ENOBLOCK>
"pops· the LVAL of the ATOM OBLIST and returns the resultant LIST of oaLISTs.
Note that this OBLIST "pushing" and "popping" is entirely independent of functional application.
binding. etc.

15.5·15.6

Lexical Blocking

142

15.7. SUBRs Associated with Lexical Blocking
15.7.1. READ (again)
(READ channsl tlof-routine lookup>
This is a fuller call to READ. lookup is an OBlIST or a LIST of them. used as stated in section 15.3 to
look up ATOMs and insert them in OBLISTa. If it is not specified•• OBlIST is used. See also sections
11.1.1.1. 11.S, and 17.1.S for other arguments.

15.7.2. PARSE and LPARSE (again)
(PARSE string rMiix:fix lookup>
as was previously mentioned. applies READ's algorithm to string and returns the first MDL object
resulting. This includes looking up prospective ATOMs on lookup. if given. or .OBlIST. lPARSE can
be calJed in the same way. See also sections 7.6.6.2 and 17.1.3 for other arguments.

15.7.S. LOOKUP
(lOOKUP string oblist>
returns the ATOM of PNAME string in the OBlIST ob/ist. if there is such an ATOM; otherwise. it returns
#FALSE (). If string would PARSE into an ATOM anyway. LOOKUP i. falter. although it looks in only
one OBLIST instead of a LIST of them.

15.7.4. ATOM
(ATOM string>
creates and returns a spanking new ATOM of PNAME string which is guaranteed not to be on !.!!.I.
OBLIST.
An ATOM which il not on any OBLIST is PRINTed with a trailer of ! -'FALSE ().

15.7.5. REMOVE
(REMOVE string oblist>

15.7 • 15.7.5

Lexical Blocldng

'143

removes the ATOM of PNAME string from oblist and returns that ATOM. If there is no such ATOM.
REMOVE returns IFAlSE (). Also.

removu atom from iu OBlIST. if it is on one. It returns .tom if it was on an OBlIST; otherwise it
returns IFALSE ().

15.7.6. INSERT

creates an ATOM of PNAME string. inserts it into oblist and returns it. If there is already an ATOM with
the same PNAME as atom in oblist. error. The standard way to avoid the error and always get your
atom is
  instead.

15.7.7. PNAME

returns a STRING (newly created) which is atom's PNAME {"printed'name'. If trailers are not needed.
PNAME is much faster than UNPARSE on atom. (In fact UNPARSE has to go all the way through the
PRINT algorithm twice. the first time to see how long a STRING is needed.)

IS.7.S. SPNAME
SPNAME ("shared printed name") is identical to PNAME. except that the STRING it returns shares storage
with atom (appendix 1). which is more efficient if the STRING will not be modified. PUTting into
such a STRING will cause an ERROR.

15.7.5 - 15.7.8

Lexical Blocking

144

15.8. Example: Another Solution to the INC Problem
What fo))ows is an example of the way OBLISTs are "norma))y" used to provide "externaJly
available" ATOMs and "local" ATOMs which are not so readily available externany. Ref 8 describes a
systematic way to accomplish the same thing and more.
(HOBlIST INCO 1>
;"Create an OBLIST to hold your external symbols.
Its name is INCOI-INITIALI- ."
INCI-INCO
:"Put your external symbols into that OBlIST.
If you have many, just write them successively.n
  
:"Create a local OBlIST, naming it INCII-INCO, and set up .OBlIST for
reading in your program. The OBlIST INCO is included in the BLOCK so
that as your external symbols are used, they will be found in the
right place. Note tha,t the ATOM INCO is not in any OBlIST of the
BLOCK: therefore, trailer notation of I-INCO will not work within the
current BLOCK-ENDBLOCK pair."
>>

(A)

This example is rather trivial, but it contains a)) the issues, of which there are three.
The first idea is that you should create two OBLISTs, one to hold ATOMs which are to be known to
other users (INCO), and the other to hold internal ATOMs which are not normaUy of interest to others
(INC I). The case above has one ATOM in each category.
Second, INCO is expJicitJy used without trailers so that surrounding BLOCKs and ENDBlOCKs wiJ) have
an effect on it. Thus INCO wi)) be in the OBLIST desired by the user; INC will be in INCO, and the
user can access it by saying INC I -INCO; INCI wiJI also be in INCO, and can be accessed in the same
way; finany, A is rea))y A! -INCI! -INCO. The point of a)) this is to structure the nesting of OBlISTs.
Finally. if for some reason (like saving storage space) you wish to throw INCI away, you can fonow
the ENDBlOCK with
.

15.8

Lexical 81oel(ing

146

Chapter 16. Errors, Frames, eto.

16.1. LISTEN
This SUBR takes any number of arguments. It first checks the LVALs of INCHAN, OUTCHAN. and
OBLIST for reasonability and console usability. In each case. if the value is unreasonable. the ATOM
is rebound to the corresponding GVAl., if reasonable, or to an invented reasonable value. LISTEN then
does $
69
5
 
("arguments' returns the argument TUPLE of frame.

16.3.2. FUNCT

("function} returns the ATOM whose G/LVAL is being applied in frame.

16.3.3. FRAME (the SUBR)

returns the FRAME stacked before frame or. if there is none. it will generate an ERROR. The lowest
FRAME that can be returned without error has a FUNCT of TOPLEVEL If called with no argumenu.
FRAME returns the topmost FRAME used in an application of ERROR or LISTEN. which was bound by
the interpreter to the ATOM LERR\ I-INTERRUPTS ("last error").

16.3.4. Examples
Say you have gotten an ERROR. You can now type at ERROR's LISTEN loop and get things EVALed.
For example.
S
the-name-of-the-Subroutine-which-called-ERROR:atom
(ARGS (FRAME (FRAME»>S
the-arguments-to-the-Subroutine-which-called-£RROR:tup/e

16.4. ERRET

This SUBR ("error return") (1) causes the control stack to be stripped down to the level of frame. and
(2) then returns any. The net result is that the application which generated frame is forced to return

IS.S.1 - IS.4

Errors. Frames, etc.

149

any. Additional side effects that wouJd have happened in the absence of an error may not have
happened.
The second argument to ERRET is optionaJ, with defauJt the FRAME of the last invocation of ERROR or
LISTEN.
If ERRET is called with ~ arguments, it drops you all the way down to the bottom of the control
stack - before the leveJ.1 LISTEN loop •• and then caUs LISTEN. As always, LISTEN first ensures that
MDL is receptive.
Examples:
<* 3 <+ a 1»S
*ERROR·
ARG-WRONG-TYPE
+
LISTENING-AT-LEVEL 2 PROCESS 1
S
f
>

16.7. Control-G (,'G)
Typing control-G ("G, (ASCII 7» at MDL causes it to act just as if an error had occurred in
whatever was currently being done. You can then examine the values of variables as above.
continue by applying ERRET to one argument (which is ignored), RETRY a FRAME lower on the control
stack, or flush everything by applying ERRET to no arguments.

16.5 - 16.7

Errors. Frames. etc.

151

16.8. Control-S (.....S)

Typing control-S (AS, 

There is one error that can be disabled: numeric overflow and underflow caused by the arithmetic
SUBRs (+ f - f l i t , I). The SUBR OVERFLOW takes one argument: if it is of TYPE FALSE. under/overflow
errors are disabled; otherwise they are enabled. The initial state is enabled. OVERFLOW returns T or
#FALSE (). reflecting the previous state. Calling it with no argument returns the current state.

16.8 - 16.9

Errors, Frames, etc.

152

Chapter 17. Maoro-operatlolUl

17.1. READ Macros

17.1.1. X and XX
The tokens X and XX are interpreted by READ in such a way a6 to give a ·macro· capability to MDL
similar to PL/I's.
Whenever READ encounters a single X - anywhere. at any depth of recursion -- it immediately.
without looking at the rat of the input. evaluates the object following the %. The result of that
evaluation is used by READ in place of the object following the X. That is. X means "don't really
READ this. use EVAL of it instead." X is often used in files in front of caUs to ASCII, BITS (which
see). etc.. although when the FUNCTION is compiled the compiler will do the evaluation if the
arguments are constant. Also seen is X. INCHAN. read as the CHANNEL in use during LOAD or FLOAD;
for example. (PUT X.INCHAN 18 8> causes succeeding FIXes to be read as octaL
Whenever READ encounters Xx. it likewise immediately evaluatu the object following the Xx.
However, it completely ignore. the result of that evaluation. Side effects of that evaluation remain,
of course.
Example:

(DEFINE SETUP () $
NXT
[XX X X (XX]S
[1 Z () 1]

17 - 17.1.1

Macro-operations

153

17.1.2. LINK
(LINK exp:any string oblist>
creates an object of TYPE LINK, PRIMTYPE ATOM. A LINK looks vaguely like an ATOH; it has a PNAME
(the string argument}. resides in an OBLIST (the oblist argument) and has a ·value· (the exp
argument). A LINK has the strange property that, whenever it is encountered by READ (that is. its
PNAHE is read. just like an ATOM, possibly with OBLIST trailers), READ substitutes the LINK's "value"
for the LINK immediately. The effect of READing a LINK's PNAME is exactly the same as the effect of
reading its ·value".
The ob/ist argument is optional, with default <1 .OBLISD. LINK returns its first argument. The
LINK is created via INSERT. so an error results if there is already an ATOH or LINK in ob/ist with the
same PNAHE.
The primary use of LINKs is in interactive work with MDL; expressions which are commonly used,
but annoyingly long to type. can be "linked" to PNAMEs which are shorter. The canonical example is
the following:
(LINK '(ERRET> MAE" (ROOT»
which Jinks the ATOM of PNAME "E in the ROOT OBLIST to the expression .

17.13. Program-defined Macro-characters
During READing from an input CHANNEL or PARSEing a STRING, any character can be made to have a
special meaning. A character can cause an arbitrary routine to be invoked. which can then return
any number of elements to be put into the object being built by READ, PARSE, or LPARSE.
Translation of characters is also possible. This facility was designed for those persons who want to
use the MDL reader to do large parts of their input but have to modify its actions for some areas;
for example. one might want to treat left and right parentheses as tokens, rather than as delimiters
indicating a LIST.
17.1.S.1. READ (finally)
Associated with READ is an ATOM, READ-TABLE! -, whose local value, if any, must be a VECTOR of
elements. one for each character up to and including all characters to be treated specially. Each
element indicates, if not 0, the action to be taken upon READ's encounter with that character. A
similar VECTOR, the local value of PARSE-TABLE! -. if any. is used to find the action to take for
characters encountered when PARSE or LPARSE is applied to a STRING.
These tables can have up to 256 elements, one for each ASCII character and one for each possible
exclamation-point/ASCII-character pair. In MDL, the exclamation-point is used as a method of

17.1.2 • 17.1.3.1

Macro-operations

154

expanding the ASCII character set. and an exclamation-point/character pair is treated as one logical
character when not reading a STRING.
The element corresponding to a character is (NTH t6ble (+ 1 (ASCII char»>. The element
corresponding to an exclamation-point/ASCII.character pair is (NTH t.ble (+ 129 (ASCII char»).
The table can be shorter than 256 elements. in which case it is treated as if it were 256 long with aU
the elements above its actual length O.
An element of the tables must satisfy one of the following DECL Patterns:
• 0 indicates that no special action is to be taken when this character is encountered.
CHARACTER indicates that the encountered character is to be translated into the given CHARACTER
whenever it appears, except when as an object of TYPE CHARACTER, or in a STRING. or
immediately following a \.
FIX indicates that the character is to be given the same treatment as the character with the
ASCII value of the FIX, This allows you to cause other characters to be treated in the nme
way as A-Z for example. The same exceptions apply as described above.
 indicates the same thing. except that the character does not by itself cause a break.
Therefore. if it occurs when reading an ATOM or number, it will be treated as part of that ATOM
or number.
APPLICABLE (to one argument) indicates that the character is to be a break character. Whenever
it is encountered. the reading of the current object is finished. and the corresponding element
of the table is APPLYed to the ASCII CHARACTER. (If READ is called during the application. the

end-of·file slot of the CHANNEL temporarily contains a special kind of ACTIVATION (TYPE
READA) so that end-of-file can be signalled properly to the original READ. Isn't that
wonderful?) The value returned is taken to be what was read, unless an object of TYPE SPLICE
is returned. If so, the elements of this object, which is of PRIMTYPE LIST, are spliced in at the
point where MDL is reading. An empty SPLICE anows one to return nothing. If a structured
object is not being built, and a SPLICE is returned, elements after the first will be ignored. A
SPLICE during reading is similar to a SEGMENT during evaluating, except that, in some sense, a
SPLICE says "expand me", whereas the structure containing a SEGMENT silYs "I will expand you".
 indicates the same thing, except that the character does not by itself cause
a break. Therefore. if it occurs when reading an ATOM or number, it will be treated as part of
that ATOM or number.
READ takes an additional optional argument, which is what to use instead of the local value of the
ATOM READ-TABLE as the VECTOR of read-macro characters. If this argument is supplied. READ-TABLE
is rebound to it within the call to READ. READ takes from zero to four arguments. The fullest call to
READ is thus:
'

17.1.3.1

Macro-opera tions

155



The other arguments are explained in sections 11.1.1.1. 11.3. and 15.7.1.

ERROR and LISTEN rebind READ-TABLE to the GVAL of READ-TABLE. if any. else UNASSIGN it.
17.1.3.2. Examples
Examples of each of the different kinds of entries in macro tables:


; "CHARACTER: translate a to A."S
(

... ]

abc$
Abc

;U(LIST FIX>: make comma no longer a break
character, but still special if at a break.uS
(

... ]

A,BS
A\.B
: "That was an ATOM with PNAME A, B

II

I.B$

,B
; "That was the FORM 

II


;II(LIST APPLICABLE>: l1ke above. but not a break
now.IIS
[

... ]

B:AS
B:A
;IIThat was an ATOM."
: : :FOOS
(COLON (COLON (COLON FOO»)
17.1.S.3. PARSE and LPARSE (finally)

(PARSE string radix lookup parss-tabls:vsctor look-ahead:character>
is the fullest call to PARSE. PARSE can take from zero to five arguments. If PARSE is given no
arguments. it returns the first object parsed from the local value of the STRING PARSE-STRING and
additionally SETs PARSE-STRING to the STRING having those CHARACTERs which were parsed RESTed
off. If PARSE is given a STRING to parse. the ATOM PARSE-STRING is rebound to the STRING within
that call. If the parse-tabls argument is given to PARSE. PARSE-TABLE is rebound to it within that
call to PARSE. Finally. PARSE can take a look-ahead CHARACTER. which is treated as if it were
logically concatenated to the front of the string being parsed. Other arguments are described in
sections 7.6.6.2 and 15.7.2.
LPARSE is exactly like PARSE, except that it tries to parse the whole STRING, returning a LIST of the
Objects created.

17.2. EVAL Macros

An EVAL macro provides the convenience of a FUNCTION without the overhead of calling. SPECIAls.
etc. in the compiled version. A special·purpose function that is caUed often by FUNCTIONs that will
be compiled is a good candidate for an EVAL macro.

17.2.1. DEFMAC and EXPAND

DEFMAC ("define macro") is syntacticaJ1y exactly the same as DEFINE. However. instead of creating a
FUNCTION, DEFMAC creates a MACRO. A MACRO is of PRIMTYPE LIST and in fact has a FUNCTION (or
other APPLICABLE TYPE) as its single element.
A MACRO can itself be applied to arguments. A MACRO is applied in a funny way. however: it is

. 17.1.3.2 • 17.2.1

Macro-operation.5

157
EVALed twice. The first EVAL causes the MACRO's element to be applied to the MACRO's arguments.
Whatever that application returns (usuaUy another FORM) is also EVALed. The result of the second
EVALUation is the result of applying the MACRO. EXPAND is used to perform the first EVAL without
the second.
To avoid complications. the first EVAL (by EXPAND, to create the object to be EVALed the second time
around) is done at top level. The result of this policy is that two syntactically identical invocations
of a MACRO always return the same expansion to be EVA Led in the 5eCond step. The first EVAL
generates two extra FRAMEs: one for a caJl to EXPAND, and one for a caU to EVAL the MACRO
application in a top-level environment.
Example:
(OEFMAC INC (ATM "OPTIONAL" (N 1»
IDECL «VALUE) FORM (ATM) ATOM (N) (OR FIX FLOAT»
S 1 (INC X>S 2 .XS 2 (EXPAND '(INC X»S (SET X <+ .X I» Perhaps the intention is clearer if PARSE and X are used: (OEFMAC INC (ATM "OPTIONAL" (N 1» IDECL ( ••• ) (PARSE "(SET X.ATM (+ X.ATM X.N»"» MACROs rea))y exhibit their advantages when they are compiled. The compiler wi)) simply cause the first EVALuation to occur (via EXPAND) and compile the result. The single element of a compiled MACRO is an RSUBR or RSUBR-ENTRY. 17.2.2. Example Suppose you want to change the following simple FUNCTION to a MACRO: If this FUNCTION is applied, the top-level binding of Y is used, not the binding just created by the application. Compilation of this FUNCTION would probably fail, because the compiler probably would have no top-level binding for Y. Well. how about -> -> -> <+ 2 Z> -> 4 But, when DOUBLE is applied to that FORM, the argument is QUOTEd, so: -> -> -> <+ Z 3> -> 5 So. since the evaluation of DOUBLE's argument has a side effect. you should ensure that the evaluation is done exactly once, say by FORM: (DEFMAC DOUBLE (IANY) As a bonus, the DECL can once more be used. This example Is intended to show that writing good MACROs is a little trickier than writing good FUNCTIONs. But the effort may be worthwhile if the compiled program must be speedy. 17.2.2 Macro-operations 159 Chapter 18. Maohine Words and Bits The MDL facility for dealing with uninterpreted machine words and bits involves two data TYPEs: WORD and BITS. A WORD is simply an uninterpreted machine word, while a BITS is a "pointer" to a set of bits within a WORD. Operating on WORDs is usually done only when compiled programs are used (chapter 19). 18.1. WORDs A WORD in MDL is a PDp·I0 machine word of a6 bits. A WORD always PRINTs in "I format", and its contents are always printed in octal (hence preceded and followed by It). Examples: IWORD 0 IWORD ·000000000000· #WORD ·ZOOO#WORD ·OOOOOOOOZOOO- ; "one bit 1"$ IWORD ·525252525252'WORD -SZSZSZSZ5Z5Z It ;"every other bit IN$ WORD is its own PRIMTYPE; it is also the PRIMTYPE of FIX, FLOAT, CHARACTER, and any other TYPE which can fit its data into one machine word. A WORD cannot be an argument to +. -, or indeed any SUBRs except for CHTYPE, GETBITS, PUTBITS and several bitwise logical functions, all to be described below. Thus any arithmetic bit manipulation must be done by CHTYPEing a WORD to FIX, doing the arithmetic, and then CHTYPEing back to WORD. However. bit manipulation can be done without CHTYPEing the thing to be played with to a WORD. so long as it is of PRIMTYPE WORD; the result of the manipulation will be of the same TYPE as the original object or can be CHTYPEd to it. 18·18.1 Machine Words and Bits 160 18.2. BITS An object of TYPE BITS is of PRIMTYPE WORD. and PRINTs just like a WORD. The internal form of a BITS is precisely that of a PDp·IO "byte pointer". which is. in fact. just what a BITS is. For purposes of explaining what a BITS is. assume that the bits in a WORD are numbered from right to Jeft. with the rightmost bit numbered 0 and the leftmost numbered ~5. as in 35 34 33 ••. 2 1 0 (This is not the ·standard" ordering; the "standard" one goes from left to right.) A BITS is most conveniently created via the SUBR BITS: (BITS width:fiK right-edge:fiK> returns a BITS which "points to" a set of bits width wide. with rightmost bit right-edge. arguments must be of TYPE FIX, and the second is optional with default o. Both Examples: the indicated application of BITS returns an object of TYPE BITS which points to the indicated set of bits in a WORD: (BITS 7) 35 ••. 7 6 •.• 0 (BITS 4 18> 35 •.• 22 21 20 19 18 17 ••• 0 (BITS 36) 35 ... 0 18.3. GETBITS (GETBITS from:primtype-word bits> where from is an object of PRIM TYPE WORD. returns a new object whose TYPE i. WORD. This object is constructed in the following way: the set of bits in from pointed to by bit, i. copied into the new object. right-adjusted. that is. lined up against the right edge (bit number 0) of the new object. All those bits' of the new object which are not copied are set to zero. In other words. GETBITS takes bits from an arbitrary place in from and puts them at the right of a new object. The from argument to GETBITS is not affected. Examples: 18.2 - 18.3 Machine Words and Bits 161 (GETBITS IWORD *777777777777* (BITS 3»S IWORD *000000000007* (GETBITS *012345670123* (BITS 6 18»S 'WORD *000000000045* 18.4. PUTBITS (PUTBITS to:primtYP8-word bits from:primtYP8-word> where to and from are of PRIMTYPE WORD, returns a copy of to, modified as follows: the set of bits in to which are pointed to by bits are replaced by the appropriate number of rightmost bits copied from from (optional, default 0). In other words: PUTBITS takes bits from the right of from and stuffs them into an arbitrary position in to. None of the arguments to PUTBITS is affected. Examples: (PUTBITS IWORD *777777777777* (BITS 6 3»S 'WORD *777777777007* (PUTBITS IWORD *666777000111* (BITS 5 15> IWORD *123*>5 IWORD *666776300111* (PUTBITS IWORD *765432107654* (BITS 18»S 'WORD *765432000000* 18.5. Bitwise Boolean Operations Each of the SUBRs ANDB, ORB, XORB, and EQVB takes arguments of PRIMTYPE WORD and returns a WORD which is the bitwise Boolean "and", inclusive "or", exclusive "or", or "equivalence" (inverse of exclusive "or"), respectively. of its arguments. Each takes any number of arguments. If no argument is given. a WORD with all bits off (ORB and XORB) or on (ANDB and EQVB) is returned. If only one argument is given, it is returned unchanged but CHTYPEd to a WORD. If more than two arguments are given. the operator is applied to the first two, then applied to that result and the third. etc. Be careful not to confuse AND and OR with ANDB and ORB. 18.3 -18.5 Machine Words and Bits 162 Chapter 19. Oompiled Programs 19.1. RSUBR (the TYPE) RSUBRs rrelocatable subroutinesj are machine.language programs written to run in the MOL environment. They are usually produced by the MDL assembler (often from output produced by the compiler) although this is not necessary. AU RSUBRs have two components: the "reference vector" and the "code vector". In some cases the code vector is in pure storage. There is also a set of "fix ups" associated with every RSUBR, although it may not be available in the running MDt. 19.2. The Reference Vector An RSUBR is basicaUy a VECTOR that has been CHTYPEd to TYPE RSUBR via the SUBR RSUBR (see below~ This ex·VECTOR is the reference vector. The first three elements of the reference vector have predefined meanings: The first element is of TYPE CODE or PCODE and is the impure or pure code vector respectively. The second element is an ATOM and specifies the name of the RSUBR. The third element is of TYPE DECL and declares the type/structure of the RSUBR's arguments and result. The rest of the elements of the reference vector are objects in garbage·collected storage that the RSUBR needs to reference and any impure slots that the RSUBR needs to use. When the RSUBR is running. one of the PDP·I0 accumulators (with symbolic name R) is always pointing to the reference vector. to permit rapid access to the various elements. 19· 19.2 Compiled Program5 163 19.3. RSUBR Linking RSUBRs can call any APPLICABLE object. all in a uniform manner. In general. a call to an F/SUBR is linked up at assembly/compile time so that the calling instruction (UUO) points directly at the code in the interpreter for the F/SUBR. However. the locations of most other APPLICABLEs are not known at assembly/compile time. Therefore. the calling UUO is set up to point at a slot in the reference vector (by indexing off accumulator R). This slot initially contains the ATOM whose C/LVAL is the called object. The calling mechanism (UUO handler) causes control to be transferred to the called object and. depending on the state of the RSUBR-link flag. the ATOH will be replaced by its C/LVAL. (If the call is of the "quick" variety. the called RSU~R or RSUBR-ENTRY will be CHTYPEd to a QUICK-RSUBR or QUICK-ENTRY. respectively. before replacement.) Regardless of the RSUBR-link flag's state. calls to FUNCTIONs are never permanently linked. A call to a non-Subroutine generates an extra FRAME. whose FUNCT is the dummy ATOM CALLER. RSUBRs are linked together for faster execution. but linking may not be desirable if the RSUBRs are being debugged. and various versions are being reloaded. A linked call will forever after go to the same code, regardless of the current G/LVAL of the called ATOM. Thus. wh lie testing RSUBRs. you may want to disable linking. by calling the RSUBR-LINK SUBR with a FALSE argument. Calling it with a non-FALSE argument enables linking thereafter. It returns the previous state of the link flag. either T or 'FALSE (). Calling it with no argument returns the current state. 19.4. Pure and Impure Code The first element of an RSUBR is the code vector. of TYPE CODE or PCODE. TYPE CODE is of PRIHTYPE UVECTOR. and the UTYPE should be of PRIMTYPE WORD. The code vector is simply a block of words that are the instructions which comprise the RSUBR. Since the code vector is stored just like a standard UVECTOR, it will be moved around by the garbage collector. Therefore. all RSUBR code is required to be location-insensitive. The compiler guarantees the location-insensitivity of its output. The assembler helps to make the code location-insensitive by defining all labels as offsets relative to the beginning of the code vector and causing instructions that refer to labels to index automatically off the PDP-I0 accumulator symbolically named M. M. like R, is set up by the UUO handler. but it points to the code vector instead of the reference vector. The code vector of an RSUBR can be frozen (using the FREEZE SUBR) to prevent it from moving during debugging by DDT in the superior job or fork. If the first element of an RSUBR is of TYPE PCODE ("pure code"). the code vector of the RSUBR is pure and sharable. TYPE PCODE is of PRIMTYPE WORD. The left half of the word specifies an offset into an internal table of pure RSUBR.s. and the right half specifies an offset into the block of code where this RSUBR starts. The PCODE prints out as: %< PCODE nams:string offsst:fix> 19.3 - 19.4 Compiled Programs Ui4 where name names the entry in the user's pure-RSUBR table, and offset is the offset. (Obviously. PCOOE is also the name of a SUBR, which generates a pure code vector.) Pure RSUBRs may also move around. but ollly by being included in MDL's page map at different places. Once again M can be used exactly as before to do location-independent address referencing. Individual pure code vectors can be ·unmapped" (marked as being not in primary storage but in their original pure-code disk files) if the space in storage allocated for pure code is exhausted. An unmapped RSUBR is mapped in again whenever needed. All pure RSUBRs are unmapped before a SAVE file is written. so that the code is not duplicated on disk. A purified RSUBR must use RGLOC \relative GLOC1 instead of GLOC. RGLOC produces objects of TYPE LOCR instead of LOCO. 19.5. TYPE -C and TYPE -W In order to handle user NEWTYPEs reasonably, the internal TYPE codes for them have to be able to be different from one MDL run to another. Therefore, references to the TYPE codes must be in the reference vector rather than the code vector. To help handle this problem, two TYPEs exist, TYPE-C rtype code' and TYPE-W ("type word"), both of PRIMTYPE WORD. They print as: " " The SUBR TYPE-C produces an internal TYPE code for the type, and TYPE-W produces a prototype -TYPE word" (appendix 1) for an object of that TYPE. The primtype argument is optional. included only as a check against the call to NEWTYPE. TYPE-W can also take a third argument. of PRIMTYPE \lORD, whose right half is included in the generated "TYPE word". If type is not a valid TYPE. a NE\lTYPE is automatically done. To be complete, a similar SUBR and TYPE should be mentioned here. produces an internal "storage allocation code" (appendix 1) for the type. The value is of TYPE PRIHTYPE-C t PRIMTYPE WORD. In almost all cases the SUBR TYPE PRIM gives just as much information, except in the case of TEMPLATEs: all TYPEs of TEMPLATes have the same TYPEPRIM, but they all have different PRIMTYPE-Cs. 19.6. RSUBR (the SUBR) CHTYPEs its argument to an RSUBR, after checking it for legality. RSUBR is rarely called other than 19.4 - 19.6 Compiled Programs 165 in the MDL Assembler (ref 2). It can be used if changes must be made to an RSUBR that are prohibited by MDL's built·in safety mechanisms. For example, if the GVAL of name is an RSUBR: (SET FIXIT and the following happens: (1) In • PO the arguments of the RESUME are evaluated: that is, we get that LVAL of A which is current in ,PO and the GVAlof Pl. (2) The STATE of • PO is changed to RESUMABLE and • PO is "frozen" right where it is. in the middle of the RESUME. (S) The STATE of ,PI is changed to RUNNING. and ,STARTER is applied to • PO's LVAL of A in ,PI now continues on its way. evaluating the body of ,STARTER. ~. The .A in the RESUME could have been anything. of course. The important point is that. whatever it is, it is evaluated in ,PO. What happens next depends, of course, on what ,STARTER does. 20.5.2. Top-level Return Let us initially assume that • STARTER does nothing relating to PROCESSes. but instead simply returns a value •• say starval. What happens when ,STARTER returns is this: (1) The STATE of ,Pl is Changed to DEAD. ,PI can never again be RESUMEd. (2) The last PROCESS to RESUME ,PI is found, namely, PO, and its STATE is changed to RUNNING. 20.4 • 20.5.2 Multiprocessing 171 (3) starval is returned in , PO as the value of the original RESUME, and , PO continues where it left off. AU in aU, this simple case looks just like an elaborate version of applying ,STARTER to .A in , PO. 20.5.3. Symmetric RESUMEing Now suppose that while still in , PI the following is evaluated, either in , STARTER or in something caUed by ,STARTER: S -GOT 1(RESUME 1 .SUMUP>$ "GOT 2" (RESUME 2 .SUMUP>S 8 Just as a note, by taking advantage of MDL's order of evaluation. SUM3 could have been written as: (DEFINE SUM3 (A) (REPEAT «S .A» IDECL «A S) (OR FIX FLOAT» (SET S (RESUME (+ .S (RESUME "GOT I"> (RESUME "GOT 2"»»» 20.7. Other Multiprocessing: Features 20.7.L BREAK-SEQ (BREAK-SEQ sny process> ("break evaluation sequence") returns process, which must be RESUMABLE, after having modified it so that when it is next RESUMEd, it will first evaluate any and then do an absolutely normal RESUME; the value returned by any is thrown away, and the value given by the RESUME is used normally. If a PROCESS is BREAK-SEQed more than once between RESUMEs, al1 of the anys BREAK-SEQed onto it will be remembered and evaluated when the RESUME is finaJly done. The anys will be evaluated in "last-in first-out" order. The FRAME generated by EVAUng more than one any wiJI have as its FUNCT the dummy ATOM BREAKER. 20.7.2. MAIN When you initially start up MDL, the PROCESS in which you are running is slightly "special" in these two ways: (1) Any attempt to cause it to become DEAD will be met with an ERROR. 20.6 - 20.7.2 Multiprocessing 173 (2)
always returns that PROCESS. The PROCESS number of
is always 1. The initial GVAL of THIS-PROCESS is what MAIN always returns, IPROCESS 1. 20.7.3. HE . Note that
does not ever have any resumer. Example: IDECl «R) RESUMABLE> 20.7.5. SUICIDE acts just like RESUME. but clobbers the PROCESS (which cannot be returns process, after putting it into "single.step mode". A PROCESS in single.step mode, whenever RESUMEd, runs only until an application of EVAL in it 20.7.2·20.7.6 Multiprocessing 174 begins or finishes. At that point in time, the PROCESS that did the ISTEP is RESUMEd, with a ret val which is a TUPLE. If an application of EVAL just began. the TUPLE contains the ATOM EVLIN and the arguments to EVAL If an application of EVAL just finished. the TUPLE contains the ATOM EVLOUT and the result of the evaluation. process will remain in single-step mode until FREE-RUN (below) is applied to it. Until then. it will stop before and after each EVAL in it. Exception: if it is RESUMEd from an EVLIN break with a refval of TYPE DISMISS (PRIMTYPE ATOM). it will leave single-step mode only until the current call to EVAL is about to return. Thus lower-level EVALs are skipped over without leaving the mode. The usefulness of this mode in debugging is obvious. 20.7.7. FREE-RUN (FREE-RUN process) takes its argument out of single-step mode. Only the PROCESS that put process into single-step mode can take it out of the mode; if another PROCESS tries. FREE-RUN returns a FALSE. 20.8. Sneakiness with PROCESSes FRAMEs, ENVIRONMENTs, TAGs. and ACTIVATIONs are specific to the PROCESS which created them, and each "knows its own father", Any SUBR which takes these objects as arguments can take one which was generated by !..!!.!. PROCESS. no matter where the SUBR is really applied. This provides a rather sneaky means of crossing between PROCESSes, The various cases are as follows: GO. RETURN. AGAIN. and ERRET. given arguments which lie in another PROCESS, each effectively "restarts" the PROCESS of its argument and acts as if it were evaluated over there. If the PROCESS in which it was executed is later RESUMEd, it returns a value just like RESUME! SET. UNASSIGN. BOUND? ASSIGNED?, LVAL, VALUE, and LLOC, given optional ENVIROW'jENT arguments which lie in another PROCESS, will gleefully change, or return. the local values of ATOMs in the other PROCESS. The optional argument can equally well be a PROCESS. FRAME. or ACTIVATION in another PROCESS; in those cases. each uses the ENVIRONMENT which is current in the place specified. FRAME. ARGS. and FUNCT will be glad to return the FRAMEs. argument TUPLEs. and applied Subroutine names of another PROCESS. If one is given a PROCESS (including .) Certain names must be further qualified by a CHANNEL or a LOCATIVE to tell which interrupt by that name is meant. 21 - 21.1 Interrupts 177 Each interrupt that is on has a priority, a FIX greater than 0 which specifies itl "importance". The processing of a higher-priority (larger-numbered) interrupt will supercede the processing of a lowerpriority interrupt. 21.2. IHEADER and HANDLER (the TYPEs) One IHEADER ('interrupt header") and a chain of HANDLERs are associated with each interrupt that is on. Both TYPEs are of PRIM TYPE VECTOR, but they do not print as such, since they are circular. Instead they PRINT as 'type most-interesting-component The contents of IHEADERs and HANDLERs can be changed by PUT, and the new values will then determine the behavior of MDL. 21.2.1. IHEADERs There is one IHEADER associated with each interrupt that is on. EVENT and ON (see below) will automatically create one if none exists, and OFF (see below) will destroy one when it has no reason to continue existing. The elements of an IHEADER are as follows: (J) name of interrupt (STRING. or CHANNEL if the name is "CHAR". or LOCATIVE if the name is IIREAD" or "WRITP) (2) nonzero if and only if disabled (3) first HANDLER, if any. else a zero-length HANDLER (4) priority Obtaining an IHEADER is a little complicated. For "READ" interrupts. returns the IHEADER. For "WRITP interrupts. returns the IHEADER. For "CHAR" interrupts. returns the IHEADER. Otherwise. the IHEADER is PUT on the name ATOM with the indicator INTERRUPT! -. Thus. for example. (GET CLOCK I-INTERRUPTS INTERRUPTI-> returns the IHEADER for the clock interrupt (if it exists). 21.1 - 21.2.1 Interrupti 178 21.2.2. HANDLERs A new HANDLER is generated each time HANDLER or ON (see below) is applied. A HANDLER specifies a particular action for a particular interrupt. The elements of a HANDLER are as fonows: (I) next HANDLER if any, else a zero-length HANDLER (2) previous HANDLER or the IHEADER (Thus the HANDLERs of a given interrupt form a "doublylinked list- chaining between each other and back to the IHEADER.) (3) handler to be applied (anything APPLICABLE that evaluates its arguments - the application is done not by APPLY but by RUNINT, which can take a PROCESS argument: see next line) (4) PROCESS in which the handler will be applied, or IPROCESS O. meaning whatever PROCESS was running when the interrupt occurred. In the former case, RUNINT is applied to the handler and its arguments in the currently running PROCESS, which causes an APPLY in the interrupt PROCESS, which must be RESUMABLE. The running PROCESS becomes RESUMABLE, and the interrupt PROCESS becomes RUNNING, but no other PROCESS variables (for example RESUMER) are Changed. 21.3. EVENT This SUBR sets up an enabled IHEADER without adding any HANDLERs. It returns the IHEADER. The name may be an ATOM in the INTERRUPTS OBLIST or a STRING. (If it is a STRING. EVENT does a LOOKUP or INSERT in .) which must be given only for certain names. It must be a CHANNEL if and only if name is "CHAR" or CHAR! - INTERRUPTS 1-. In this case it is the input CHANNEL from the (pseudo-)console or Network socket whose received characters will cause the interrupt to occur, or the output CHANNEL to the pseudo-console or Network socket whose desired characters will cause the interrupt to occur. (See section 21.10.1 below. Pseudo-consoles are not available under TENEX.) The argument must be a LOCATIVE if and only if name is "READ" (or the ATOM) or "WRITP (or the ATOM). In this case it specifies an object to be "monitored" (section 21.10.7). EVENT can also be called with one argument, an IHEADER. If this IHEADER was previously disabled or OFFed. EVENT will reinstate and enable it. 21.2.2 - 21.3 Interrupts 179 21.4. HANDLER (the SUBR) Thi5 SUBR makes a HANDLER, attaches it to the front of iheader's HANDLER chain (first to be performed). and returns it as a value. app/icabltl may be either an APPLICABLE handler or a previously OFFect HANDLER. (In the latter case the HANDLER is put into the chain, and a new HANDLER is not created.) process is optionaL with default 'PROCESS O. 21.5. ON ON is a combination of EVENT and HANDLER: it creates or finds the IHEADER. reinstates and enables it, and adds a HANDLER to the front of the chain (first to be performed). It returns the HANDLER. The process argument is optional. It can be supplied as, and defaults to, 0, meaning 'PROCESS O. which is only sometimes required, as for EVENT. Note: priority is associated with an interrupt. ~ with a particular action. If ON specifies a previously-on interrupt, the new priority i5 effected for aU existing actions on that interrupt. For this reason the SUBR HANDLER is often preferable. (ON can leave the priority unchanged by GETing it out of the IHEAOER.) 21.S. OFF turns off an interrupt or an action and returns ispec. Exactly what happens depends on the ispec argument as follows: If ispec is a name (STRING, ATOM, or IHEADER), OFF completely turns off the interrupt; all actions associated with that interrupt are thrown away. (An error occurs if the interrupt is not ON or has no HANDLERs.) The IHEADER is thrown away if nobody points to it. Even the interpreter does not respond to occurrences of the interrupt. If ispec is a HANDLER, OFF removes it from its chain. There is no effect on any other HANDLERs associated with that interrupt. which must be specified as a CHANNEL if and only if isptlc 15 "CHAR" or CHAR I - INTERRUPTS 1- • Caution: don't without ONing it again or MDL wiJJ become deaf. 21.4 - 21.6 Interrupts 180 which must be specified as a locative if and only if ispfJC is "READ" or "WRITEII (or one of the corresponding ATOMs). 21.7. DISABLE (DISABLE iheader> returns its argument and causes interrupts associated with ihfJ,dfJr to be noticed by the interpreter but not passed on to the HANDLERs in the chain. This is inefficient, I unless the HANDLER chain must be preserved. 21.8. gNABLE (ENABLE iheader> returns its argument and causes interrupts associated with ihfJader to be passed on to the HANDLERs. 21.9. Priorities and Interrupt Levels At any given time there is a defined interrupt ~ This is a FIX which determines which interrupts can really "interrupt" - that is, cause the current processing to cease while their wants are satisfied. Normal, non-interrupt programs operate at an interrupt level of O. Processing done for interrupts is done at an interrupt level equal to the interrupt's priority. 21.9.1. Interrupt Processing Interrupts "actually" occur only at well-defined points in time: during a call to a Subroutine, or at critical places within Subroutines (for example, during each iteration of HAPF on a LIST, which may be circular). or while a PROCESS is "BLOCKED" (see below). No interrupts can occur during garbage collection. What actually happens when an enabled interrupt occurs is that the priority of the interrupt is compared with the current interrupt level. and the following is done: If the priority is greater than the current interrupt level, the current proce»ing is "frozen in its tracks" and processing of the action(s) specified for that interrupt begins. 21.6 - 21.9.1 Interrupts 181 If the priority i5 leu than or equal to the current interrupt level, the interrupt occurrence is queued •• that is, the fact that it occurred is saved away for processing when the interrupt level becomes low enough. When the processing of an interrupt's actions is completed. MDL usually (I) "acts as if" the previously-existing interrupt level is restored. and processing continues on what was left off (perhaps for no time duration); and (2) "acts as if" any queued interrupt occurrences actually occurred right then. in their original order of occurrence. The value returned by the handler of an interrupt's HANDLER is ignored, unless it is of TYPE DISMISS (PRIHTYPE ATOM). in which case none of the remaining HANDLERs in the chain will be invoked. The processing of an interrupt's actions can terminate prematurely if a handler calls the SUBR DISM ISS (see below). 21.9.2. INT-LEVEL The SUBR INT-LEVEL is used to examine and change the current interrupt level directly. simply returns the current interrupt level. changes the interrupt level to its argument and returns the previously-existing interrupt level. If INT-LEVEL lowers the interrupt level, it does not "really" return until all queued occurrences of interrupts of priority higher than the target priority have been processed. Setting the INT-LEVEL extremely high (for example. FIX») effectively disables all interrupts (but occurrences of enabled interrupts will still be queued). If LISTEN or ERROR is called when the INT-LEVEL is not zero, then the typeout will be LISTENING-AT-LEVEL I PROCESS pINT-LEVEL i 21.9.3. DISMISS DISMISS permits a handler to return an arbitrary value for an arbitrary ACTIVATION at an arbitrary interrupt level; The call is as follow&: 21.9.1 - 21.9.3 Interrupts 182 where only the V.tUB is required. If activation is omitted. return is to the place interrupted from. If int-Ievel is omitted. the INT-LEVEL prior to the current interrupt i, restored. 21.10. SpecifiC Interrupts Descriptions of the characteristics of particular "built·in" MDL interrupti follow. Each is named by its STRING name. Expect this Jist to be incomplete yesterday. 21.10.1•• CHAR II ·CHAR· is currently the most complex built·in interrupt. It occurs every time either (1) a character arrives from the (pseudo.)console or Network socket. or (2) a character is required by the pseudo.console or Network socket, or (3) a line-feed character is output or (under ITS) the operating system generates a screen·fuJl interrupt for the CHANNEL in the IHEADER. There is an IHEADER for each CHANNEL for wh ich the .. CHAR" interrupt is on. Under ITS, even though the interrupt occurs in case (1) for each console character received. the operating system holds off receipt until an "activation" character is typed. The activation characters are A@ through AG, "K, AL, AN through ........ and DEL (that is. ASCII codes 0.7.13. 14. 1636. and 177 octal). Under TENEX. this interrupt occurs immediately. The operating system can be told which characters typed on a console should cause this interrupt to occur. by calling the SUBR ACTIVATE-CHARS with a STRING argument containing those characters. If caUed with no argument. ACTIVATE-CHARS returns a STRING containing the characters that currently interrupt. Initially. only AG. AS. and AO interrupt. Pseudo-consoles are not available under TENEX. An initial MDL already has "CHAR II enabled on ,INCHAN with priority 8 and a handler named QUITTER to run in process 0; this is how AG and ....S are processed. In addition. every time a new CHANNEL is opened in "READII mode to a console, a similar IHEADER and HANDLER are ONed for that new CHANNEL automatically. These automatically-generated interrupt handlers use the standard IHEADER and HANDLER machinery, and can be DISABLEd or OFFed at will. However. the IHEADER for • INCHAN must not be OFFed: MOL knows that $ is typed only by an interrupt (!). The handler supplied for an input "CHARII interrupt on a real console must take ~ arguments: (1) the CHARACTER which was typed. and (2) the CHANNEL on whIch it was typed. The handler supplied for an output "CHAR" interrupt on a real console must take one or two arguments (using 21.9.3 - 21.10.1 Interrupts 183 "OPTIONAL" or "TUPLE")I if two arguments are supplied by the interrupt system. they are the line number (FIX) and the CHANNEL, respectively. and the interrupt is for a line-feed; if only one argument is supplied (only under ITS). it is the CHANNEL, and the interrupt is for a full console screen. Example: the following causes the given message to be printed out whenever a Ay is typed on .INCHAN: S IHANDLER IFUNCTION«CHAR CHAN) ••. ) UVWXAY some of my best friends are AYs.ZABS UVWXAYZAB Note that occurrences of "CHAR" do !!2l wait for the S to be typed. Under ITS, a "CHAR" interrupt can also be associated with a CHANNEL open to a pseudo.consoJe ("STY" device and friends) for either input or output. An input CHANNEL will cause an interrupt when a character is available. and an output CHANNEL will cause an interrupt when the program at the other end needs a character (and the operating-system buffer is empty). These interrupts are enabled in exactly the same way as real-console interrupts. except that the handler gets applied to only one argument. the CHANNEL A ·CHAR- interrupt can also be associated with a CHANNEL open to a Network socket ("NEP device) for either input or output. The handler gets applied to a NETSTATE array (which see) and the CHANNEL 21.10.2. - GC" -GC· occurs just after every garbage collection. Enabling this interrupt is the only way to tell from a program that a garbage collection has occurred. A handler for "GC" takes three arguments. The first is the time for garbage collection. This is a FLOAT indicating the number of seconds it took. The second argument is a FIX indicating the cause of the garbage collection. as follows (chapter 22): O. Program called GC. 1. Movable storage was exhausted. 2. Control stack overflowed. 3. Top-level LVAls overflowed. 4. Global vector overflowed. 5. TYPE vector overflowed. 6. Immovable garbage-collected storage was exhausted. 21.10.1 - 21.10.2 Interrupts 184 7. Internal stack overflowed. 8. Both control and internal stacks overflowed (rare). 9. Pure storage was exhausted. 10. Second, exhaustive garbage coUection occurred. The third argument is an ATOM indicating what initiated the garbage collection: BLOAT. GROW. LIST, VECTOR, SET. SETG. FREEZE. GC. NEWTYPE. PURE-PAGE-LOADER (pure storage was exhausted), or INTERRUPT-HANDLER (stack overflow, unfortunately). 2LlaL -DIVERT-AGC"DIVERT -AGC· ("Automatic Garbage CoJ)ection") occurs just before a deferrable garbage collection because of exhausted movable garbage-coJ)ected storage. Enabling this interrupt is the only way to tell from a MDL program that a garbage colJection is about to occur. A handler takes two arguments: a FIX telling the number of words needed and an ATOM telling what initiated the garbage collection (above). If it wishes, a handler can avoid a garbage colJection by calling BLOAT. If the pending request for garbage-collected storage cannot then be satisfied, a garbage collection occurs anyway. AGC-FLAG is SET to T while the handler is running, so that new storage requests do not try to cause a garbage collection. 21.10.4. • CLOCK· ·CLOCK". when enabled, occurs every half second (the ITS slow clock tick). It i. not available under TENEX. It wants handlers which take no arguments. Example: 21.10.5. UBLOCKED" "BLOCKED· occurs whenever any PROCESS (not only the PROCESS which may be in a HANDLER) !!.!!:!.! waiting for console input; that is, it indicates that somewhere, somebody did a READ. READCHR, NEXTCHR. TYI, etc. to a console. The handler for a "BLOCKED" interrupt should take one argument, namely the PROCESS which started waiting (which will also be the PROCESS in which the handler runs, if none is in the HANDLER). Example: the following will cause MDL to acquire a JII prompt character. (PRINC " from "') . If no handler takes care of the error. it falls into the normal ERROR. If an ERROR occurs at an INT-LEVEL greater than or equal to that of the "ERROR" interrupt. reaJ ERROR will be caJled. because attempts to defer "ERROR" interrupts are illegal. 21.10.10. "IPC" "IPC· occurs when a message is received on the ITS IPC device (chapter 23). It is not available under TENEX. 21.10.11. "INFERIOR" "INFERIOR" occurs when an inferior ITS job interrupts the MDL job. It is not available under TENEX. A handler talees one argument: a FIX between 0 and 7 inclusive, telling which inferior i. interrupting. 21.10.12. II RUNP and II REAL P These are not available under TENEX. "RUNT". if enabled, occurs once, N seconds of MDL running time (CPU time) after calling , which returns its argument. A handler takes no arguments. .. REAL T". if enabled. occurs every N seconds of real-world time after calling hangs for time seconds interruptibly. where time is non.negative. and then returns T. pred is the same as for HANG. 21.12.2 Interrupts 189 Chapter 22. Storage Management The reason this chapter comes so late in this document is that, except for special cases, MDL programs have their storage needs handled automatically. There is usually no need even to consider storage management, except as it affects efficiency (chapter 24). This chapter gives some explanation of why this is 50, and covers those special means by which a program can assume control of storage management. The MDL address space is divided into five parts, which are usually caUed (1) movable garbage-co))ected space, (2) immovable space (both garbage-collected and not). (3) user pure/page space. (4) pure-RSUBR mapping space, and (5) internal storage. Internal storage occupies both the highest and lowest addresses in the address space. and its size never changes as MDL executes. The other spaces can vary in size according to the needs of the executing program. Generally the interpreter allocates a contiguous set of addresses for each space. and each space graduaUy fills up as new objects are created and as disk files are mapped in. The action taken when a space becomes fuU varies, as discussed below. 22.1. Movable Garbage-coUected Storage Most storage used explicitly by MDL programs is obtained from a pool of free storage managed by a "garbage collector". Storage is obtained from this pool by the SUBRs which construct objects. When such a SUBR finds that the pool of available storage is exhausted. it automatically calls the garbage collector. The garbage coUector examines the storage pool and separates all the objects there into two classes: those which can possibly be referenced by a program. and those which cannot. The former are then compacted (moved) into one section of the pool. and the remainder of the pool is made available for new constructed objects. A garbage colJection can be normal or exhaustive. the difference being whether certain kinds of storage that require complicated treatment (for example. associations) are 22 - 22.1 Storage Management 190 reclaimed. An exhaustive garbage collection occurs every S2nd time, or possibly when the garbage collector is explicitly called by a program (see below), or when a normal garbage collection cannot satisfy the storage request. If the request for more storage still cannot be satisfied from reclaimed storage, the garbage collector will attempt to obtain more total storage from the operating system under which MOL runs. (Also, if there is a gross superfluity of storage space, the garbage collector will politely return some storage to tbe operating system.) Only when the total system resources are exhausted will you finally lose. Thus. if you just "forget about" a datum, that is, lose all possible means of referencing it, its storage area is automatically reclaimed. "Datum" in this context includes that stack-structured storage space used in PROCESSes for functional application. 22.1.1. Stacks and Other Internal Vectors Control stacks are used in MDL to control the changes in environment caused by calling and binding. Each active PROCESS has its own control stack. On this stack are stored LVALs for ATOMs; PRIHTYPE TUPLEs, which are otherwise like VECTORs; PRIMTYPE FRAMEs, which are generated by calling Subroutines; and ACTIVATIONs, which are generated by calling FUNCTIONs with named ACTIVATIONs, PROG. and REPEAT. TAG and LLOC can make TAGs and LOCOs (respectively) that refer to a specific place on a specific control stack. (LEGAL 7 returns T if and only if the portion of the control stack in which its argument is found or to which its argument refers is stiU active. or if its argument doesn't care about the control stack. The garbage collector may change a non-LEGAL? object to TYPE ILLEGAL before reclaiming it.) As the word "stack" implies. things can be put on it and removed from it at only one end. called the top. It has a maximum size (or depth). and attempting to put too many things on it will cause overflow. A stack is stored like a vector, and it must be GROWn if and when it overflows. A control stack is actually two stacks in one. One section is used for "top-level" LVALs -- those SET while the ATOM is not bound by any active Function's argument LIST or Subroutine's SPECIAL binding -- and the other section is used for everything else. Either section can overflow, of course. The top-Ievel-LVAL section is below the other one, so that a top-level LVAL will be found only if the ATOM is not currently bound elsewhere, namely in the other section. MDL also has an internal stack, used for calling and temporary storage within the interpreter and compiled programs. It too is stored like a vector and can overflow. There are other internal vectors that can overflow: the "global vector" holds pairs ("slots") of ATOMs and corresponding GVALs ("globally bound" means that the ATOM in question is in this vector, whether or not it currently has a global value), and the "TYPE vector" holds TYPE names (predefined and NEWTYPEs) and how they are to be treated. 22.1 - 22.1.1 Storage Management 191 22.2. Immovable Storage 22.2.1. Garbage-collected: FREEZE In very special circumstances, such as debugging RSUBRs, you may need to prevent an object from being moved by the garbage collector. FREEZE takes one argument, of PRIMTYPE VECTOR. UVECTOR, STRING or TUPLE. It copies its argument into non-moving garbage-collected space. FREEZE returns the copy CHTYPEd to its PRIM TYPE, except in the case of a TUPLE, which is changed to a VECTOR. 22.2.2. Non-garbage-coll~cted: STORAGE (the PRIMTYPE) An object of PRIMTYPE STORAGE is really a frozen UVECTOR whose UTYPE is of PRIMTYPE WORD. but it is always pointed to by something internal to MDL and thus is never garbage-COllectible. The use of FREEZE is always preferable, except when for historical reasons a STORAGE is necessary. for example when displaying PICTUREs: the principle use of STORAGE is in communicating with the Evans and Sutherland Display, which autonomously accesses primary storage and must run concurrently with garbage collection. 22.3. Other Storage User pure/page space serves two purposes. First, when a user program PURIFYs (see below) MDL objects. they are copied into this space. Second. so-caUed hand-crafted RSUBRs (assembled but not compiled) can call on the interpreter to map pages of data files into this space for arbitrary purposes. Pure-RSUBR mapping space is used by the interpreter to dynamically map pages of pure compiled programs into and out of the MDL address space. Pure code can refer to impure storage through the "transfer vector", another internal vector. This space is the most vulnerable to being compressed in size by the long-term growth of other spaces. Internal storage has both pure and impure parts. The interpreter program itself is pure and sharable. while impure storage is used for internal pointers, counters, and flags, for example, pointers to the boundaries of other spaces. In the pure part of this space are most of the ATOMs in an initial MDL. along with their OBLIST buckets (LISTs) and GVAL slots (a pure extension of the global vector). where possible. A SET or SETG of a pure ATOM automatically impurifies the ATOM and as much of its OSLIST bucket as needs to be impure. 22.2 - 22.3 Storage Management 192 22.4. Garbage Collection: Details When either of the garbage-colJected spaces (movable or immovable) becomes full. MDL goes through the following procedure: (1) A "DIVERT-AGC" interrupt occurs if the garbage colJection can be deferred temporarily by shifting boundaries between storage spaces slightly. The interrupt handler may postpone a garbage coIlection by moving boundaries itself with a call to BLOAT (below). (2) The garbage colJector begins execution. It creates an inferior job (under ITS. named AGe) or fork (under TENEX) whose address space is used to hold the new copies of non-garbage objects. MDL accesses the inferior's address space through two pages ("frontier" and "window") in its internal space that are shared with the inferior. (3) The garbage collector marks and moves all objects that can possibly be referenced hereafter. It begins with the , considered as vectors containing the control stacks, object pointers in live registers, etc. Every object in these "process vectors" is marked "acces.sible", and every element of these objects (bindings, etc.), and so on recursively. (4) If the garbage collection is "exhaustive", then both the chain of associations and top-level local/global bindings are marked last. which takes more time but is more likely to uncover garbage therein. In a normal garbage collection these constructs are not treated specially. (5) FinaIly. the inferior job/fork's address space is mapped into MDL's own, replacing old garbagey storage with the new compact storage, and the inferior is destroyed. 22.5. GC (GC min:fix exh?:false-or-any> causes the garbage collector to run and returns the total number of words of storage reclaimed. Both of its arguments are optional; if they are not supplied, a call to GC simply causes a normal garbage collection. If min is explicitly supplied as an argument, a garbage-collection parameter is changed permanently before the garbage collector runs. min is the smallest number of words of "free" (unclaimed, available for use) movable garbage-collected storage the garbage collector will be satisfied with having after it is done. Initially it is 8192 words. If the total amount of reclaimed storage is less than min. it will ask the operating system for enough storage (in l024.word blocks) to make it up. N.B.: the system may be incivil enough not to grant the request; in that case, the garbage collector will be content with what it has, unless that is not enough to satisfy a pending request for storage. 22.4 - 22.5 Storage Management 193 Then it wiU inform you that it is losing. A large min will result in fewer total garbage collections, but they will take longer since the total quantity of storage to be scanned will generally be larger. Smaller mins result in shorter, more frequent garbage collections. exh? teUs whether or not the garbage collection should be exhaustive. It is optional, with default a FALSE. 22.6. BLOAT BLOAT is used to cause a temporary expansion of the available storage space with or without changing the garbage-collection parameters. BLOAT is particularly useful for avoiding unnecessary garbage collections when loading a large file. It will cause (at most) one garbage collection, at the end of which the available storage will be at least the amount specified in BLOArs application. (Unless. of course, the operating system is cranky and will not provide the storage. Then you will get an error. from this error will cause the BLOAT to return 1, which usually just causes you to lose at a later time - unless the operating system feels nicer when the storage is absolutely necessary.) A call to BLOAT looks like this: (BLOAT fre stk leI glb typ sto pstk min plcl pglb ptyp imp pur dpstk dstk> where all arguments on the first line are FIX, optional (default 0), and indicate the following: fre: number of words of free movable storage desired (for LISTs, VECTORs. ATOMs, etc.) sllc number of words of free control-stack space desired (for functional applications and binding of ATOMs) leI: number of new top-level LVALs for which to leave space (SETs of ATOMs which are not currently bound) glb: number of new GVALs for which to leave space (in the global vector) typ: number of new TYPE definitions for which to leave space (in the TYPE vector) slo: number of words of immovable garbage-collected storage desired pst/<: number of words of free internal-stack space desired (for READing large STRINGs, and calling routines within the interpreter and compiled programs) 22.5 - 22.6 Storage Management 194 Arguments on the second line are also FIX and optional. but they set garbage-coUection parameters permanently. a, followSl min: as for GC pld: number of slots for LVALs added when the space for top-level LVALs is expanded (initially 64) pglb: number of slots for GVALs added when the global vector is grown (initialJy 64) ptyp: number of slots for TYPEs added when the TYPE vector is grown (initiaJly 32) imp: number of words of immovable garbage-collected storage added when it is expanded (initiaJJy 1024) pur: number of words reserved for pure compiled programs, if possible (initially 0) dpstk: most desirable size for the internal stack, to prevent repeated shrinking and GROWing (initially 512) dstk: most desirable size for the control stack (initially 4096) BLOAT returns the actual number of words of free movable garbage-collected storage available when it is done. 22.7. BLOAT-STAT BLOAT-STAT can be used with BLOAT to "tune" the garbage collector to particular program requirements. fiUs the uvector with information about the state of storage of MOL. The argument should be a UVECTOR of length 27 and UTYPE FIX. If BLOAT-STAT does not get an argument. it wiJJ provide its own UVECTOR. The information returned is as follows: the first 8 elements indicate the number of garbage coJJections that are attributable to certain causes, and the other 19 give information about certain areas of storage. In detail: 1. number of garbage collections caused by exhaustion of movable garbage-coJJected storage 2. ditto by overflow of control stack(s) 3. ditto by overflow of top·level-LVAL section of control stack(s) 4. ditto by overflow of global vector 5. ditto by overflow of TYPE vector 22.6 - 22.7 Storage Management 195 6. ditto by exhaustion of immovable garbage-coJJected storage 7. ditto by overflow of internal stack 8. ditto by overflow of both stacks at the same time (rare) 9. number of words of movable storage 10. number of words of movable storage used since last BLOAT-STAT 11. maximum number of words of movable storage ever existing 12. number of words of movable storage used since MOL began running 13. maximum size of control stack 14. number of words on control stack in use 15. maximum size of control stack(s) ever reached 16. number of slots for top-level LVALs 17. number of top-level LVALs existing 18. number of slots for GVALs in global vector 19. number of GVALs existing 20. number of slots for TYPEs in TYPE vector 21. number of TYPEs existing 22. number of words of immovable garbage-coJJected storage 28. number of words of immovable storage unused 24. size of largest unused contiguous immovable-storage block 25. number of words on internal stack 26. number of words on internal stack in use 27. maximum size of internal stack ever reached I 22.8. GC-HON returns old. after cau.sing a miniature garbage coJJection to occur. during which aU references to old are changed 50 as to refer to new. Neither argument can be of PRIHTYPE STRING or BYTES or lOCO or live on the control stack. unless both are of the same PRIMTYPE. One TYPE name cannot be .substituted for another. One of the few legitimate uses for it is to substitute the "right" ATOM for the "wrong" one. after OBLISTs have been in the wrong state. This is more or le55 the way ATOMs are impurified. It is also useful for unlinking RSUBRs. SUBSTITUTE returns old as a favor: unless you hang onto old at that point. it will be garbage-coUected. 22.9.2. PURIFY return.s i15 last argument. after causing a miniature garbage collection that results in all the arguments becoming pure and sharable. and ignored afterward by the garbage collector. No argument can live on the control stack or be of PRIMTYPE PROCESS or lOCO or ASOC. Sharing between jobs or forks actuaIJy occurs after a SAVE. if and when the SAVE file is RESTOREd. 22.9 • 22.9.2 Storage Management 197 Chapter 23. MDL as an ITS Job This chapter treats MOL considered as executing in an ITS job, and interactions between MOL and other ITS jobs. See also section 21.10.11. Unless otherwise indicated, none of the features in this chapter is available under TENEX. 23.1. TIME This SUBR is available under TEN EX. TIME takes any number of arguments, which are evaluated but ignored, and returns a FLOAT giving the number of seconds of CPU time MOL has used so far. It is often used in debugging to examine the values of its arguments, by having MOL's superior job or fork (say, DDT) plant a breakpoint in the code for TIME. 23.2. UNAME returns a STRING of up to six characters which is the "user name" (login identification) of MOL's job. The characters belong to the "sixbit" or "printing" subset of ASCII, namely those between and inclusive. 23.3. JNAME returns a "sixbit" STRING of up to six characters which is the "job name" of MOL's job. 23.4. LOGOUT attempts to log out the job in which it is executed. It will succeed only if the MOL is the top-level 23·23.4 MOL as an ITS Job 198 job. that is. it is running disowned or as a daemon. If it succeeds. it of course never returns. If it does not. it returns #FALSE (). 23.5. VALRET ("value return") seldom returns. It passes control back up the ITS job tree to the superior of MDL. passing string as a message to that superior. If it does return. the value is IFALSE (). 23.6. QUIT causes your MDL to die in an orderly manner. Under ITS. it is equivalent to . Under TEN EX. is equivalent to a control-C signal. and control passes to the superior fork. 23.7. DEMSIG signals to ITS that the daemon named by its argument should run now. It returns T if the daemon exists, IFALSE () otherwise. 23.8. Inter- job Communication The IPC ("interprocess communication") device is treated as an 1/0 device by ITS but not explicitly so by MDL; that is. it is never OPENed. It allows MDL to communicate to other ITS jobs by means of sending and receiving messages. A job identifies itself as sender or recipient of a message with an ordered pair of "sixbit" STRINGs, which are often but not always and . A message has a "body" and a "type". 23.8.1. SEND and SEND-WAIT 23.4 - 23.S.1 MDL as an ITS Job 199 both send an IPC message to any job that is listening for it as othernl othern2. body must be either a STRING, or a UVECTOR of objects of PRIMTYPE WORD. type is an optional FIX, with default O. which is part of the information the other guy receives. The last two arguments are from whom the message is to be sent. These are optional and default to and respectively. SEND returns a FALSE if no one is listening. while SEND-WAIT hangs until someone wants it. Both return T if someone accepts the message. 23.8.2. The "IPC" Interrupt When your MDL job receives an IPC message. "IPC" occurs (chapter 21). A handler is called with either four or six arguments gleaned from the received message. body. type. othernl. and othern2 are always supplied. mynamel and myname2 are supplied only if they are not this jOb's and . There is a default HANDLER for the "IPC" interrupt, with a handler named IPC-HANDLER and 0 in the process slot. The handler prints out on the console the body, whom it is from, the type if not O. and whom it is to if not . If the type is 1 and the body is a STRING. then. after the message information is printed out. the STRING is PARSEd and EVALuated. 23.8.3. IPC-OFF < IPC-OFF > stops all listening on the IPC device. 23.8.4. IPC-ON causes listening on the IPC device as mynamel myname2. If no arguments are provided. listening is on . When a message arrives. "IPC" occurs. MDL is initially listening as with the default HANDLER set up on the "IPC" interrupt with a priority of 1. 23.8.1 • 23.8.4 MDL as an ITS Job 200 Chapter 24. Efficiency and Tastefulness 24.1. Efficiency Actually. you make MDL programs efficient by thinking hard about what they really make the interpreter do. and making them do less. Some guidelines. in order of decreasing expense: (1) Free storage is expensive. (2) Calling functions is expensive. (3) PROG and REPEAT are expensive. Explanation: (1) Unnecessary use of free storage (creating needless LISTs. VECTORs, UVECTORs. etc.) will cause the garbage collector to run more often. This is expensive!! A fairly large MDL (for example. 60 000 J6..bit words) can take ten seconds of PDP-IO CPU time to garbage-collect. Be especially wary of constructions like (0). Every time that is evaluated. it creates a new one-element LIST; it is too easy to write such things when they aren't reaUy necessary. Unless you are doing PUTs or PUTRESTs on it, use 1(0) instead. (2) Sad, but true. Also generally ignored. If you call a function only once, or if it is short (Jess than one line). you are much better off in speed if you substitute its body in by hand. On the other hand. you may be much worse off in modularity. There are techniques for combining several FUNCTIONs into one RSUBR (with RSUBR-ENTRYs), either during or after compilation, and for changing FUNCTIONs into MACROs. (3) PROG is almost never necessary. given (a) "AUX" in FUNCTIONs; (b) the fact that FUNCTIONs can contain any number of FORMs; (c) the fact that COND clauses can contain any number of FORMs; and (d) the fact that new variables can be generated and initialized by REPEAT. By the way. REPEAT is faster than GO in a PROG. The FORM has to be separately interpreted. right? In fact, if you organize things properly you very seldom need a GO; using GO is generally considered "bad style", but in some cases it's needed. Very few. In many cases, a REPEAT can be replaced with a MAPF or MAPR, or an ILlST, IVECTOR, etc. of the form 24 - 24.1 Efficiency and Tastefulness 201 <.N .Y»» > » » Comments: (1) LIST is only temporarily necessary. It is just created and then thrown away. (2) Worse. the construct (!. LIST! takes a long time. if the LIST is long. <3 •.. > or (4 ••• > is not worth worrying about, but <10 ••• > is, and <100 .•• > takes quite a while. Even if the indexing were not phased out. the compiler would be happier with . 24.1 - 24.1.1 Efficiency and Tastefulness 202 (4) The variable CHN is unnecessary if OUTCHAN is bound to the argument CHANNEL (5) It is tasteful to call ERROR in the same way that F/SUBRs do. This includes using an ATOM from the ERRORS OBLIST (if one is appropriate) to tell what is wrong. and it includes identifying yourself. So. do it this way: (DEFINE PLOTVDSK (X Y OUTCHAN) IDECL «OUTCHAN) (REPEAT «OL <1 .V»» #FUNCTION «XE YE) #FUNCTION «T) IIDONEII> 24.1.1 Efficiency and Tastefulness 203 24.2. Creating a LIST in Forward Order LIST in sequence from first to last, you can avoid copying earJier ones when adding a later one to the end. One way is to use MAPF or MAPR or STACKFORM with a first argument of ,LIST: the elements are put on the control stack rather than in free storage, ulltil the final can to LIST. If you know how many elements there will be. you can put them on the control stack yourself. in a TUPLE built for that purpose. Another way is used when REPEAT is necessary: If you must create the elements of a could also be written (PUTREST •LAST (SET LAST (. NEW)>>. 24.3. Read-only Free Variables If a Function uses the value of a free variable «GVAL unmanifest:atom> or rather than (DEFINE name (X) (fix • X» for read-only elements. 24.6. Tables There are several ways in MOL to store a table, that is, a collection of (names and) values that will be searched. Unsurprisingly. choosing the best way is often dictated by the size of the table and/or the nature of the (names and) values. For a small table. the names and values can be put in (separate) structures - the choice of LIST or array being determined by volatility and Iimitability _. which are searched using MEMQ or MEMBER. This method is very space-efficient. If the table gets larger, and if the elements are completely orderable. a (uniform) vector can be used. kept sorted. and searched with a binary search. For a large table. where reasonably efficient searches are required. a hashing scheme is probably best. Two methods are available in MOL: associations and OBLISTs. In the first method. PUTPROP and GETPROP are used. which are very fast. The number of hashing buckets is fixed. Duplicates are eliminated by ==7 testing. If it is necessary to use =? testing, or to find all the entries in the table. you can duplicate the table in a LIST or array. to be used only for those purposes. In the second method. INSERT and LOOKUP on a specially-built OBLIST are used. (If the names are not STRINGs, they can be converted to STRINGs using UNPARSE, which takes a little time.) The number of hashing buckets can be chosen for best efficiency. Duplicates are eliminated by =? testing. MAPF/R can be used to find all the entries in the table. 24.7. Nesting The beauty of deeply-nested control structures in a single FUNCTION is definitely in the eye of the beholder. (PPRINT, a preloaded RSUBR. finds them trying. However, the complier often produces better code from them.) If you don't like excessive nesting. then you will agree that ••• ) ..• > 24.5 - 24.7 Efficiency and Tastefulness 205 looks better than (COND «01 and that (REPEAT ... (COND ( .•• (RETURN ... »> •.. > looks better than (REPEAT (COND ••• > ( •.• 1 FIX I ------->1 FIX 1 ------->1 FIX 1 0 I ------------I - - - - I I - - - - I I - - - - I I 1 I I 2 I I 3 I The use of pointers to tie together elements explains why new elements can be added easily to a Jist, how sharing and circularity work. etc. The Jinks go in only one direction through the Jist. which is why a list cannot be BACKed or TOPped: there's no way to find the RESTed elements, Since some MDL values require a word and a half for the value in the pair. they do not fit directly into list elements. This problem is solved by having "deferred pointers·, Instead of putting the datum directly into the list element. a pointer to another pair is used as the value with the special internal TYPE DEFER, and the real datum is put in the deferred pair. For example the LIST (1 -hello· 3) would look like this: I LIST I 0 I 1 - - - - - I ------------------------------I 0 I ------>1 FIX I ------->IDEFERI ------->1 FIX I 0 I ------------- I - - - - I I 1 I I - - - - I I ----------- I I ----------- I ISTRING I 51 1 --------------- FIX I I I - - - - - - - I I 1 1 1 STRING I 3 I 1-- - - - - - I I byte pointer I I FIX I I I - - - - - - - I I 3 I I 440000 1 0 I I - - - - - - - I I 10 1 I The UVECTOR ! [-1 7 -4!] looks like: I UVECTOR I 0 I I - - - - - - I I -3 I ----------------- -----------> I -1 7 -4 I 40000+FIX I 0 I I - - - - - - - I I 5 I I Atoms Internally. atoms are special vector-like objects. An atom contains a value cell (the first two words of the block. filled in whenever the global or local value of the ATOM is referenced and is not already there). an OBLIST pointer. and a print name (PNAME), in the following format: Appendix 1 213 type bindid -------------------._-----------pointer-to-value ---------------------------._.--pointer-to-OBLIST --------------------------------I print-name I I I I I I (ASCII with NUL padding on end)1 ATOM valid-type 1---------------1 length gc If the type field corresponds to TYPE UNBOUND, then the ATOM is locally and globally unbound. (This is different from a pair, where the same TYPE UNBOUND is used to mean unassigned.) If it corresponds to TYPE LOCI (an internal TYPE), then the value cell points either to the global stack. if bindid is zero, or to a local control stack. if bindid is non-zero. The bind1d field is used to verify whether the local value pointed to by the value cell is valid in the current environment. The pointer-to-OBLIST is either a counting pointer to an oblist (uvector). a positive offset into the "transfer vector" (for pure ATOMs). or zero, meaning that this ATOM is not on an OBLIST. The va 1 idtype field tens whether or not the ATOM represents a TYPE and if so the code for that TYPE: grow values are never needed for atoms. Associations Associations are also special vector-like objects. The first six words of the block contain TYPE/value pairs for the ITEM, INDICATOR and AVALUE of the ASOC. The next word contains forward and backward pointers in the chain for that bucket of the association hash table. The last word contains forward and backward pointers in the chain of aU the associations. Appendix 1 214 I ITEM I I - - - - - - - - - - - - - - - I I pair I I INDICATOR I I - - - - - - - - - - - - - - - I I pair I I AVALUE I I - - - - - - - - - - - - - - - I I pair I bucket-cha1n pointers association-chain pOinters I ASOC I 0 I I - - - - - - - - - - - - - - - I I 12 octa 1 I gc I Processes A PROCESS vector looks exactly like a vector of TYPE/value pairs. It is different only in that the garbage collector treats it differently from a normal vector, and it contains extremely volatile information when the PROCESS is RUNNING. Templates In a template, the number in the type field (left half of first dope word) identifies to which "storage allocation class" this TEMPLATE belongs, and it is used to find PDP.to instructions in internal tables (frozen uvectors) for performing LENGTH, NTH, and PUT operations on any object of this TYPE. The programs to build these tables are not part of the interpreter, but the interpreter does know how to use them properly. The compiler can put these instructions directly in compiled programs if a TEMPLATE is never RESTed; otherwise it must let the interpreter discover the appropriate instruction. The value word of a template pair contains, not a counting pointer, but the number of elements that have been RESTed off in the left half and a pointer to the first dope word in the right half. Appendix 1 215 The Control Stack Accumulators with symbolic names AB. TB. and TP are all pointers into the RUNNING PROCESS's control stack. AS ("argument base") is a pointer to the arguments to the Subroutine now being run. It is set up by the Subroutine-call mediator, and its old value is always restored after a mediated Subroutine calJ returns. TS ("temporaries base") points to the frame for the running Subroutine and also serves as a stack base pointer. The TB pointer is really all that is necessary to return from a Subroutine - given a value to return, for example by ERRET -- since the frame specifies the entire state of the calling routine. TP ("temporaries pointer") is the actual stack pointer and always points to the current top of the control stack. While we're on the subject of accumulators, we might as well be complete. Each accumulator contains the value word of a pair, the corresponding TYPE words residing in the RUNNING PROCESS vector. When a PROCESS is not RUNNING (or when the garbage collector is running), the accumulator contents are stored in the vector, so that the Objects they point to look like elements of the PROCESS and thus are not garbage-collectible. Accumulatofs A. B. C, 0, E and 0 are used almost entirely as scratch accumulators, and they are not saved or restored across Subroutine calls. Of course the interrupt machinery always saves these and all other accumulators. A and B are used to return a pair as the value of a Subroutine call. Other than that special feature, they are just like the other scratch accumulators. Hand R are used in running RSUBRs. M is always set up to point to the start of the RSUBR's code, which is actually just a uniform vector of instructions. All jumps and other references to the code use M as an index register. This makes the code location-insensitive, which is necessary because the code uvector will move around. R is set up to point to the vector of objects needed by the RSUBR. This accumulator is necessary because Objects in garbage-collected space can move around, but the pointers to them in the reference vector are always at the same place relative to its beginning. FRH is the internal frame pointer, used in compiled code to keep track of pending Subroutine calls when the control stack is heavily used. P is the internal-stack pointer, used primarily for internal calls in the interpreter. One of the nicest features of the MOL environment is the uniformity of the calling and returning sequence. All Subroutines -- both built-in F/SUBRs and compiled RSUBR( -ENTRY)s -- are called in exactly the same way and return the same way. Arguments are always passed on the control stack and results always end up in the same accumulators. For efficiency reasons, a lot of internal calls within the interpreter circumvent the calling sequence. However, all calls made by the interpreter when running user programs go through the standard calling sequence. A Subroutine call is initiated by one of three UUOs (PDP-10 instructions executed by software rather than hardware). MCALL ("MOL call") is used when the number of arguments is known at assemble or compile time, and this number is less than 16. QCALL ("quick calli may be used if. in addition. an RSUBR( -ENTRY) is being called that can be called "quickly" by virtue of its having Appendix 1 216 special information in its reference vector. ACALL ("accumulator call") is used otherwise. The general method of calling a Subroutine is to PUSH (a PDP-lO instruction) pairs representing the arguments onto the control stack via TP and then either (1) MCALL or QCALL or (2) put the number of arguments into an accumulator and ACALL. Upon return the object returned by the Subroutine will be in accumulators A and S, and the arguments will have been POPped off the control stack. The call mediator stores the contents of P and TP and the address of the calling instruction in the current frame (pointed to by TB). It also stores MDL's "binding pointer" to the topmost binding in the contra) stack. (The bindings are linked together through the control stack so that searching through them is more efficient than looking at every object on the stack.) This frame now specifies the entire state of the caller when the call occurred. The mediator then builds a new frame on the control stack and stores a pointer back to the caller's frame (the current contents of TB), a pointer to the Subroutine being called, and the new contents of AB, which is a counting pointer to the arguments and is computed from the information in the MCALL or QCALL instruction or the ACAll accumulator. TB is then set up to point to the new frame, and its left half is incremented by one. making a new un 1que-1 d. The mediator then transfers control to the Subroutine. A control stack frame has seven words as shown: ENTRY called-addr unique-id prev frame argument pointer saved binding pOinter saved P saved TP saved calling address The first three words are set up during the call to the Subroutine. The rest are filled in when this routine calls another Subroutine. The left half of TB is incremented every time a Subroutine call occurs and is used as the unique-id for the frame, stored in frame and tuple pairs as mentioned before. Obviously this id is not strictly unique. since each 256K calls it wraps around to zero. The right half of TB is always left pointing one word past the saved-calling-address word in the frame. TP is also left pointing at that word, since that is the top of the control stack at Subroutine entry. The arguments to the called Subroutine are below the frame on the control stack (at lower storage addresses). and the temporaries for the called Subroutine are above the frame (at higher storage addresses). These arguments and temporaries are just pairs stored on the control stack while needed: they are all that remain of UNSPECIAL values in compiled programs. Appendix 1 217 The following figure shows what the control stack might look like after several Subroutine calls. I I I I ----------------- I ,I I args for 51 I frame for 5.1 ----------------I temps for 51 I ----------------args for 52 ----------------I frame for 52 I temps for 52 I args for 53 (-- I I I I I I I I (-----I I I I I I I frame for 53 temps for 53 (top) The above figure shows the frames an linked together through the control stack (the "execution path"), 50 that it is easy to return to the caJler of a given Subroutine (ERRET or RETRY). Subroutine exit is accomplished simply by the call mediator, which loads the right half of TB from the previous frame pointer, restores the "binding pointer", P, and TP, and transfers control back to the instruction following the saved calling address. Appendix 1 218 Variable Bindings All local ATOM values are kept on the control stack of the PROCESS to which they are local. As described before. the atom contains a word that points to the value on the control stack. The pointer is actuany to a six-word "binding block" on the control stack. Binding blocks have the foJJowing format: I BIND or UBIND I prev pointer to ATOM I value I I - - - - - - - - - - - - - - - I I pair I decl unique-id previous-binding where: BIND means this is a binding for a SPECIAL ATOM (the only kind used by compiJed programs). and UBIND means this is a binding for an UNSPECIAL ATOM -- for SPECIAL checking by the interpreter; prev points to the closest previous binding block for any ATOM (the "acce55 path" -- UNWIND objects are also linked in this chain); decl points to a OECL associated with this value. for SET( LOC) to check; un i que- i d is used for validation of this block; and previous-binding points to the closest previous binding for this ATOM (used in unbinding). Bindings are generated by an internal subroutine called SPECBINO (name comes from SPECIAL). The calJer to SPECBIND PUSHes consecutive six-word blocks onto the control stack via TP before caJJing SPECBINO. The first word of each block contains the TYPE code for ATOM in its left half and an ones in its right half. SPECBINO uses this bit pattern to identify the binding blocks. SPECBINO's caJler also fills in the next three words and leaves the last two words empty. SPECBIND fills in the rest and leaves the "binding pointer" pointing at the topmost binding on the control stack. SPECBIND also stores a pointer to the current binding in the value cen of the atom. Appendix 1 219 Unbinding is accomplished during Subroutine return. When the previous frame is being restored. the call mediator checks to see if the saved "binding pointer" and the current one are different; if they are, SPECSTORE is called. SPECSTORE runs through the binding blocks. restoring old value pointers in atoms until the "binding pointer" is equal to the one saved in the frame. Obviously variable binding is more complicated than this. because ATOMs can have both local and global values and even different local values in different PROCESSes. The solution to all of these additional problems lies in the b1nd1d field of the atom. Each PROCESS vector also contains a current bindid. Whenever an ATOM's local value is desired, the RUNNING PROCESS's bindid is checked against that of the atom: if they are the same, the atom points to the current value; if not. the current PROCESS's control stack must be searched to find a binding block for this ATOM. This binding scheme might be caUed "shallow binding". The searching is facilitated by having all binding blocks linked together. Accessing global variables is accomplished in a similar way. using a VECTOR that is referred to as the "global stack". The global stack has only an ATOM and a value slot for each variable, since global values never get rebound. EVAl with respect to a different environment causes some additional problems. Whenever this kind of EVAl is done, a brand new bindid is generated, forcing all current local value cells of atoms to appear invalid. Local values must now be obtained by searching the control stack, which is inefficient compared to just pulling them out of the atoms. (The greatest inefficiency occurs when an ATOM's lVAl is never accessed twice in a row in the same environment.) A special block is built on the control stack and linked into the binding-block chain. This block is called a "skip block" or "environment splice", and it diverts the access path to the new environment, causing searches to become relative to this new environment. Appendix 1 220 Appendix 2. Predefined Subroutines The folJowing is a very brief description of aU the primitives currently available in MOL. These descriptions are in no way to be considered a definition of the effects or values produced by the primitives. They just try to be as complete and as accurate as is possible in a single-statement description. However. because of the complexity of most primitives. many important defaults and restrictions have been omitted. A description is given in this format: NAME rsubr-decl English description The NAME is just the ATOM that is used to refer to the primitive. The rsubr-decl is what would go inside an HRSUBR DECL" (chapter 14) for the primitive. One special convention is used: an element of a DECL Pattern enclosed in hyphens (for example -STRING-) means that that element only is optional. This convention is handy for F/SUBRs that take flexible argument Patterns. Even though all primitives return a value. some English descriptions only mention the side effects produced by a primitive. because these primitives are most often used for this effect rather than the value. "VALUE- (OR FIX FLOAT) MTUPLE- (TUPLE [REST (OR FIX FLOAT>]> Arithmetic: multiplication + ·VALUE- (OR FIX FLOAT) "TUPLE- Arithmetic: addition ·VALUE- -TUPLE- ]> Arithmetic: subtraction I "VALUE- -TUPLE- ]> Arithmetic: division Appendix 2 221 01 "VALUE" (OR FALSE 'T) (OR FIX FLOAT) Predicate: equality to number zero 11 "VALUE" (OR FALSE 'T) (OR FIX FLOAT) Predicate: equality to number one ISTEP "VALUP PROCESS PROCESS Cause a proceu to enter single-step mode ==1 "VALUE" (OR 'T FALSE) ANY ANY Predicate: "exact" equality =1 "VALUE" (OR 'T FALSE) ANY ANY Predicate: "structural" equality ASS -VALUE- (OR FIX FLOAT) (OR FIX FLOAT) Arithmetic: absolute value ACCESS "VALUE" CHANNEL CHANNEL FIX Randomly access disk file etc. ACTIVATE-CHARS "VALUE" STRING "OPTIONAL" STRING Set or inspect interrupt characters for console typing (TEN EX only) AGAIN "VALUE" ANY "OPTIONAL" ACTIVATION Restart a given activation block Appendix 2 222 ALLTYPES "VALUE" Return a vector of all type names AND -VALUED -ARGS· LIST Logical: "and" of truth values. evaluated one at a time by the Subroutine AND? IIVALUE" -TUPLE" TUPLE Logical: "and" of truthvaJues• .evaJuated at caU time ANDB "VALUEII WORD "TUPLE" ]> Bitwise "and" of words APPLICABLE? -VALUE- ANY Predicate: is object applicabJe APPLY "VALUE" ANY APPLICABLE "TUPLE" TUPLE Apply first argument to the rest of them APPLYTYPE "VALUE" ATOM "OPTIONAL" Specify or return how a data type is applied ARGS "VALUE" TUPLE Return arguments of a given previous Subroutine caU ASCII "VALUE" Return character with given ASCII code or vice versa AppendiX 2 223 f ASSIGNED? "VALUE" ATOM "OPTIONALIJ Arithmetic: arc tangent ATOM "VALUP ATOM STRING Create an atom with a given name AVALUE "VALUE" ANY ASOC Return the "value" field of an association BACK "VALUE" ·OPTIONAL· (UVECTOR [27 ANY]> Give garbage-collector and storage statistics BLOCK "VALUE" ]» Create a current path of oblists for READing BOUND? "VALUE" (OR 'T FALSE> ATOM IIOPTIONAL II (OR ENVIRONMENT ACTIVATION FRAME PROCESS) Predicate: is an atom locally bound BREAK-SEQ IIVALUE" PROCESS ANY PROCESS Modify execution sequence of another process BUFOUT "VALUE" CHANNEL "OPTIONAL" CHANNEL Write out aU internal MDL buffers for an I/O channel BYTES "VALUE" BYTES FIX "TUPLE" ]> Create a byte-string of explicit arguments BYTE-SIZE "VALUE" FIX BYTES Return size of bytes in a byte-string CHANLIST "VALUE" Return a list of currently open 1/0 channels Appendix 2 / 225 CHANNEL IIVALUEII CHANNEL IIOPTIONAL" STRING "TUPLE" TUPLE Create an 1/0 channel CHTYPE "VALUEII ANY ANY ATOM Make a new object with a given data type from an old one CHUTYPE "VALUER ATOM Change the data type of the elements of a uniform vector CLOSE "VALUEII CHANNEL CHANNEL Close an I/O channel CLOSURE "VALUE" CLOSURE FUNCTION "TUPLE" Bind the free variables of a FUNCTION to current values COND ·VALUE" ANY IIARGS" Conditional evaluation of expressions CONS ·VALUE" LIST ANY LIST Add an element to the front of a list COS "VALUE II FLOAT Arithmetic: cosine CRLF "VALUE" IT "OPTIONAL" CHANNEL Print a carriage-return and line-feed on an I/O channel Appendix 2 226 DECL-CHECK "VALUE" (OR FALSE ANY> "OPTIONAL" (OR FALSE ANY> Enable or disable type-declaration checking DECL? "VALUE" (OR 'T FALSE> ANY (OR ATOM (FORM 'QUOTE FORM» Predicate: does given object match given type declaration DEFINE UVALUE" ATOM ATOM uARGS· (LIST -ATOM- LIST -DECL- ANY> Set the global value of an atom to a FUNCTION DEFMAC "VALUE" ATOM ATOM uARGSu (LIST -ATOM- LIST -DECL- ANY> Set the global value of an atom to a MACRO DEMSIG "VALUE" (OR IT FALSE> STRING Signal an ITS daemon DISABLE ·VALUE· IHEAOER IHEADER Disable an interrupt DISMISS "VALUE- ANY ANY uOPTIONAL" ACTIVATION FIX Dismiss an interrupt occurrence DISPLAY "VALUE" CHANNEL CHANNEL "OPTIONAL" PICTURE Display pictures on the E&:S (ITS only) ECHOPAIR ·VALUE- CHANNEL CHANNEL CHANNEL Coordinate 110 channels for echoing characters on rubout Appendix 2 227 EMPTY? ·VALUE" (OR IT FALSE) STRUCTURED Predicate: does a structure have zero elements ENABLE ·VALUE· IHEADER IHEADER Enable an interrupt ENDBLOCK ·VALUE· (OR OBLIST (LIST (REST ]» Restore path of obli$u extant before last call to BLOCK ENTRY-LOC ·VALUE- FIX RSUBR-ENTRY Return the offset of an RSUBR-ENTRY EQVB ·VALUE u WORD "TUPLE" (TUPLE (REST ]> Bitwise "equivalence" of words ERASE ·VALUeu CHANNEL CHANNEL "OPTIONALu PICTURE Erase a picture from the E&CS (ITS only) ERRET ·VALUEu ANY ·OPTIONAL" ANY FRAME Continue evaluation from the last ERROR or LISTEN or from a given frame ERROR IIVALUE" ANY IITUPLE" TUPLE Stop and inform user of an error ERRORS IIVALUP OBLIST Return the obJist where error messages are located Appendix 2 228 EVAL IIVALUE" ANY ANY IIOPTIONAL II (OR ACTIVATION FRAME ENVIRONMENT PROCESS> Evaluate an expression in a given environment EVALTYPE ·VALUE· (OR ATOM APPLICABLE FALSE> ATOM ·OPTIONAL" Specify or return how a data type is evaluated EVENT uVALUEu IHEADER (OR STRING ATOM IHEADER> FIX "OPTIONAL" (OR CHANNEL LOCATIVE> Set up an interrupt EXP "VALUE" FLOAT (OR FIX FLOAT> Arithmetic: exponentiation to the base "e" EXPAND "VALUER ANY ANY Evaluate argument (only once if a MACRO is involved) in top-level environment FILE-LENGTH ·VALUE" FIX CHANNEL Return the system-provided file length of an input channel FILECOPY "VALUE" FIX uOPTIONAL" CHANNEL CHANNEL Copy characters from one channel to another until an EOF on the input channel FIX "VALUE" FIX (OR FLOAT FIX> Arithmetic: return integer value of a number FLATSIZE IIVALUEII ANY FIX IIOPTIONALII FIX Return number of characters needed to PRINI an object Appendix 2 229 FLOAD "VALUE" '''DONE'' "OPTIONAL" -STRING- -STRING- -STRING- -STRING- (OR OBLIST (LIST [REST (OR OBLIST 'DEFAULT>]» Read and evaluate all objects in a file FLOAT "VALUE" FLOAT Arithmetic: return floating value of a number FORM "VALUE" FORM "TUPLE" TUPLE Create a form of explicit arguments FRAME "VALUP FRAME "OPTIONAL" (OR PROCESS FRAME ACTIVATION ENVIRONMENT> Return a previous Subroutine call FREE "VALUE" PROCESS Cause a process to leave single-step mode FREEZE "VALUE" Return Subroutine name of a given .previous Subroutine call FUNCTION "VALUE" FUNCTION "ARGS" Create a FUNCTION Appendix 2 230 G=1 "VALUE" (OR IT FALSE> (OR FIX FLOAT> (OR FIX FLOAT> Predicate: is first argument greater than or equal to second G1 "VALUE" (OR IT FALSE> (OR FIX FLOAT> (OR FIX FLOAT> Predicate: is first argument greater than second GASSIGNEO? -VALUE- (OR IT FALSE> ATOH Predicate: is an atom globally assigned GBOUN01 -VALUE" (OR IT FALSE> ATOM Predicate: is an atom globally bound GC "VALUE" FIX "OPTIONAL- FIX (OR FALSE ANY> Cause a garbage collection and change a garbage-collection parameter GC-DUMP "VALUE" (OR ANY (UVECTOR (PRIMTYPE WORD»> ANY (OR CHANNEL FALSE> Dump an object so that it can be reproduced exactly GC-MON "VALUE" (OR FALSE ANY> "OPTIONALu (OR FALSE ANY> Turn garbage-collection monitoring off or on GC-READ "VALUE" ANY CHANNEL "OPTIONAL" ANY Input an object that was previously GC-DUMPed GDECL "VALUE" ANY "ARGS" (LIST [REST (LIST [REST ATOM]> (OR ATOM FORM>]> Declare the type/structure of the global value of an atom Appendix 2 231 GET "VALUE" ANY ANY ANY "OPTIONAL" ANY Return a given property associated with an item GET-DECL ·VALUE" LOCO Get the type declaration for an atom's value GETBITS ·VALUE- WORD ANY ANY "OPTIONAL" ANY Return a locative to an association or to an element GETPL "VALUE" ANY ANY ·OPTIONALH ANY Return a locative to an association GETPROP ·VALUE· ANY ANY ANY HOPTIONAL H ANY a more general version of GET GLOC ·VALUEH LOCO ATOM HOPTIONAL H Return a locative to the global-value cell of an atom GO HVALUEu ANY IIOPTIONAL" PROCESS Create an interrupt handler HANG "VALUP ANY IIOPTIONAL II ANY Do nothing. interruptibly. forever IBYTES ·VALUP BYTES FIX FIX "OPTIONAL" ANY Create a byte-string of implicit arguments IFORH "VALUP FORM FIX "OPTIONAL II ANY Create a form of implicit arguments ILlST "VALUP LIST FIX "OPTIONAL II ANY Create a list of implicit arguments IMAGE "VALUE" FIX FIX IIOPTIONAL" CHANNEL Send an image-mode character to a channel IN IIVALUP ANY LOCATIVE Return the object pointed to by a locative Appendix 2 233 INDICATOR "VALUE" ANY ASOC H Return the "indicator field of an association INSERT "VALUE" ATOM Arithmetic: natural logarithm Appendix 2 236 LOGOUT "VALUE" FALSE Log out of ITS (useful for disowned and daemon jobs) LOOKUP "VALUE" STRING OBLIST Return an atom found on a given oblist LPARSE "VALUE" LIST "OPTIONAL II STRING FIX ]» VECTOR CHARACTER Return a list of the objects parsed from a string (sections 7.6.6.8. 15.7.2. 17.1.8) LVAL "VALUE" ANY ATOM "OPTIONAL II APPLICABLE "TUPLE" Map function onto elements of structures MAPLEAVE "VALUE" ANY "OPTIONAL" ANY Leave the most recent MAPF/R with a value Appendix 2 237 MAPR "VALUE" ANY APPLICABLE "TUPLE" Map function onto "rests" of structures MAPRET ·TUPLE" TUPLE Return a variable number of objects to the current MAPF/R MAPSTOP "TUPLE· TUPLE MAPRET. then stop looping of MAPF/R and cause application MAX "VALUE" "TUPLE" ]> Arithmetic: maximum argument ME "VALUE" PROCESS Return the current process MEMBER ·VALUE" ANY STRUCTURED Predicate: is object =7 to some element of a structure MEMQ "VALUE" ANY STRUCTURED Predicate: is object ==7 to some element of a structure MIN "VALUE" "TUPLE" ]> Arithmetic: minimum argument MOB LIST "VALUE" OBLIST ATOM "OPTIONAL" FIX Create or get an obJist Appendix 2 238 MOD "VALUE" FIX FIX FIX Arithmetic: numerical modulus or remainder HONAD? ·VALUE" (OR IT FALSE) ANY Predicate: is object unstructured or empty structure N==? ·VALUER Logical: "not" of a truth value NTH -VALUE- ANY STRUCTURED -OPTIONALM FIX Return the Nth element of a structure OBLIST? "VALUE" (OR OBLIST FALSE) ATOM Predicate: return atom's oblist or false if none OFF ·VALUE" (OR FALSE HANDLER IHEADER) (OR HANDLER IHEADER STRING ATOM) "OPTIONAL M(OR CHANNEL LOCATIVE) Remove an interrupt handler or destroy an interrupt ON "VALUE" HANDLER (OR STRING ATOM) APPLICABLE FIX "OPTIONAL" (OR FIX PROCESS> (OR CHANNEL LOCATIVE> Turn on an interrupt and create an interrupt handler OPEN IIVALUEII (OR CHANNEL (FALSE STRING STRING FIX» "OPTIONAL" STRING "TUPLE" TUPLE Create and open an 1/0 channel OPEN-NR "VALUE" (OR CHANNEL (FALSE STRING STRING FIX» "OPTIONAL" STRING "TUPLE" TUPLE Create and open an I/O channel without changing file's reference date OR "VALUe- (OR FALSE ANY> -ARCS" LIST Logical: inclusive "or" of truthvalues. evaluated one at a time by the Subroutine Appendix 2 240 OR? "VALUE" ]> Bitwise inclusive "or" of words OVERFLOW "VALUE" ]» VECTOR CHARACTER Parse a string into an object (sections 7.6.6.2. 15.7.2. 17.1.3) PCODE "VALUE" PCODE STRING FIX Create pointer to pure RSUBR code PNAME "VALUE" STRING ATOM Return the print name of an atom as a distinct copy PRIMTYPE "VALUE" ATOM ANY Return the primitive data type of an object PRIMTYPE-C "VALUE" PRIMTYPE-C ATOM Get a "storage allocation code" for a TYPE PRINI "VALUE" ANY ANY ·OPTIONAL" CHANNEL Print an object on an 1/0 channel without format Appendix 2 241 PRINC "VALUE" ANY ANY "OPTIONAL" CHANNEL Print an object on an 1/0 channel without format or indicators PRINT "VALUE" ANY ANY "OPTIONAL" CHANNEL Print an object on an 1/0 channel PRINTS ·VALUE" «OR UVECTOR STORAGE> [REST ]> CHANNEL \Vrite binary information on an 1/0 channel PRINTSTRING "VALUE" FIX STRING "OPTIONAL" CHANNEL FIX Write contents of string on an 1/0 channel PRINTTYPE "VALUE" ATOM "OPTIONAL" Specify or return how a data type is printed PROCESS "VALUE" PROCESS APPLICABLE Create a new process with startup function PROG "VALUE" ANY "ARGS· Execute sequential expressions PURIFY IIVALUE" ANY IITUPLEII TUPLE Purify objects everywhere PUT "VALUE" ANY ANY ANY "OPTIONAL" ANY Associate a property with an item Appendix 2 242 PUT-oECL "VALUE" LOCO LOCO BITS "OPTIONAL" Set a masked fieJd in a word PUTPROP "VALUE" ANY ANY ANY "OPTIONAL" ANY a more general version of PUT PUTREST "VALUE" Replace the rest of a list QUIT "VALUEII 'IFALSE () Exit from MDL gracefuJly QUITTER ·VALUE- CHARACTER CHARACTER CHANNEL interrupt handler for AG and AS quit features QUOTE "VALUE" ANY "ARGSII LIST Return first argument unevaluated RANDOM "VALUE" FIX "OPTIONAL" FIX FIX Arithmetic: generate a uniform pseudo-random integer READ "VALUE" ANY "OPTIONAL" CHANNEL ANY [REST (PRIMTYPE WORD>]> CHANNEL ·OPTIONAL· ANY Read binary information from an 1/0 channel READCHR "VALUE" (OR CHARACTER FIX> "OPTIONAL" CHANNEL ANY Read one character from an 1/0 channel READSTRING "VALUE" FIX STRING "OPTIONAL" CHANNEL (OR FIX STRING> ANY Read into a string from an 1/0 channel REALTIMER "VALUE" (OR FIX FLOAT> (OR FIX FLOAT> Set interval for real-time interrupts REMOVE "VALUE" (OR ATOM FALSE> (OR ATOM STRING> "OPTIONAL" OBLIST Remove an atom from an oblist RENAME "VALUE" (OR •T (FALSE STRING FIX» "TUPLE" (TUPLE (OR STRING CHANNEL» Rename or delete a disk file REP "VALUE" ANY default for Read-Eval-Print loop REPEAT "VALUE" ANY "ARGS" (LIST -ATOM- LIST -DECL- ANY> Execute sequential expressions repeatedly RESET "VALUE" (OR CHANNEL (FALSE STRING STRING FIX» CHANNEL Reopen an 1/0 channel at iu beginning Appendix 2 244 REST "VALUE" STRUCTURED STRUCTURED "OPTIONAL" FIX Remove the first N elements from a structure and change to primitive data type RESTORE "VALUE" '"RESTORED" "OPTIONAL" STRING STRING STRING STRING Restore MDL's state from a file RESUME "VALUE" ANY ANY ·OPTIONAL· PROCESS Transfer execution to another process RESUMER "VALUE" (OR PROCESS FALSE) "OPTIONAL· PROCESS Return the process that most recently resumed this one RETRY "OPTIONAL" FRAME Retry a previous Subroutine caU from the error level RETURN ·VALUE" ANY ·OPTIONAL" ANY ACTIVATION Leave an activation block with a value RGLOC "VALUE" LOCR ATOM "OPTIONAL" (OR FALSE ANY) Return a locative to the global-value ce)) of an atom for pure-program use ROOT "VALUE" OBLIST Return the obJist containing primitives RSUBR "VALUE II RSUBR (VECTOR ATOM OECL> FIX Add entry point to RSUBR RSUBR-LINK ·VALUE" "OPTIONAL" Enable or disable the automatic linking feature RUNINT "VALUP ANY APPLICABLE "TUPLE" TUPLE Apply interrupt han~ler (for internal use only) RUNTIMER "VALUE" Set interval for run-time interrupt SAVE ·VALUE" I "SAVEO" "OPTIONAL" -STRING- -STRING- -STRING- -STRING- Write the entire state of MDL to a file SEND ·VALUE" STRING STRING ]» STRING STRING "OPTIONAL" FIX Send an IPC message (ITS only) SEND-WAIT "VALUE" IT STRING STRING ]» STRING STRING Send an IPC message and wait for it to be received (ITS only) SET "VALUP ANY ATOM ANY "OPTIONAL" Change the local value of an atom Appendix 2 ·OPTIONAL" FIX 246 SETG "VALUE" ANY ATOM ANY Change the global value of an atom SETLOC ·VALUE" ANY LOCATIVE ANY Change the contents pointed to by a locative SIN "VALUP FLOAT Arithmetic: sine SLEEP "VALUE" ANY "OPTIONAL" ANY Do nothing. interruptibly. the number of seconds indicated SNAME "VALUE" STRING "OPTIONAL" STRING Set or return the default directory name for new 1/0 channels SORT "VALUE· Sort elements of a structure SPECIAL-CHECK "VALUE" "OPTIONAL" Turn interpreter special-checking on or off SPECIAL-MODE "VALUE" "OPTIONAL" (OR 'SPECIAL 'UNSPECIAL> Set default speciality Appendix 2 247 SPNAME "VALUE" STRING ATOM Return the print name of an atom by sharing it SQRT "VALUE" FLOAT Arithmetic: square root SQUOTA "VALUEII Get the address of an internal interpreter symbol (for internal use only) STACKFORM "VALUE" ANY "ARGS" Apply a function to stacked arguments STATE "VALUE" ATOM PROCESS Return a process's current state STORE "VALUE" STORAGE «OR ]> Copy into new non-garbage-collected storage (archaic) (ITS only) STRCOMP ·VALUE" FIX Compare two character strings or two print names STRING "VALUE" STRING "TUPLE" ]> Create a string of explicit arguments STRUCTURED? "VALUE" ANY Predicate: is argument structured Appendix 2 248 SUBSTITUTE "VALUE" ANY ANY ANY Substitute one object for another everywhere SUBSTRUC "VALUEu (OR Copy part of a structure into another SUICIDE uVALUE" ANY ANY "OPTIONAL" PROCESS Cause the current process to die and resume another TAG "VALUE" TAG ATOM Create a tag in an activation block TERPRI ·VALUE- '/FALSE () ·OPTIONAL· CHANNEL Print a carriage-return and line-feed on an I/O channel TIME uVALUE" FLOAT "TUPLfU TUPLE Return the elapsed execution time in seconds TOP MVALUE" Turn echoing of characters typed on a console on or off Appendix 2 249 TUPLE "VALUP TUPLE "TUPLP TUPLE Create a tuple (vector on the control stack) of explicit arguments TYI "VALUE" CHARACTER "OPTIONAL" CHANNEL Input a character from a console immediately TYPE "VALUE" ATOM ANY Return the data type of an object TVPE-C "VALUE" TYPE-C ATOM "OPTIONAL" ATOM Get a data-type code for pure-program use TYPE-W "VALUE" TYPE-W ATOM "OPTIONAL" ATOM Declare the global values of atoms not to be constants UNPARSE ·VALUEu STRING ANY ·OPTIONALw FIX Create a string representation of an object UNWIND "VALUE" ANY -ARGSw Specify cleaning-up during non-local return UTYPE wVALUE" ATOM Return the data type of aU elements of a uniform vector UVECTOR "VALUE" UVECTOR "TUPLE" TUPLE Create a uniform vector of explicit arguments VALID-TYPE? -VALUE· ATOH Predicate: is an atom the name of a type VALRET "VALUE" FALSE STRING Pass a string to superior job using .VALUE UUO VALUE "VALUE" ANY ATOH "OPTIONAl" ]> Bitwise exclusive "or" of words Appendix 2 252 Appendix 3. Predefined Types On these two pages is a table showing each of MDL's predefined TYPEs. its primitive type if different. and various flags: S for STRUCTURED, E for EVALTYPE not QUOTE. and A for APPLICABLE. X means that an object of that TYPE cannot be CHTYPEd to and hence cannot be READ in (if attempted, CAN' T-CHTYPE-INTO ERROR is usual). B means that an object of that TYPE canllot be READ in (if attempted, STORAGE - TYPES-DIFFER ERROR is usuaJ), that instead it is built by the interpreter or CHTYPEd to by a program. and that its PRINTed representation makes it look as though its TYPEPRIM were different. X mean.s that an object of that TYPE is PRINTed using" notation and can be READ in only that way. TYPE TYPE PRIM SEA ACTIVATION ASOC ATOM BITS BYTES CHANNEL CHARACTER CLOSURE CODE DECL DISMISS ENVIRONMENT FALSE FIX FLOAT FORM FRAME FSUBR FUNCTION HANDLER IHEADER ILLEGAL INTERNAL LINK LIST LOCA FRAME comments X B sic: only one S WORD VECTOR WORD LIST UVECTOR LIST ATOM FRAME LIST WORD WORD LIST WORD LIST VECTOR VECTOR WORD WORD ATOM S S S S S X A can be returned by interrupt handler B S A S E S S S B AX A X X X X X "interrupt header" Garbage collector may put this on non-LEGAL? object. should not be seen by programs for console shorthand S E B locative to TUPLE Appendix 3 253 LOCAS LOCB LOCO LOCL LOCR LOCS LOCT LOCU LOCV LOSE MACRO OBLIST PCODE PICTURE PRIMTYPE-C PROCESS QUICK-ENTRY QUICK-RSUBR READA RSUBR RSUBR-ENTRY SEGMENT SPLICE STORAGE STRING SUBR TAG TEMPLATE TIME TUPLE TYPE-C TYPE-W UNBOUND UVECTOR VECTOR WORD locative to ASOC B locative to BYTES Yo locative to G/L VAL B locative to LIST Yo locative to GVAL in pure program B locative to STRING B locative to TEMPLATE B locative to UVECTOR B locative to VECTOR a place holder B WORD LIST UVECTOR WORD STORAGE WORD VECTOR VECTOR FRAME VECTOR VECTOR LIST LIST WORD VECTOR WORD WORD WORD WORD S S A X Yo "pure code" S % "primtype code" B S A % an RSUBR-ENTRY that has been QCALLed and RSUBR-LINKed S A "/B an RSUBR that has been QCALLed and RSUBR-LINKed X in eof slot during recursive READ via READ-TABLE S A Yo/B if code vector is pure/impure, respectively SA" S E for returning many things via READ-TABLE S If possible. use FREEZE SUBR instead. S S AX S X for non-local GOs S B The interpreter itself can't build one. See ref 1. used internally to identify FRAMEs B vector on the contro] stack S "type code" % "type word" X value of unassigned but bound ATOM. 85 seen by locatives "uniform vector" S E S E " Appendix 3 254 Appendix 4. Error Messages This is a list of aU ATOMs initiaJJy in the ERRORS OBLIST, in the left·hand column, and appropriate examples or elucidations, where necessary, in the right.hand column. ACCESS-FAILURE AlREADY-DEFINED-ERRET-NON-FAlSE-TO-REDEFINE APPlY-OR-STACKFORM-OF-FSUBR ARG-WRONG-TYPE ARGUHENT-OUT-OF-RANGE ATOM-ALREADY-THERE ATOM-NOT-TYPE-NAME-OR-SPECIAl-SYMBOl ATOM-ON-DIFFERENT-OBlIST ATTEHPT-TO-BREAK-OWN-SEQUENCE ATTEHPT-TO-CHANGE-MANIFEST-VARIABlE ATTEHPT-TO-ClOSE-TTY-CHANNEl ATTEHPT-TO-DEFER-UNOEFERABlE-INTERRUPT ATTEHPT-TO-GROW-VECTOR-TOO-MUCH ATTEHPT-TO-MUNG-ATOMS-PNAME ATTEHPT-TO-MUNG-PURE-STRUCTURE ATTEHPT-TO-SUICIDE-TO-SElF BAD-ARGUMENT-lIST BAD-ASCI I-CHARACTER BAD-BYTES-DECl BAD-CHANNEL BAD-CLAUSE ACCESS, RESTORE (TENEX only) First argument to APPLY, STACKFORM, MAPF/R doesn't EVAl all its arguments. $ (PARSE ")">$ (CHTYPE 1 SUBR>S attempt to GC-READ a structure containing a TEMPLATE whose TYPE does not exist SAVE attempt to RETRY a call to an RSUBRENTRY whose RSUBR cannot be found (SUBSTITUTE "T" T>$ (READ (CLOSE channel»$ AG (PRINTSTRING nn .OUTCHAN 1>$ (See section 21.10.13.) (ITS only) FREEZE ISTORAGE STORE (ITS only) I[MSTRING"]S I[(FRAME>]S RENAME DISPLAY (ITS only) DECL problem (OR> or (PRIMTYPE> in DECL (READSTRING "M>$ 256 ERROR-IN-COMPILED-CODE FILE-NOT-FOUND FILE-SYSTEM-ERROR FIRST-ARG-WRONG-TYPE FIRST-ELEMENT-OF-VECTOR-NOT-CODE FIRST-VECTOR-ELEMENT-NOT-REST-OR-A-FIX FRAME-NO-LONGER-EXISTS HANDLER-ALREADY-IN-USE HAS-EMPTY-BODY ILLEGAL ILLEGAL-ARGUMENT-BLOCK ILLEGAL-FRAME ILLEGAL-LOCATIVE ILLEGAL-SEGMENT ILLEGAL-TENEX-FILE-NAME INT-DEVICE-WRONG-TYPE-EVALUATION-RESULT RESTORE RSUBR in bad form. IDECL «X) S attempt to PRINT a TUPLE that no longer exists Third and later arguments to MAPF/R not STRUCTURED. (TENEX only) function for • INP input CHANNEL returned non-CHARACTER. in compiled code (unused) Interpreter couldn't open an ITS channel. bad object in argument LIST of Function IPC (ITS only) RESTORE INTERNAL-BACK-OR-TOP-OF-A-LIST INTERNAL-INTERRUPT ITS-CHANNELS-EXHAUSTED MEANINGLESS-PARAMETER-DECLARATION MESSAGE-TOO-BIG HUDDLE-VERSIONS-DIFFER NEGATIVE-ARGUMENT NIL-LIST-OF-OBLISTS $ $ in compiled code in compiled code zero-length STRING ZE3S$ $ BLOAT argument too large $ <- IE30 IE30>$ PICTURE-NOT-FOUND PROCESS-NOT-RESUMABLE PROCESS-NOT-RUNABLE-OR-RESUMABLE PURE-LOAD-FAILURE READER-SYNTAX-ERROR-ERRET-ANYTHING-TO-GO-ON RSUBR-ENTRY-UNLINKED Stack overflow while trying to expand 5tack: use RETRY. ERASE (ITS only) use of another PROCESS', FRAME. etc. Pure-code file disappeared. RSUBR-ENTRY whose RSUBR cannot be found RSUBR-IN-BAD-FORMAT RSUBR-LACKS-FIXUPS KEEP-FIXUPS 5hould have been true when RSUBR wa5 input. SECOND-ARG-WRONG-TYPE STORAGE-TYPES-DIFFER STRUCTURE-CONTAINS-UNDUMPABLE-TVPE SUBSTITUTE-TYPE-FOR-TYPE TEHPLATE-TYPE-NAME-NOT-OF-TYPE-TEMPlATE TEMPLATE-TYPE-VIOLATION THIRD-ARG-WRONG-TYPE TOO-FEW-ARGUMENTS-SUPPlIED TOO-MANY-ARGS-TO-SPECIAL-UNSPECIAl-DECl TOO-MANY-ARGUMENTS-SUPPlIED TOP-LEVEL-FRAME TYPE-ALREADY-EXISTS TYPE-MISMATCH TYPE-UNDEFINED TYPES-DIFFER-IN-STORAGE-OBJECT TYPES-DIFFER-IN-UNIFORM-VECTOR UNASSIGNED-VARIABLE UNATTACHED-PATH-NAME-SEPARATOR Appendix 4 S attempt to GC-READ a structure containing a TEMPLATE whose TYPE is defined but is not a TEMPLATE S NEWTYPE attempt to make a value violate its DECl ISTORAGE ![T <>]$ !-$ 258 UNBOUND-VARIABLE UNMATCHED UVECTOR-PUT-TYPE-VIOLATION ENDBlOCK with no matching BLOCK PUT. SETlOC. SUBSTRUC in compiled code VECTOR-LESS-THAN-2-ELEMENTS WRONG-DIRECTION-CHANNEL IOECL «X) (LIST [REST]>) (OPEN HMYFILP >$ (Mode missing or misspell.) WRONG-NUMBER-OF-ARGUMENTS Appendix 4 259 Appendix 5. Initial Settings The various switches and useful variables in MOL are initially set up with the following values: (ACTIVATE-CHARS (DECL-CHECK 1> "working-directory-> j-ITS only- Appendix 5 260 Referenoes Note: the form SYS.nn.mm refers to an internal PTD document. available from Programming Technology Division. Laboratory for Computer Science. M.I.T. 1. Berkowitz. Brian. and Chris Reeve. Templates in MOL, SYS.11.2i. 2. Daniels, Bruce. The MDL Assembler, SYS.11.07 S. Daniels. Bruce. and Ed Black. MOL Graphics User's Manual. SYS.11.04 4. CaUey. S. W.• Pre-loaded Pure MDL RSUBRs. SYS.ll.28 5. Hewitt. Carl, Planner: A Language for Manipulating Model$ and Proving Theorems in a Robot, Proc. IntI. joint Conf. on Artif. Intel .• May 1969 6. Moon, David A.• MACLISP Reference Manual, Laboratory for Computer Science, M.I.T. 7. Reeve. Chris. The MDL Compiler. SYS.1l.25 8. Ryan. Neal D., and Michael S. Broos. MDL Library System Guide. SYS.ll.15 References • 261 Topic Index Parenthesized words refer to other items in this index. arguments IIOPTIONAL" HTUPLE II "ARGS" (parameter) arithmetic + - • I ASS EXP LOG SIN COS ATAN MIN MAX RANDOM 07 17 ==7 L7 G7 L=7 G=7 N==7 array VECTOR UVECTOR TUPLE STRING BYTES TEMPLATE assignment SET SETG DEFINE DEFMAC ENVIRONMENT (value parameter binding) binding BOUND? GBOUND? ASSIGNED? GASSIGNED? LEGAL? (assignment value parameter) bits WORD BITS PUTBITS GETBITS BYTES ANDB ORB XORB EQVB block BIND PROG REPEAT OBLIST BLOCK ENDBLOCK OBLIST MOBLIST OBLIST7 !- boolean FALSE COND AND AND? OR OR? NOT (comparison) bugs (errors) caJJ FORM APPLY APPLICABLE? EVAL SEGMENT change PUT PUTPROP PUTBITS SET SETG character CHARACTER STRING ASCII PRINC READCHR NEXTCHR FLATSIZE LISTEN PARSE LPARSE UNPARSE circular PUTREST PUT LENGTH? FLATSIZE comma GVAL SETG comments ; FUNCTION ASSOCIATION comparison ==? N==? =? N=? G? L=? L? G=? O? I? MAX MIN STRCOMP FLATSIZE LENGTH? (boolean) conditional COND AND OR (boolean) Topic Index 262 concatenation SEGMENT STRING CONS console (tty) coroutine PROCESS data type TYPE TYPE? PRIMTYPE TYPE PRIM CHTYPE UTYPE CHUTYPE NEWTYPE PRINTTYPE APPLYTYPE EVALTYPE ALLTYPES VALID-TYPE? decimal do (loops execute call) dump SAVE (output) errors FRAME ARGS FUNCT ERROR ERRORS ERRET RETRY UNWIND escape \ .... G ....S execute EVAL APPLY QUOTE FSUBR "ARGS" (call) exit RETURN ACTIVATION (goto) file system FILECOPY FILE-LENGTH RENAME OPEN OPEN-NR CHANNEL NMI NMZ DEV SNAME goto GO TAG UNWIND PROG REPEAT AGAIN RETURN ACTIVATION -ACT- (loops) graphics DISPLAY ERASE PICTURE STORAGE IMAGE identifier ATOM PNAME SPNAME LINK LOOKUP INSERT REMOVE OBLIST SPECIAL (parameter value) if (conditional) indexing NTH GET PUT SACK TOP (loops) input READ READCHR NEXTCHR READS READSTRING READ-TABLE GC-READ OPEN ACCESS LOAD FLOAD RESTORE FILE-LENGTH RESET integer FIX (arithmetic) interrupts EVENT HANDLER ON OFF ENABLE DISABLE INT-LEVEL DISMISS INTERRUPT iteration (loops) Topic Index 263 leave (q uit) loading FLOAD SAVE RESTORE LOAD location (pointer) loops REPEAT PROG RETURN GO ACTIVATION AGAIN MAPF MAPR STACKFORH ILIST IVECTOR IUVECTOR ISTRING IBYTES IFORM macro % XX LINK READ-TABLE PARSE-TABLE DEFMAC EXPAND MACRO monitor IIREADII IIWRITE" multiprocessing PROCESS STATE RESUME SUICIDE RESUMER ME MAIN BREAK-SEQ octal It output PRINT PRINI PRINC PRINTB PRINTSTRING IMAGE GC-DUMP ECHOPAIR FLATSIZE SAVE TERPRI CRLF ACCESS RESET BUFOUT NETS parameter FUNCTION ATOM LVAL SET SPECIAL UNSPECIAL (identifier value) parentheses LIST parse PARSE LPARSE PARSE-TABLE UNPARSE period LVAL SET READ pointer LOCATIVE AT IN SETLOC LIST predicate (boolean) primitives SUBR FSUBR ROOT GVAL SETG procedure FUNCTION DEFINE DEFMAC GVAL CLOSURE quit AG AS QUIT VALRET LOGOUT RETURN (loops) real FLOAT (arithmetic) recursion (always assumed and built in) search MEMQ MEMBER =7 ==7 (comparison) sharing SEGMENT GROW SUBSTRUC Topic Index 264 sixbit UNAME JNAME SEND SEND-WAIT IPC-ON storage GC BLOAT BLOAT-STAT FREEZE TUPLE "GC" (structure) structure LIST VECTOR UVECTOR STRING BYTES TEMPLATE STRUCTURED? EMPTY? MONAD? LENGTH LENGTH? (concatenation) subroutine (procedure primitive) temporary "AUX" BIND PROG REPEAT text (character) trailer !- true (boolean) tty LISTEN AL AG A@ AD rubout ECHOPAIR TTYECHO TYI "BLOCKED" "UNBLOCKED" ACTIVATE-CHARS (character) unbinding (binding) value LVAL GVAL VALUE IN SET SETG ENVIRONMENT ASSIGNED? GASSIGNED? BOUND? GBOUND? "BIND" ACTIVATION "ACT" (parameter) RETURN (quit loops) OBLIST Topic Index 265 Token Index An underscored page number refers to a primary description; an unadorned page number refers to a secondary description. !" !$ !• 1- !-'FALSE () !. !< !) ![ !\ !] " 11)11 IIACT" IIARGS" IIAUX" "BIND" "BLOCKED" "CALL" "CHAR" "CLOCK" -DISPLAY" ·DIVERT-AGC· IIDSK" "ERROR" "EXTRA" "GC" "ILOPR" "INFERIOR" "INPUT" "INT" "IOC" "IPC" "MOL" "MPV" "MUDDLE" 63 17 65 24 139 142 65201 65201 65 53 6399 53 24 53 99 101 8285 8084 7985102104 8184 180 184 8185 182 184 100 113 184 192 101 107 186 7985 183 187 !§! "NAME" liNEr" "OPT" IIOPTIONAL" "PARITY" uPRINT" "PRINTS" uPRINTO· ·PURE" "QUOTE" "READ" "READS" "REALP "RUNP "SAVE" "STY" "SYSDOWN" "TUPLP "UNBLOCKED" UVALUE" "WRITE" 8285 113 7684 135 767984 135 187 100 100 100 187 135 100 104 182 185 206 100 186 186 107 111 185 7785104 136 185 135 185206 I 24 44 46 99 S f """ 24 152 152 ( 24 53 ) 24 53 III 2328 151 159 + 28151 101 112 187 186 199 101 187 107 16 24 97 112 182 183 185 24 55 24 31 28151 2J 24 32 Token Index 266 I 28151 O? 69 11 70 173 lSTEP 24 40 < 24 ==? 1:1 70206 7091 > 24 ABS ACCESS ACTIVATE-CHARS ACTIVATION AGAIN AGC-FLAG ALLTYPES AND AND? ANDB ANY APPLICABLE APPLICABLE? APPLY APPLYTYPE ARGS ASCII ASOC ASSIGNED? ASSOCIATIONS AT ATAN ATOM AVALUE 28 101109 182 82 150 181 190 201 8388150174 184 46 7274 183 7291 64161 125 125 72 4786 47 148174 63 123 168 213 7477 174 185 123 117 40 22 99 142 191 212 123 BACK BINARY BIND BITS 58210 165 82 88 160 BLOAT BLOAT-STAT BLOCK BOUND? BREAK-SEQ BREAKER BUFOUT BYTE-SIZE BYTES 184 193 194 141144 77 174 185 172 172 101110 115 64 ~ 6464208 CALLER CHAN LIST CHANNEL CHARACTER CHTYPE CHUTYPE CLOSE CLOSURE CODE COMMENT COND CONS COS CRLF 163 102 64 100 102 102 103 122 6399154 4547206 62211 102 86 163 122 73 58 40 99 DEAD DECL DECL-CHECK DECL? DEFAULT DEFINE DEFMAC DEMSIG DEV DISABLE DISMISS DISPLAY 169169 124218 133 135 140 156 198 101259 180 174 181 181 101 113 ECHOPAIR EMPTY? ENABLE ENDBLOCK ENTRY-LOC ENVIRONMENT EQVB 101112 146 72 180 141144 165 37 161 Token Index ~147 '" 267 ERASE ERRET ERROR ERRORS EVAL EVALTYPE EVENT EVLIN EVLOUT EXP EXPAND 101113 18 148 174 217 18 147 181 202 141147202 2047 81173 47 178 174 174 41 157 FALSE FBIN FILE-LENGTH FILECOPY FIX FLATSIZE FLOAD FLOAT FORM FRAME FREE FREE-RUN FREEZE FSAVE FSUBR 69 FUNCT function FUNCTION Function 166 101109 101110 21 2223 28 47 99 18 74 109 150 22 23 2733 5769 19 147 148 174 190 208 229 174 163 184 191 107 28 31 39 39 55 72 72 73 87 88 94 131147 150 148174 27 3539768082 82 G/LVAL G=? G? GASSIGNED? GBOUND? GC GC-OUMP GC-MON GC-READ GOECL GET GET-DECL 33148 70 70 77 185 77 132 184 192 101 106 196 195 101106 196 131 52121 134 GETBITS GETL GETPL GETPROP GLOC GO GROW GUNASSIGN GVAL 160 117 117 121 117 164 95174200 59184 32 31 39 41 117 168 190 191 203 HANDLER HANG 177 179 183 187 IBYTES 64 57 IFORM IHEADER 177 IllST 56200 ILLEGAL 190 IMAGE 101106 IN 116 118 119 INCHAN 102 146 INDICATOR 123 INITIAL 140259 INSERT 143 145 INT-LEVEL 181 INTERNAL 252 INTERRUPT 177 187 INTERRUPT-HANDLER 184 INTERRUPTS 141176 IPC-HANDLER 199 IPC-OFF 199 IPC-ON 199 ISTORAGE 233 ISTRING 5663 123 ITEM ITUPLE 78 IUVECTOR 56 IVECTOR 56 JNAME 197 KEEP-FIXUPS 166259 L-INS L-OUTS 146 146 Token Index 268 L=? L? LAST-OUT LEGAL? LENGTH LENGTH? LERR\ LINK LIST MEMBER MEMQ MIN MOB LIST MOD MONAD? MUDDLE LISTEN LLOC LMAP\ LOAD LOCA LOCAS LOCATIVE LOCATIVE? LOCB LOCO LOCL LOCR LOCS LOCT LOCU LOCV LOG LOGOUT LOOKUP LOSE LPARSE LPROG\ LVAL 70 70 146 78 82 95 116 118 175 190 209 5173 72 148 151 153 53 55 56 57 66 70 184 200 207 209 146 149 168 181 116 174 190 93 101108 117 117 125209 117 117 116 117 190209 117 164 117 117 117 117 41 197 142 565962 64 142 153 156 88 32 37 116 119 168 174 190 203 N==? N=? NBIN NETACC NETS NETSTATE NEWTYPE NEXT NEXTCHR NMl NMZ NOT NTH 70 71 165 114 115 114 46 132 164 184 190 123 95 98 101184 101 259 101 259 71 5186 OBLIST OBLIST? OFF ON OPEN OPEN-NR OR OR? ORB OUTCHAN OVERFLOW 99 138 140 146 168 191 139 179 179 100 104 110 112 113 102 7274 7291 161 47 102 146 151 MACRO MAIN MANIFEST MANIFEST? MAPF MAPLEAVE MAPR MAP RET MAPSTOP MAX ME 88156 172 173 192 131 132 8990 93 8990 92 93 28 173 192 PARSE PARSE-STRING PARSE-TABLE PCODE PICTURE PNAME PRIM TYPE PRIMTYPE-C PRINl PRINC PRINT 63 142 142 153 156 157 156 153 163 113 22 143212 44 164 98 101 111 99 101111 20 23 47 ~ 101 111 140 Token Index 71 71 28 139 144 28 72 107 141 -- • -, I' 1'11'111'1111\1 'I '1\1\\ ,~rl l\~lf~l '{l '1\' 1\\\\1 ,,\\ 1\ '1\\\ I' 269 3 9080 004 083 058 PRINTB 101105 PRINTSTRING 101105 PRINTTYPE 47 PROCESS 146 168 169 190 214 PROG 82 87200 PURE-PAGE-LOADER 184 PURIFY 107 191196 PUT 525467 120 PUT-DECL 134 PUTSITS 161 PUTPROP 120 PUTRE'ST 5768 QUICK-ENTRY QUICK-RSUBR QUIT QUITTER QUOTE 163253 163253 198 182 558081 RANDOM READ 29 20 22 98 101 122 139 142 153 184 153 154 101 105 95 98 101 104 111 112 184 101105 111 186 40259 142 144 101110 146 82 87200 101 102 110 111 52 54 73 126214 107 108 169 169 171172 173 150217 83 88 174 164 140144 147 162 164 191 147 165 READ-TABLE READA READS READCHR READSTRING REALTIMER REDEFINE REMOVE RENAME REP REPEAT RESET REST RESTORE RESUMABLE RESUME RESUMER RETRY RETURN RGLOC ROOT RSUBR RSUBR-ENTRY RSUBR-LINK rubout RUNABLE RUNINT RUNNING RUN TIMER 163 259 17 24 97 112 169 178 169 186 SAVE SEGMENT SEND SEND-WAIT SET SETG SETLOC SIN SLEEP SNAME SNH SORT SORTX SPECIAL SPECIAL-CHECK SPECIAL-MODE SPLICE SPNAHE SQRT SQUOTA STACKFORM STATE STORAGE STORE STRCOMP STRING STRUCTURED STRUCTURED? SUBR Subroutine SUBSTITUTE SUBS TRue SUICIDE 107 107 164 196 6570154 198 198 32 33 37 174 184 191 30 37 41 184 191 116 118 119 40 188 109 101 107 109 259 6071 60 127 156 190 218 134 128 134 154 143 40 247 94 169 191 247 71 53 56 63 64 99 154 208 125 72 28 31147 28 147 196 5254 173 T TAG TEMPLATE TERPRI 69 95190 5465214 74 99 101 Token Index 270 THIS-PROCESS TIME TO TOP TOP LEVEL TTYECHO TUPLE TYI TYPE TYPE-C TYPE-W TYPE? TYPEPRIH 173 173 197 UNAME UNASSIGN UNBOUND UNMANIFEST UNPARSE UNSPECIAL UNWIND UTYPE UVECTOR 197 33174 213253 132 64143 127216218 150218 62 53 55 56 61 64 200 208 212 VALID-TYPE? VAlRET VALUE VECTOR 46 198 33 124 174 53 55 56 61 184 200 207 211 WORD 159207 XORB 161 [ 24 53 \ 255399154 ] 24 53 .... .... @ .... 0 .... G .... l 110 59210 148 101112 146 7878 190209 101 112 184 185 20 44 72 92 190 206 213 164 164 72 45 4 106 17 24 17 24 17 24 1724 { 24 54 } 24 54 56 97 112 97 112 150 182 97 112 Token Index

Source Exif Data:
File Type                       : PDF
File Type Extension             : pdf
MIME Type                       : application/pdf
PDF Version                     : 1.6
Linearized                      : No
Create Date                     : 2016:04:07 15:48:05-04:00
Creator                         : Adobe Acrobat 9.5.5
Modify Date                     : 2016:04:07 16:10:36-04:00
XMP Toolkit                     : Adobe XMP Core 4.2.1-c043 52.372728, 2009/01/18-15:08:04
Metadata Date                   : 2016:04:07 16:10:36-04:00
Creator Tool                    : Adobe Acrobat 9.5.5
Format                          : application/pdf
Document ID                     : uuid:f4dd5a61-f6ac-4b13-a58a-bbf0f2cfe24b
Instance ID                     : uuid:cb5e685e-c289-49ca-a396-9a00a9c2b410
Producer                        : Adobe Acrobat 9.55 Paper Capture Plug-in
Page Count                      : 270
EXIF Metadata provided by EXIF.tools

Navigation menu