|The Revised Maclisp Manual||Page A-6|
The SETF Family
|SETF||Special Form||(SETF form1 val1 form2 val2 ...)|
This form allows one to assign a value to the cell which would be read by form. In the simplest case, a form is a symbol so the value, val, is placed in the symbol's value cell. In other words, (SETF sym val) is the same as (SETQ sym val). It is more general than SETQ, however, since form may be a non-symbol. In that case, the cell which would be accessed by form is changed to contain value. e.g., (SETF (CXR n exp) val) is the same as (PROGN (RPLACX n exp val) val).
SETF goes to a lot of trouble to return the last value even if the setting function, in this case RPLACX, does not return that value. Other Lisp dialects (e.g., Lisp Machine Lisp) may not guarantee the return value of SETF; Common Lisp is expected to change this, however, by explicitly defining the return value (compatibly with the Maclisp definition). However, some programmers still feel that the return value of an operation done for side-effect should not be relied upon. For these reasons, it may be desirable to adopt a coding style which does not make use of SETF's return value.
SETF initially knows how to invert the following types of operations: *LDB, *LOAD-BYTE, ARG, ARGS, ARRAYCALL, CXR, FIXNUM-IDENTITY, FLONUM-IDENTITY, GET, LDB, LOAD-BYTE, NTH, NTHCDR, PLIST, PROGN, SFA-GET, SYMEVAL, and C...R. To see what sorts of things SETF expands into, it may be convenient to just write a loop that displays some expansions. e.g.,
(DO ((FORM)) (NIL) (IF (NULL (SETQ FORM (PROGN (PRINT 'INPUT/:) (READ)))) (RETURN 'DONE)) (PRINC " => ") (PRIN1 (MACROEXPAND FORM))) INPUT: (SETF X VAL) => (SETQ X VAL) INPUT: (SETF (CAR X) VAL) => (PROGN (RPLACA X VAL) VAL) INPUT: (SETF (CDR X) VAL) => (PROGN (RPLACD X VAL) VAL) INPUT: (SETF (NTH 5 X) VAL) => (PROGN (RPLACA (NTHCDR 5 X) VAL) VAL) INPUT: (SETF (GET X Y) VAL) => (PROGN (PUTPROP X VAL Y)) INPUT: NIL DONE
If SETF does not find a SETF-X property on the operator of form, it will try to macroexpand form. If it can be expanded into something SETF knows about, the SETF expansion will still succeed. If the macroexpansion fails to produce a recognizable result, SETF will err.
SETF also tries to support proper left to right evaluation of its arguments (according to the textually apparent order of the forms in the user-specified form). For example, since STORE evaluates its arguments backwards,
can expand to something like
must expand to
to make sure that the FOO, BAR, and BAZ function calls, which may have side-effects, occur in the proper order.
Multics users must (%include setf) to get SETF.
|DEFSETF||Special Form||(DEFSETF fn (form val) retval invform)|
This is the appropriate way to provide information to SETF about how to do inversions on forms that it would not otherwise know about. It creates a SETF-X property on the function, fn, which is a function constructed from information given by the rest of its arguments. The second argument is destructured over and should have the same shape as the cdr of a simple (2 argument) SETF form. The third argument, retval, is T if the return value from the inversion of form is the same as val. The last argument, invform, is code which must return the appropriately inversion of form in order to store val. For example, the DEFSETF code for CXR would look like this:
Note: This definition is not compatible with the proposed Common Lisp definition of DEFSETF.
(DEFSETF GET-INFO ((GET-INFO TAG) VALUE) T `(PUT-INFO ,TAG ,VALUE)) => GET-INFO (MACROEXPAND '(SETF (GET-INFO 'FOO) 3)) => (PUT-INFO (QUOTE FOO) 3) (MACROEXPAND '(SETF (GET-INFO (A)) (B))) => (PUT-INFO (A) (B)) (DEFSETF FOO ((FOO IDX) VALUE) NIL `(SET-FOO ,IDX ,VALUE)) => FOO (MACROEXPAND '(SETF (FOO 3) 5)) => (PROGN (SET-FOO 3 5) 5) (MACROEXPAND '(SETF (FOO (A)) (B))) => ((LAMBDA (G0001 G0002) (SET-FOO G0001 G0002) G0002) (A) (B))
Multics users must (%include setf) to get DEFSETF.
Stack Handlers Using SETF
|PUSH||Special Form||(PUSH value form)|
|POP||Special Form||(POP form)|
(POP sym) is like (PROG1 (CAR sym) (SETQ sym (CDR sym))). Called on other forms, it uses SETF to attempt to access and pop whatever type of access is being attempted. For example, the expression (POP (GET 'FOO 'BAR)) can be used instead of (PUTPROP 'FOO (CDR (GET 'FOO 'BAR)) 'BAR).
Multics users must (%include other_other) to get POP.
(defvar *reg1* nil "Register 1") => *REG1* (push 'a *reg1*) => (A) (push 'b *reg1*) => (B A) (pop *reg1*) => B ;; Use in a program (defun list-fringe (tree) (let ((*fringe* nil)) (declare (special *fringe*)) (list-fringe-aux tree) (nreverse *fringe*))) => LIST-FRINGE (defun list-fringe-aux (tree) (declare (special *fringe*)) (cond ((atom tree) (push tree *fringe*)) (t (list-fringe-aux (car tree)) (list-fringe-aux (cdr tree))))) => LIST-FRINGE-AUX (list-fringe '((a . b) (c (d . e) . f) . g)) => (A B C D E F G)
|The Revised Maclisp Manual (Sunday Morning Edition)|
Published Sunday, December 16, 2007 06:17am EST, and updated Sunday, July 6, 2008.