5/22/74 JONL
Brief Synopsis: for LISP 838 and greater
1) SUSPEND - new function, LSUBR of 0 or 1 arguments, like MACDMP,
but can continue where the computation left off, rather than
restarting at top level.
2) MUNKAM is the inverse of MAKNUM. On the PDP10 system, is pretty
much the same as CDR, except that the argument is required to be
a fixnum, and the COMPLRs will open-code MUNKAM.
3) RANDOM will accept two arguments, as a means of "seeding" it.
Also, a slight deficiency has been noted.
4) A programmable features list for (STATUS FEATURE foo), and an aid,
(SSTATUS LINMODE T), for systems with line rather than character
oriented TTY input.
5) ARRAYCALL, SUBRCALL, AND LSUBRCALL are now all FSUBRs, and
take an extra argument to aid NCOMPLR in open-coding these
applications. Disregard any previous notes on these functions,
and note well below.
6) Examples of some particularly useful lisp macros, especially for
users of ARRAYCALL, SUBRCALL, and LSUBRCALL.
7) The compiler declaration ARRAY* has been extended to allow
information about the ranges of indices. Also, NCOMPLR now uses
its own private obarray when compiling a file, in addition to its
own private readtable.
--------------------------------------------------------------------
1) (SUSPEND) may be executed at any point in a computation, and
control will be returned to the LISP's superior [DDT or monitor].
Accumulators, push-down stacks, and other variables will be saved,
and its starting address will be set so that if the job be dumped
on dsk, and reloaded at some future time, starting it again will
cause it to resume where the computation and continue just after
the call to SUSPEND. One limitation: if any input-output devices
are in use other than the TTY, SUSPEND will error with a fail-act.
(SUSPEND s), like (MACDMP s), passes the characters of the symbol
s to the superior job as a valret string.
2) By "inverse of MAKNUM", the following is meant:
(EQ X (MUNKAM (MAKNUM X))) evaluates to T for all X. Shortly,
the MULTICS implementation will have a reasonable version of
MAKNUM and MUNKAM implemented, so that one may write a
hash-coder-on-EQ for s-expressions. Previous notes in LISP ARCHIV
have given examples on how to hash an s-expression on EQUAL. By
replacing "(\ (SXHASH X) 777))" with "(\ (MAKNUM X) 777)" one
will have a hasher on EQ.
3) By "seeding" RANDOM, one may obtain a variety of starting points,
corresponding to the various internal states of the two-word state
register. Any two successive outputs of RANDOM will do as the
two words for a seed; for example, (SETQ X (RANDOM) Y (RANDOM))
will preserve the current state of the random number generator
in the variables X and Y, and the state may be restored to that
state [after, possibly, further usage of RANDOM] by (RANDOM X Y).
Users of RANDOM should take note of a fact which Bill Gosper
ferreted out of Knuth - this random number genertor flunks the
3-way serial test. That is, if triplets of "random" numbers
<x[n], y[n], z[n]> are generated by clumping together the
3n, 3n+1, and 3n+2 outputs of RANDOM, then there will be an
interdependency among the triples such that half of all triples
will be missed - not particularly good for picking "random"
points in 3-space. One way out of this bind is simply
to use only every other output of RANDOM in generting the triples.
4) As described in previous notes, there is an internal list of
"features" describing which of the various MACLISP options are
actually available in the LISP being used, and which time-sharing
system it is running under. Now the user can create his own
feature names and add, or delete, from this list at will.
(STATUS FEATURE FOO)
is non-NIL if and only if FOO is on the features list;
(SSTATUS FEATURE FOO)
will add FOO to the features list, and
(SSTATUS NOFEATURE FOO)
will delete it.
(SSTATUS LINMODE T)
tells the time-sharing system not to activate your job while
waiting for TTY input until a carriage-return is typed. For the
TOPS-10 system, it means the basic input instruction is INCHRW
instead of INCHR, and that the time-sharing system will handle
rubouts until the carriage-return is typed. Since the ITS system
does not handle rubouts under any circumstances, many users want
a mode under which the rubout handler of the MACLISP reader will
be effective on a line-by-line basis, and under which no read
reading is done until a carriage-return is typed. This can be
achieved as follows:
(SSTATUS SYNTAX 13. 501540)
;makes <cr> an invisible force-feed char
(SSTATUS LINMODE T)
;tells ITS to sleep until <cr> or <rubout>
(SSTATUS TTYREAD T)
;tells LISP's reader to forget about looking for
;"balanced" s-expressions before actually gobbling
;up characters from the TTY
It is worthwile to note here that the "force-feed" option on <cr>,
and the TTYREAD option are properties of LISP's readtable, while
the LINMODE option is a property of the LISP's relation with the
time-sharing system.
5) There has long been a certain ambiguity in LISP with respect to
the meaning of an atomic function. For (FOO X Y), most LISP
systems will scan the property list of FOO to see if there are
any functional properties [such as SUBR, EXPR, etc], and if so,
use the first one found as the functional-interpretation of FOO;
if none are found, then the value of FOO as a variable is picked up,
and the function-hunting process continues recursively. Some other
systems always pick up the variable value, and never resort to
storing subroutine addresses, or LAMBDA forms, as "properties" on
a property list. The function FUNCALL was implemented as a means
of directing the MACLISP evaluator first to the variable
value as function rather than starting out on the functional
properties. Thus, (FUNCALL FOO X Y) is equivalent to
(APPLY FOO (LIST X Y)). However, FUNCALL is essentially an
interpretation, and the COMPLR can not open-code the dispatch to
the function of interest unless more is known about its calling
conventions. For this reason, ARRAYCALL, LSUBRCALL, and SUBRCALL
have been implemented as FSUBRs. The general forms are
(ARRAYCALL type ap i1 . . . in)
(LSUBRCALL type lfun arg1 . . . argn)
(SUBRCALL type fun arg1 . . . argn)
type should be either "FIXNUM", "FLONUM", "T", or "NIL", depending
on the resulting type of value returned by the function [or on the
array type, in the case of ARRAYCALL. Both T-type and NIL-type
arrays may be specified by NIL here, which simply means
"s-expression" array rather than "numeric" array.]. ap should
evaluate to an array pointer such as created by *ARRAY
[(1) returned by *ARRAY if its first argument is NIL, or (2) put
on the property list of the given non-NIL symbol]. lfun should
evaluate to an LSUBR pointer, which on the PDP10 systems is obtained
only by doing (GET 'FOO 'LSUBR) for some LSUBR FOO; similarly, fun
should evaluate to a SUBR pointer. The reason the type argument is
required is that NCOMPLR can generate optimal code for these
applications. Versions of NCOMPLR greater than 454 will code these three
functions open [COMPLR will not be nearly so optimal in its codings of these
three. Neither will COMPLR actually open-code array references.].
in the case of SUBRCALL and LSUBRCALL, the type info is mainly
an aid to NCOMPLR, and type NIL could always be used as default;
however, using type FIXNUM or FLONUM where NIL is required
will result in wrong code. For ARRAYCALL, it will be necessary
always to have the correct type info since wrong code would result
from any kind of type mismatch.
EXAMPLE: suppose you have done
(SETQ BARODD (ARRAY NIL FIXNUM N) BAREVEN (ARRAY NIL FIXNUM N))
Now at this point, both BARODD and BAREVEN hold as value an array
pointer. They would have ARRAY properties on their property list
if, for example, (ARRAY BARODD FIXNUM N) had been done instead.
Then the following will fill BARODD with the first N odd integers,
and BAREVEN with the first N even integers:
(DO I 1 (1+ I) (> I (* 2 N))
(STORE (ARRAYCALL FIXNUM
(COND ((ODDP I) BARODD) (BAREVEN))
(/ (1- I) 2))
I))
6) EXAMPLE USING MACROS FOR SIMPLIFIED SYNTAX:
Assuming BARODD and BAREVEN as above [that is, variables that
have been set to some array pointer], let us define two macros
(DEFUN MACRO BO (X)
(SUBST (CADR X) 'INDEX '(ARRAYCALL FIXNUM BARODD INDEX)))
(DEFUN MACRO BE (X)
(SUBST (CADR X) 'INDEX '(ARRAYCALL FIXNUM BAREVEN INDEX)))
Then we could fill BARODD and BAREVEN as follows:
(DO J 1 (1+ J) (> J N) (STORE (BE (1- J)) (* 2 J)))
(DO J 0 (1+ J) (NOT (< J N)) (STORE (BO J) (1+ (* 2 J))))
Admittedly, this saves a lot of typing. But suppose you have a
host of such array variables that you would like to abbreviate
with such a MACRO. Typing in all the macro definitions could be
tediously repetitive. Consider the following macro-defining macro,
and some of its uses:
(DEFUN MACRO ABBA (Y)
(SUBLIS (LIST (CONS 'SHORT (CADR Y))
(CONS 'LONG (CADDR Y))
(CONS 'TYPE (CADDR Y)))
'(DEFUN MACRO SHORT (X)
(SUBST (CDR X)
'INDEXLIST
'(ARRAYCALL TYPE LONG . INDEXLIST)))))
Now we might use ABBA to produce the macro for BE, but note that
the form of the macro is slightly different - the main body of the
macro output appears to be a dotted-list rather than a standard
list. This is so that arrays of varying numbers of dimensions may
have their abbreviations defined by the same super-macro.
(ABBA BO BARODD FIXNUM)
expands into
(DEFUN MACRO BO (X)
(SUBST (CDR X)
'INDEXLIST
'(ARRAYCALL FIXNUM BARODD . INDEXLIST)))
which then causes the appropriate macro definition for BO. As
you would expect, then, (BO J) expands into
(ARRYACALL FIXNUM BARODD J)
But consider the two-dimensional hash array HASH defined as
(SETQ HASH (ARRAY NIL T 37 37))
Then (ABBA HA HASH T) defines HA so that (HA 3 (+ N 2)) expands
into (ARRAYCALL T HASH 3 (+ N 2))
Guy Steele has accumulated a file of sophisticated macros and
macro-defining macros, and the interested may consult with him
about them.
7) In order to get maximal speed from open-compiled array references,
you may inform NCOMPLR of the actual ranges of the array
indices. Thus a two-dimensional array of FIXNUMS, size 3 by 4,
could be declared by:
(ARRAY* (FIXNUM (CIR 3 4)))
Even partial information will be useful; a NIL or ? in index
positions will indicate that no information is available about that
particular dimension. For example, to add to the above declaration
that for a two-dimensional array in which only the column dimension
is known in advance, one could say:
(ARRAY* (FIXNUM (CORL ? 4) (CIR 3 4)))
The previous syntax for ARRAY* is still available, and one
should note that the following two forms both convey the same
information:
(ARRAY* (NOTYPE DXA 1 CIR 2))
(ARRAY* (NOTYPE (DXA NIL) (CIR ? ?)))
Also, NCOMPLR now uses its own private obarray when compiling a
file, in addition to its own private readtable; they are contained,
respectively, in the two global variables COBARRAY and CREADTABLE.
If you have the practice of escaping to top-level LISP, and
loading in some of your own functions, be sure to do this stuff
under the correct obarray and readtable. E.G., you might do
((LAMBDA (OBARRAY READTABLE)
(FASLOAD MY FUNS DSK LOSER))
COBARRAY CREADTABLE)