From: Tom Almy
Subject: A Common Lisp binding question...
Date: 
Message-ID: <9934@sail.LABS.TEK.COM>
I have a potential problem with Xlisp that I am proposing to the
general lisp group because it is really common lisp related.

***Background:

In Xlisp, the variable binding of let versus let* is handled as
follows. In both cases the variables are bound, and thus the local
binding environment is built, sequentially. In the case of the
"parallel binding" let, the enviroment used in evaluating the value
expressions is the environment prior to binding, while for
"sequential binding" let*, the currently building environment is
used. 

(Note that you can substitute prog or do for let in this discussion)


***The problem(?):

I added special variables to Xlisp. Since the special variable has
dynamic extent, when I execute:

(defparameter *x* 10)

(let ((*x* (1+ *x*)) (y (1+ *x*))) ...)

In the body of the let, y has the value 12, which is probably
incorrect. It has this value because the special variable *x* is
bound to 11 first, and it has dynamic extent.

I don't have Common Lisp, but I assume y actually gets bound to 11.


***The Question:

Do Common Lisp implemenations actually have to do the binding "in
parallel" or is there some trick involved (or am I way off base)? It
seems ugly and wasteful, but it looks like to achieve parallel
binding I would have to evaluate all the expressions first, saving
their values in a list, and then do the bindings at once, much like
progv (except progv binds all arguments dynamically).

As a side question, do Common Lisp users favor let* over let for
efficiency reasons?

Tom Almy
····@sail.labs.tek.com
Standard Disclaimers Apply









-- 
Tom Almy
····@sail.labs.tek.com
Standard Disclaimers Apply
From: Barry Margolin
Subject: Re: A Common Lisp binding question...
Date: 
Message-ID: <1991Jul19.214841.22607@Think.COM>
In article <····@sail.LABS.TEK.COM> ····@sail.LABS.TEK.COM (Tom Almy) writes:
>I added special variables to Xlisp. Since the special variable has
>dynamic extent, when I execute:
>
>(defparameter *x* 10)
>
>(let ((*x* (1+ *x*)) (y (1+ *x*))) ...)
>
>In the body of the let, y has the value 12, which is probably
>incorrect. It has this value because the special variable *x* is
>bound to 11 first, and it has dynamic extent.
>
>I don't have Common Lisp, but I assume y actually gets bound to 11.

Correct.

Your implementation probably also does the wrong thing if Y's value form
has side effects on *X*.

>Do Common Lisp implemenations actually have to do the binding "in
>parallel" or is there some trick involved (or am I way off base)?  It
>seems ugly and wasteful, but it looks like to achieve parallel
>binding I would have to evaluate all the expressions first, saving
>their values in a list, and then do the bindings at once, much like
>progv (except progv binds all arguments dynamically).

This is only really a problem in interpreters, and most Common Lisp
implementors don't spend too much time agonizing over the efficiency of the
interpreter -- performance of compiled code is generally higher priority.

However, there is a pretty simple trick you can use.  Note that the
following two forms are equivalent:

(let ((*x* <expr1>)
      (y <expr2>))
  <body>)

(let ((temp <expr1>)
      (y <expr2>))
  (let ((*x* temp))
    <body>))

as long as <body> doesn't have any free references to a variable named
TEMP.  In other words, you can put lexical bindings into the new
environment immediately, but special bindings require the value to be saved
away and the binding deferred.  So, you only need to pay for special
variable complexity when it's used.  The code for LET can look something
like this:

(defun eval-let (bindings body)
  (dolist (binding bindings)
    (let ((variable (first binding)) ; I'm simplifying the LET syntax processing
	  (value (second binding))
	  (new-environment environment)
	  (deferred-bindings nil))
      (if (special-variable-p variable environment)
	  (push (variable-binding variable
				  (eval-in-environment value environment))
		deferred-bindings)
	  (push (variable-binding variable
				  (eval-in-environment value environment))
		(environment-variable-bindings new-environment)))
      ;; Now add the deferred bindings
      (when deferred-bindings
	(setf (environment-variable-bindings new-environment)
	      (nconc deferred-bindings
		     (environment-variable-bindings new-environment))))
      (return-from eval-let
	(implicit-progn body environment)))))

>As a side question, do Common Lisp users favor let* over let for
>efficiency reasons?

In my experience, we normally use LET unless there is an actual dependency.
The "*" in the name is used to alert the reader that something funny may be
going on, and you don't want to do that when it isn't.

LET is a relatively recent addition to the Lisp language; it was added to
MacLisp in the late 70's, and was just a macro that expanded into a call to
an anonymous lambda expression:

	(let ((var1 val1) (var2 val2)) body)
 ==>	((lambda (var1 var2) body) val1 val2)

I think LET* was added sometime after that, as a macro that expanded into
nested LETs:

	(let* ((var1 val1) (var2 val2)) body)
 ==>	(let ((var1 val1))
	  (let ((var2 val2))
	    body))
 ==>	((lambda (var1)
	    ((lambda (var2) body) val2))
	 val1)

Until implementations were taught to recognize LET and LET* directly, LET
was probably more efficient than LET*.

-- 
Barry Margolin, Thinking Machines Corp.

······@think.com
{uunet,harvard}!think!barmar