[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Access to lexical environment?



    Date: Sun, 8 Nov 87 21:12:26 EST
    From: futrell%corwin.ccs.northeastern.edu@relay.cs.net

    Creating new syntax using Lisp -- access to lexical environment?

    I am not sure how to properly develop simple extensions to Lisp
    syntax (using CL).  For example, suppose that I'd like to set up a
    way to create new things that has the following (familiar) syntax:

    (def-newthing fred (other-thing) ((n :initform (+ a b))
				      (ls :initform (rest ls1)))
				     ...etc.)

    Now a,b, and ls1 would typically be lexically scoped.  Given this, how
    do I define "def-newthing"?  If I use defun, it will attempt to
    evaluate (other-thing), and worse, ((n :initform ....)).  This won't
    work.  If I use defmacro, when I pull out an initform to evaluate, such
    as (+ a b), the defmacro won't allow me access to the lexical bindings
    of a and b.  eval has similar problems. I'm stuck.  Any ideas?  (There
    might be a lisp group to send this to, but we don't subscribe --
    anyway, I have confidence that I'll be enlightened on this by sluggers.)

The right place to ask questions like this is
COMMON-LISP@SAIL.STANFORD.EDU, and I've CCed my response there so that
other CLers can help out after I do.

For the most part, defining forms such as the above are expected to
appear at top-level.  However, the standard doesn't actually say that
(except maybe for DEFUN, but I think there is some contention about
this).

In any case, the way to do what you want is to create a lexical closure,
by expanding into a use of the FUNCTION special form, and when you
actually go to use the initial form you must FUNCALL it rather than
EVALing it.  Here's a simplified DEFSTRUCT that does this (I don't know
whether the standard Symbolics defstruct does it, and I'm not sure
whether the CL standard requires it):

(defmacro structure-defaults (symbol)
  `(get ,symbol 'simple-structure-defaults))

(defmacro def-simple-struct (name &body components)
  "Use as (def-simple-struct name (slot default) ...).
   Defines NAME-SLOTn macro accessors.  Use MAKE-SIMPLE-STRUCTURE
   to create an instance."
  (let ((component-names (mapcar #'car components))
	(component-defaults (mapcar #'cadr components)))
    `(progn
       ,@(loop for component in component-names
	       for i from 0
	       collect `(defmacro ,(intern (concatenate 'string (symbol-name name)
							"-" (symbol-name component)))
				  (object)
			  `(aref ,object ,',i)))
       (setf (structure-defaults ',name)
	     (list .,(loop for default in component-defaults
			   collect `(function
				      (lambda () ,default))))))))

(defun make-simple-structure (type &rest values)
  (let* ((defaults (structure-defaults type))
	 (size (length defaults))
	 (struct (make-array size)))
    (dotimes (i (length values))		;fill in specified values
      (setf (aref struct i) (nth i values)))
    (do ((i (length values) (1+ i)))
	((>= i size))
      (setf (aref struct i) (funcall (nth i defaults))))
    struct))

Another way to accomplish this would be for DEF-SIMPLE-STRUCT to include
in its expansion the definition of a function that makes the objects,
and to include the default forms in this expansion.  This would define a
function in the appropriate lexical environment (not that it is unclear
whether DEFUN can be used in this way -- the Symbolics interpreter
queries you if you try to use DEFUN in a non-null environment).  Here is
an example of the above that works this way:

(defmacro def-simple-struct (name &body components)
  "Use as (def-simple-struct name (slot default) ...).
   Defines NAME-SLOTn macro accessors, and make-NAME instance creator."
  (let ((component-names (mapcar #'car components))
	(component-defaults (mapcar #'cadr components)))
    `(progn
       ,@(loop for component in component-names
	       for i from 0
	       collect `(defmacro ,(intern (concatenate 'string (symbol-name name)
							"-" (symbol-name component)))
				  (object)
			  `(aref ,object ,',i)))
       (setf (symbol-function
	       ',(intern (concatenate 'string "MAKE-" (symbol-name name))))
	     #'(lambda (&key .,component-names)
		 (let ((struct (make-array ,(length component-names))))
		   ,@(loop for name in component-names
			   for default in component-defaults
			   for i from 0
			   collect `(setf (aref struct ,i)
					  (or ,name ,default)))
		   struct))))))

Both of these mechanisms are essentially equivalent.  The first version
creates one lexical closure per slot and stores a list of them in the
property list of the structure name symbol, and MAKE-SIMPLE-STRUCTURE
extracts and calls them.  The second creates a single lexical closure
that contains all the default forms and stores it in the function cell
of the creation function, and normal function evaluation invokes it.  If
you don't understand the macros (they were hard enough to write
correctly), run some examples through MEXP to see what they expand into.

                                                barmar