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

Binding the same variable twice in a single form

    Date: Mon, 16 Apr 90 12:03:36 EDT
    From: traveler@Think.COM (William R. Swanson)

       Date: Fri, 13 Apr 90 16:47 EDT
       From: Barry Margolin <barmar@Think.COM>

	   Date: Fri, 13 Apr 90 11:47:11 EDT
	   From: William R. Swanson <traveler@Think.COM>

	      I can't think of any other reason why you would want to bind the same
	      variable twice in the same form, so the larger question should be moot.

	   Devils' advocate: How about the following (obviously kludgy) example?

	   (defun get-a-number ()
	     (let* ((number (read))
		    (number (if (numberp number) number 0)))
		(print number)))

	   It may not be nice programming style, but it works...

       Your example uses sequential binding, whose semantics in this case are
       obvious (although I'm not sure the ANSI draft specifically mentions
       this).  I was talking about parallel binding.

    Hmph! Okay, how about:

    (defun get-info-function ()
       (values unimportant may-be-used will-be-used))

    (defun process-all-info (function)
       (multiple-value-bind (a b c)
	    (funcall function)
       <code that uses a b & c>
    (defun process-important-info (function)
       (multiple-value-bind (value value other-value)
	    (funcall function)
       <code that uses both value and other-value>

    (process-important-info #'get-info-function)

    Of course, this example is starting to get a little strained, assuming
    as it does an attempt to create a generic function-calling interface.

You also didn't say *why* you want duplicate variable names in
process-important-info.  Why would you choose to write it that way
rather than

(defun process-important-info (function)
  (multiple-value-bind (dont-care value other-value)
      (funcall function)
    (declare (ignore dont-care))
    <code that uses both value and other-value>

    What's bothersome is that, on a Lispm at least, this code doesn't do
    the right thing. For example,

    (multiple-value-bind (a a b) (values 2 3 4) (print a) (print b))

    prints out 2 and 4, not 3 and 4 as one might expect.

Actually, it prints 2 and 4 when interpreted, and 3 and 4 when compiled.

The interpreter implements multiple-value-bind by evaluating the value
form, which pushes the values onto the stack, and then popping them into
the variable bindings.  So, it first pops 4 into B, then pops 3 into A,
and then pops 2 into A.

In compiled code, however, the variables simply map to stack frame
offsets, which are filled in by executing the values-producing code.
The compiler assigns variables to stack locations sequentially, so the
second A shadows the first one.

Since the language doesn't specify what "the right thing" is, Symbolics
is completely justified in implementing inconsistent semantics.