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


- - - - Begin forwarded message - - - -
Mail-From: ARPANET host CMU-20C received by CMU-10A at 11-Sep-82 23:02:40-EDT
Mail-from: ARPANET site SU-SCORE rcvd at 11-Sep-82 1558-EDT
Date: Sat, 11 Sep 1982 16:22:00 -0000
From: Andy Freeman <CSD.FREEMAN at SU-SCORE>
Subject: let & let*
To: steele at CMU-20C

I've been looking at the let/let* semantics.  The way that I understand it,
let* is essentially a "recursive" let.  They could be defined by (although
this ignores the extended syntax for the elements of the argument list of
lets, does no error checking, and doesn't handle declarations)

(defmacro let (args &body body)
  `((lambda ,(mapcar (function (lambda (arg)
				 (cond ((atom arg) arg)
				       (t (car arg)))))
      ,@ body)
    ,(mapcar (function (lambda (arg)
			 (cond ((atom arg) nil)
			       (t (cadr arg)))))

(defmacro let* (args &body body)
  (cond (args `(let (,(car args))
		 ,@ (cond ((cdr args)
			   `((let* ,(cdr args) ,@ body)))
			  (t body))))
	(t `(progn ,@ body)))).

The problem with this is that it is too sequential.  The reason for using
let* is that you want to put all of the variables in the same place, but to
write a form where there are both parallel and sequential bindings, you have
to revert to a nested form.

The only way that I can think of to handle this is to extend the semantics
of let by using markers, either separator tokens in the binding object list
or by a third element in a binding object that should be bound after the
previous element.  The separator token definitions of let/let* are on the
next page.
All bindings after a token (&sequential or &parallel) are nested.
The difference between the two is that each binding after &sequential
is nested while &parallel does them in parallel.  (Another possibility
is to make &parallel a nop if the bindings are already being done in
parallel so that &parallel only makes sense after a &sequential.)

I like the token syntax better.  It also can be used for do, prog,
and ALL lambda lists.  (In the latter, it only makes sense for the
&aux and &optional args, and then only for the default values.)

(defmacro let (args &body body)
  (cond ((null args) `(progn ,@ body))
	((eq (car args) '&sequential)
	 (cond ((memq (cadr args) '(&sequential &parallel))
		(comment ignore &sequential if followed by keyword)
		`(let ,(cdr args)
		   ,@ body))
	       (t (comment do one binding then nest)
		  (setq args (cond ((atom (cadr args))
				    (cons (list (cadr args) nil) (cddr args)))
				   (t (cdr args))))
		  `((lambda (,(caar args))
		      ,@ (cond ((cdr args)
				`((let ,(cons '&sequential (cdr args))
				    ,@ body)))
			       (t body)))
		    ,(cadar args)))))
	((eq (car args) '&parallel)
	 (comment &parallel just gets ignored)
	 '(let , (cdr args)
	    ,@ body))
	(t (do ((arg-list (mapcar (function (lambda (arg)
				    (cond ((memq arg '(&sequential &parallel))
					  ((atom arg) (list arg nil))
					  (t arg))))
			  (cdr arg-list))
		(syms nil (cons (caar arg-list) syms))
		(vals nil (cons (cadar arg-list) vals)))
	       ((or (null arg-list)
		    (memq (car arg-list) '(&sequential &parallel)))
		`((lambda ,(nreverse syms)
		    ,@ (cond (arg-list `((let ,arg-list ,@ body)))
			     (t body)))
		  ,@ (nreverse vals)))))))

(defmacro let* (args &body body)
  `(let , (cons '&sequential args)
     ,@ body)).

- - - - End forwarded message - - - -