From: Kent M Pitman
Subject: Re: something *very* strange (to *me*). Please help!
Date: 
Message-ID: <sfwsouidsz1.fsf@world.std.com>
In article <·············@WINTERMUTE.eagle> SDS <···········@cctrading.com> writes:

> Sorry about such a non-descriptive subject. I really have no idea about
> what's going on! In CLISP:
> (let ((l (do ((i 0 (1+ i)) r) ((= i 3) (nreverse r))
>            (push #'(lambda () i) r))))
>   (mapcar #'funcall l)) ==> (3 3 3)
> I would expect (0 1 2)

Based on what promise in what documentation?

> What is going on?

The right thing is happening.  Hence, you're asking us to diagnose
you, not the code.  That's tricky.  My guess is that you have a
made-up intution about a promise of behavior that you can't find in
any document.  I bet you think somewhere you were told that DO would
make fresh bindings of i on each iteration, but I know of no such
promise.

It's of course slightly possible that you can find that promise
because some non-authoritative document you're using is buggy, but you
haven't cited a reference.  I recommend ALWAYS going to the
documentation first and doing the due diligence of making sure the
operators are defined the way you think before you go to the newsgroup
and ask questions of this kind.  Then you can say "section such-and-so
made me think that x would happen but y happened instead".  

In this case, consulting the HyperSpec would tell you that i gets
bound at the start of the DO and gets assigned at each step.  You
might in your head be thinking DO was just syntactic sugar for a tail
recursion, but it is not documented that way and a trip to the
documentation would tell you that.

Also, I assume you know that LAMBDA does not capture values of
variables, it captures references, so each closure created in your
loop contains a captured reference to the same variable, and that
variable's final value is 3--as tested by your (= i 3) test.

> How do I get the desired result?

If you want a fresh binding, you need simply write one explicitly.

  (let ((l (do ((i 0 (1+ i)) r) ((= i 3) (nreverse r))
             (let ((i i)) (push #'(lambda () i) r)))))
    (mapcar #'funcall l))
  ==> (0 1 2)

> (declaring i special doesn't change anything)

Well, it's the completely wrong thing to do here but it SHOULD
have an effect.  If I is special, as in

  (let ((l (do ((i 0 (1+ i)) r) ((= i 3) (nreverse r))
             (declare (special i))
             (push #'(lambda () i) r))))
    (mapcar #'funcall l))

you WON'T get a closure over i.  SPECIAL variables are always accessed
dynamically and there is no visible binding for i around the funcall
so you're dynamically accessing an unbound variable ... unless you
just happen to have independently setq'd i to 3, completely confusing
yourself for unrelated reasons.  If there were an outer binding for i,
all three calls to the function outside of the scope of the inner i
are going to return the outer value:

  (let ((i 7)) (declare (special i))
    (let ((l (do ((i 0 (1+ i)) r) ((= i 3) (nreverse r))
               (declare (special i))
               (push #'(lambda () i) r))))
      (mapcar #'funcall l)))
  ==> (7 7 7)

From: Kent M Pitman
Subject: Re: something *very* strange (to *me*). Please help!
Date: 
Message-ID: <sfw90w9ayfc.fsf@world.std.com>
Someone sent me mail saying my reply to SDS seemed overly sarcastic.
That wasn't my intent--to the extent that it did, I apologize.

Regardless of the tone, I hope the technical part of my response 
answered the question.

And I still believe, to the extent possible, that it's useful to cite
a reference to a specific passage of a piece of documentation which
appears to make a promise that is found to be broken.  The exercise of
trying to do so may solve the problem before it is ever mailed.  And if
not, the inclusion of the documentation will tend to focus the report
more clearly on the one or two of many possible issues that the person
making the report is having trouble with.
From: Rob Warnock
Subject: Re: something *very* strange (to *me*). Please help!
Date: 
Message-ID: <61cbp9$17bi3@fido.asd.sgi.com>
Kent M Pitman <······@world.std.com> wrote:
+---------------
| ...i gets bound at the start of the DO and gets assigned at each step.
| You might in your head be thinking DO was just syntactic sugar for a tail
| recursion, but it is not documented that way...
+---------------

However, those of us who are mainly Scheme programmers (and who lurk here
to pick up general higher-level tips) might indeed make that assumption,
especially since a naive translation to Scheme does give the original
poster's "expected" result:

	> (let ((l (do ((i 0 (1+ i))
		        (r '()))
		       ((= i 3) (reverse r)) 
		     (set! r (cons (lambda () i) r))))) 
	    (map (lambda (x) (x)) l))
	(0 1 2)
	>

Just another reminder that there are subtle gotchas between Scheme & CL...

Thanks,


-Rob

-----
Rob Warnock, 7L-551		····@sgi.com   http://reality.sgi.com/rpw3/
Silicon Graphics, Inc.		Phone: 650-933-1673 [New area code!]
2011 N. Shoreline Blvd.		FAX: 650-933-4392
Mountain View, CA  94043	PP-ASEL-IA
From: Kent M Pitman
Subject: Re: something *very* strange (to *me*). Please help!
Date: 
Message-ID: <sfwg1qd1h1d.fsf@world.std.com>
····@rigden.engr.sgi.com (Rob Warnock) writes:

> Kent M Pitman <······@world.std.com> wrote:
> +---------------
> | ...i gets bound at the start of the DO and gets assigned at each step.
> | You might in your head be thinking DO was just syntactic sugar for a tail
> | recursion, but it is not documented that way...
> +---------------
> 
> However, those of us who are mainly Scheme programmers (and who lurk here
> to pick up general higher-level tips) might indeed make that assumption,
> especially since a naive translation to Scheme does give the original
> poster's "expected" result: [...]
>
> Just another reminder that there are subtle gotchas between Scheme & CL...

Well, indeed.  I guess that had been the point of what I was getting at
in my original post.  If you use the Scheme spec as your source of ideas
for how to deal with CL, you'll lose.  By asking for the documentation ref,
I was trying to ferret out an admission of this sort.

Just another reminder that there are subtle gotchas between any two
languages that have never purported to have equivalent semantics.

To my knowledge, there is no operator name that has the same semantics
in all dialects of Lisp--the diversity of meanings is often amazing.
So while it may be fun to try to pretend that name similarity means
something, one should always in production situations expect that
crutch to break down ... probably at some bad time.  There is no
substitute for knowing the real definition--whether from the original
source or a reliable substitute teacher (or teaching text).