From: Johan Ur Riise on
Another reason to hate loop:

(defun closures-from-loop (list)
(loop for el in list collect (lambda () (format t "~s~%" el))))

(loop for lambda in (closures-from-loop (list 1 2 3 'a 'b 'c))
do (funcall lambda))

prints, since heavy setq-ing is going on behind the scene curtain:

C
C
C
C
C
C
NIL



(defun closures-from-mapcar (list)
(mapcar (lambda (el) (lambda () (format t "~s~%" el))) list))

(mapcar #'funcall (closures-from-mapcar (list 1 2 3 'a 'b 'c)))

prints:

1
2
3
A
B
C
(NIL NIL NIL NIL NIL NIL)


Of course, when you find the bug you can also say:

(defun closures-from-loop (list)
(let (result)
(loop for el in list do
(let ((el el))
(push (lambda () (format t "~s~%" el)) result)))
(reverse result)))
From: Alberto Riva on
Johan Ur Riise wrote:
> Another reason to hate loop:
>
> (defun closures-from-loop (list)
> (loop for el in list collect (lambda () (format t "~s~%" el))))
>
> (loop for lambda in (closures-from-loop (list 1 2 3 'a 'b 'c))
> do (funcall lambda))
>
> prints, since heavy setq-ing is going on behind the scene curtain:
>
> C
> C
> C
> C
> C
> C

I don't see why you were expecting anything different. There's a single
'el' variable, and all closures are capturing it. Try doing this:

(let ((a 1))
(list (lambda () (print a))
(incf a)
(lambda () (print a))))

and then funcall the first and third elements of the resulting list: it
will print 2 in both cases. It doesn't have to do with LOOP.

> (defun closures-from-mapcar (list)
> (mapcar (lambda (el) (lambda () (format t "~s~%" el))) list))
>
> (mapcar #'funcall (closures-from-mapcar (list 1 2 3 'a 'b 'c)))

In this case, the "(lambda (el) ..." makes it clear that you're
introducing a new local variable every time.

> prints:
>
> 1
> 2
> 3
> A
> B
> C
> (NIL NIL NIL NIL NIL NIL)
>
>
> Of course, when you find the bug

Where did you see a bug?

> you can also say:
>
> (defun closures-from-loop (list)
> (let (result)
> (loop for el in list do
> (let ((el el))
> (push (lambda () (format t "~s~%" el)) result)))
> (reverse result)))

Which is exactly what your MAPCAR example does: create a new local
variable every time. You could have obtained the same result much more
simply this way:

(defun closures-from-loop (list)
(loop
for el in list
collect (let ((el2 el)) (lambda () (print el2)))))

Alberto
From: Johan Ur Riise on
Alberto Riva <ariva(a)nospam.ufl.edu> writes:

> Johan Ur Riise wrote:
>> Another reason to hate loop:
>>
>> (defun closures-from-loop (list)
>> (loop for el in list collect (lambda () (format t "~s~%" el))))
>>
>> (loop for lambda in (closures-from-loop (list 1 2 3 'a 'b 'c))
>> do (funcall lambda))
>>
>> prints, since heavy setq-ing is going on behind the scene curtain:
>>
>> C
>> C
>> C
>> C
>> C
>> C
>
> I don't see why you were expecting anything different. There's a
> single 'el' variable, and all closures are capturing it.
I didn't notice that fact, that's why I didn't expect it
>Try doing
> this:
>
> (let ((a 1))
> (list (lambda () (print a))
> (incf a)
> (lambda () (print a))))
>
> and then funcall the first and third elements of the resulting list:
> it will print 2 in both cases. It doesn't have to do with LOOP.
>
>> (defun closures-from-mapcar (list)
>> (mapcar (lambda (el) (lambda () (format t "~s~%" el))) list))
>>
>> (mapcar #'funcall (closures-from-mapcar (list 1 2 3 'a 'b 'c)))
>
> In this case, the "(lambda (el) ..." makes it clear that you're
> introducing a new local variable every time.
>
>> prints:
>>
>> 1
>> 2
>> 3
>> A
>> B
>> C
>> (NIL NIL NIL NIL NIL NIL)
>>
>>
>> Of course, when you find the bug
>
> Where did you see a bug?
>
>> you can also say:
>>
>> (defun closures-from-loop (list)
>> (let (result)
>> (loop for el in list do (let ((el el)) (push
>> (lambda () (format t "~s~%" el)) result)))
>> (reverse result)))
>
> Which is exactly what your MAPCAR example does: create a new local
> variable every time. You could have obtained the same result much more
> simply this way:
>
> (defun closures-from-loop (list)
> (loop
> for el in list
> collect (let ((el2 el)) (lambda () (print el2)))))
Better than the result/let/push/reverse solution, thanks, but also
collect (let ((el el)) (lambda () (print el)))
is nice

From: Barry Margolin on
In article <874oj30wrj.fsf(a)morr.riise-data.net>,
Johan Ur Riise <johan(a)riise-data.no> wrote:

> Another reason to hate loop:
>
> (defun closures-from-loop (list)
> (loop for el in list collect (lambda () (format t "~s~%" el))))
>
> (loop for lambda in (closures-from-loop (list 1 2 3 'a 'b 'c))
> do (funcall lambda))
>
> prints, since heavy setq-ing is going on behind the scene curtain:
>
> C
> C
> C
> C
> C
> C
> NIL

DO, DOLIST, and DOTIMES behave the same way. Why single out LOOP?

--
Barry Margolin, barmar(a)alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***
From: Tamas K Papp on
On Fri, 23 Apr 2010 03:42:56 +0200, Johan Ur Riise wrote:

> Another reason to hate loop:
>
> (defun closures-from-loop (list)
> (loop for el in list collect (lambda () (format t "~s~%" el))))
>
> (loop for lambda in (closures-from-loop (list 1 2 3 'a 'b 'c))
> do (funcall lambda))
>
> prints, since heavy setq-ing is going on behind the scene curtain:
>
> C
> C
> C
> C
> C
> C
> NIL
>
>
>
> (defun closures-from-mapcar (list)
> (mapcar (lambda (el) (lambda () (format t "~s~%" el))) list))
>
> (mapcar #'funcall (closures-from-mapcar (list 1 2 3 'a 'b 'c)))
>
> prints:
>
> 1
> 2
> 3
> A
> B
> C
> (NIL NIL NIL NIL NIL NIL)
>
>
> Of course, when you find the bug you can also say:
>
> (defun closures-from-loop (list)
> (let (result)
> (loop for el in list do
> (let ((el el))
> (push (lambda () (format t "~s~%" el)) result)))
> (reverse result)))

There is no bug, just a misunderstanding on your part.

Unlike Scheme, CL iteration constructs are not required to have a
different variable for each iteration of the loop body. There have
been many discussions on this topic, see eg
http://coding.derkeiler.com/Archive/Lisp/comp.lang.lisp/2008-10/msg01003.html

Just use

(defun closures-from-loop (list)
(loop for el in list collect
(let ((el el))
(lambda () (format t "~s~%" el)))))

Tamas

PS.: If a widely used construct in a language that was standardized 10+
years ago fails to conform to my expectations, I would read the manual
really carefully before calling it a bug.