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)