Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Loop curiosity

9 views
Skip to first unread message

Pascal Costanza

unread,
Jan 7, 2009, 5:11:10 PM1/7/09
to
Hm, the following seems to go into an endless loop in two Lisp
implementations I checked:

(loop finally (print 'screw-you))

Is this according to the spec?


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/

Kaz Kylheku

unread,
Jan 7, 2009, 5:50:51 PM1/7/09
to
On 2009-01-07, Pascal Costanza <p...@p-cos.net> wrote:
> Hm, the following seems to go into an endless loop in two Lisp
> implementations I checked:
>
> (loop finally (print 'screw-you))
>
> Is this according to the spec?

It's not a simple loop form because simple loop must have a body that has only
compound forms. If there is any atom among the forms, even if the first form is
a compound, it's an extended loop. So this is an extended loop with a FINALLY
clause that is never reached, since there is no termination test, nor any
explicit LOOP-FINISH or RETURN.

6.1.1.6 Order of Execution says: ``Execution is repeated until a clause
terminates the loop or until a return, go or throw form is encountered
which transfers control to a point outside the loop.''

I.e. the lack of any termination tests isn't treated as a positive termination
test, similarly to the way (OR) yields NIL. It's as if the set of termination
tests constitutes a logical disjunction which is false if empty.

William James

unread,
Jan 7, 2009, 7:51:38 PM1/7/09
to
Pascal Costanza wrote:

> Hm, the following seems to go into an endless loop in two Lisp
> implementations I checked:
>
> (loop finally (print 'screw-you))
>
> Is this according to the spec?
>
>
> Pascal

Ruby:

until p :screw_you ;end

Jens Teich

unread,
Jan 7, 2009, 8:05:49 PM1/7/09
to
"William James" <w_a_...@yahoo.com> writes:

> Ruby:
>
> until p :screw_you ;end

Ruby sorted:

(sort "until p :screw_you ;end" 'char<)
-> " :;_cdeeilnnoprstuuwy"

Jens

Rob Warnock

unread,
Jan 7, 2009, 11:16:08 PM1/7/09
to
Kaz Kylheku <kkyl...@gmail.com> wrote:
+---------------

| Pascal Costanza <p...@p-cos.net> wrote:
| > Hm, the following seems to go into an endless loop in two Lisp
| > implementations I checked:
| > (loop finally (print 'screw-you))
| > Is this according to the spec?
...
| It's not a simple loop form ... So this is an extended loop with a FINALLY

| clause that is never reached, since there is no termination test, nor any
| explicit LOOP-FINISH or RETURN.
|
| 6.1.1.6 Order of Execution says: ``Execution is repeated until a clause
| terminates the loop or until a return, go or throw form is encountered
| which transfers control to a point outside the loop.''
+---------------

CMUCL, for one, seems to agree:

cmu> (macroexpand
'(loop finally (print 'screw-you)))

(BLOCK NIL (ANSI-LOOP::LOOP-BODY NIL NIL NIL NIL ((PRINT 'SCREW-YOU))))
T
cmu> (macroexpand
'(ANSI-LOOP::LOOP-BODY NIL NIL NIL NIL ((PRINT 'SCREW-YOU)))
)

(TAGBODY
ANSI-LOOP::NEXT-LOOP
(GO ANSI-LOOP::NEXT-LOOP)
ANSI-LOOP::END-LOOP
(PRINT 'SCREW-YOU))
T
cmu>


-Rob

-----
Rob Warnock <rp...@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607

Kaz Kylheku

unread,
Jan 8, 2009, 12:47:25 AM1/8/09
to

Are you competing with yourself to see what is the shortest snippet of
Lisp that you can misunderstand?

Or don't you see how the above Ruby evaluates "p :screw you", whereas (print
'screw-you) is unreachable in the Lisp code?

Using the nil return value of p is poor programming style because the action of
printing is not related to logical falsehood. The return value of an i/o
function should only be used if it indicates the outcome of that operation. If
p always returned 5, would you use in place of the constant 5?

Pascal Costanza

unread,
Jan 8, 2009, 1:30:25 AM1/8/09
to

Indeed. Thanks for the clarification.

kod...@eurogaran.com

unread,
Jan 8, 2009, 5:37:43 AM1/8/09
to
> (loop finally (print 'screw-you))
Finally is normally used to return something (other than nil)
or to do something once the iteration activity has ended, which
does not happen in your case.
Your code in practice is tantamount to (loop)
though in theory it would print something at the end of eternity
(much like two paralel lines are said to join at infinity).

Pascal Costanza

unread,
Jan 8, 2009, 5:53:24 AM1/8/09
to

Yep, got that. ;)

I was under the impression that (loop finally ...) should just
immediately terminate because there is no loop clause there. Wrong
idea... ;)

kod...@eurogaran.com

unread,
Jan 8, 2009, 7:12:32 AM1/8/09
to
On Jan 8, 11:53 am, Pascal Costanza <p...@p-cos.net> wrote:
> I was under the impression that (loop finally ...) should just
> immediately terminate because there is no loop clause there. Wrong
> idea... ;)
>

Not so wrong. A clever enough compiler would see the loop is empty
and produce code that proceeds inmediatly to the print statement.
(In case the programmer really wanted an infinite loop instead of the
equivalent final result,
she/he could choose to leave the code interpreted.)
This means the compiler could produce an infinitely quick code, which
is conceptually interesting.
...and by the way completely opposite to what the SBCL compiler does,
which is to delete the finally clauses as being considered
unreacheable code.

Kaz Kylheku

unread,
Jan 8, 2009, 11:05:49 AM1/8/09
to

Whether or not parallel lines join depends on the choice of fifth postulate.

Maybe an analogous postulate is buried in imperative programming. :)

kod...@eurogaran.com

unread,
Jan 8, 2009, 11:35:36 AM1/8/09
to
> > Your code in practice is tantamount to (loop)
> > though in theory it would print something at the end of eternity
> > (much like two paralel lines are said to join at infinity).
>
> Whether or not parallel lines join depends on the choice of fifth postulate.
> Maybe an analogous postulate is buried in imperative programming. :)

There is indeed a non-resolved issue we touched here, namely the
programmer's purpose:
Is it only to achieve the result? or to achive it EXACTLY the way s/he
has programmed?
In other words: Should the compiler be allowed for instance to
eliminate the redundant code in
(setq a 3) (setq a 3) (setq a 3) (setq a 3) ?
This is a rather phylosophical -though very fundamental- question,
hardly ever touched on programming books.
My opinion is that it should be allowed to do so, since one has in
lisp the alternative of interpreted code -at least in principle- and
compiler graduation (optimization levels, etc.)

fugue88

unread,
Jan 14, 2009, 6:39:00 PM1/14/09
to
> In other words: Should the compiler be allowed for instance to
> eliminate the redundant code in
> (setq a 3) (setq a 3) (setq a 3) (setq a 3) ?

In general, no. You may be writing to a memory-mapped device, or
otherwise have unusual memory semantics.

Although, maybe in Lisp such a thing is okay. You couldn't SETQ to
memory involved with an FFI, could you?

-David

Pascal J. Bourguignon

unread,
Jan 14, 2009, 6:43:19 PM1/14/09
to
fugue88 <fug...@gmail.com> writes:

(defvar *counter* 0)
(defvar *memory* (make-array 16))
(defun (setf strange) (value)
(incf *counter*)
(setf (aref *memory* (random (length *memory*))) value))

(define-symbol-macro a (strange))
(progn (setq a 3) (setq a 3) (setq a 3) (setq a 3))

*memory* --> #(NIL NIL 3 NIL NIL NIL NIL 3 NIL 3 NIL NIL NIL NIL NIL 3) ; for example

--
__Pascal Bourguignon__

Kaz Kylheku

unread,
Jan 14, 2009, 8:08:38 PM1/14/09
to

A symbol expression can stand for another expression---symbol or
compound---thanks to symbol macros; because of this possiblity, SETQ is
required to handle assignment to forms other than symbols, just like SETF.

You can SETF to anything for which you can write a setf expander,
and you can SETQ any compound form that designates a setf-able place,
if you alias that place behind a symbol macro.

Barry Margolin

unread,
Jan 14, 2009, 9:15:01 PM1/14/09
to
In article <200901201...@gmail.com>,
Kaz Kylheku <kkyl...@gmail.com> wrote:

> On 2009-01-14, fugue88 <fug...@gmail.com> wrote:
> >> In other words: Should the compiler be allowed for instance to
> >> eliminate the redundant code in
> >> (setq a 3) (setq a 3) (setq a 3) (setq a 3) ?
> >
> > In general, no. You may be writing to a memory-mapped device, or
> > otherwise have unusual memory semantics.
> >
> > Although, maybe in Lisp such a thing is okay. You couldn't SETQ to
> > memory involved with an FFI, could you?
>
> A symbol expression can stand for another expression---symbol or
> compound---thanks to symbol macros; because of this possiblity, SETQ is
> required to handle assignment to forms other than symbols, just like SETF.

Presumably all such optimizations are done AFTER macro expansion, so
this doesn't really seem relevant.

--
Barry Margolin, bar...@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 ***

Pascal J. Bourguignon

unread,
Jan 15, 2009, 4:18:53 AM1/15/09
to
Barry Margolin <bar...@alum.mit.edu> writes:

> In article <200901201...@gmail.com>,
> Kaz Kylheku <kkyl...@gmail.com> wrote:
>
>> On 2009-01-14, fugue88 <fug...@gmail.com> wrote:
>> >> In other words: Should the compiler be allowed for instance to
>> >> eliminate the redundant code in
>> >> (setq a 3) (setq a 3) (setq a 3) (setq a 3) ?
>> >
>> > In general, no. You may be writing to a memory-mapped device, or
>> > otherwise have unusual memory semantics.
>> >
>> > Although, maybe in Lisp such a thing is okay. You couldn't SETQ to
>> > memory involved with an FFI, could you?
>>
>> A symbol expression can stand for another expression---symbol or
>> compound---thanks to symbol macros; because of this possiblity, SETQ is
>> required to handle assignment to forms other than symbols, just like SETF.
>
> Presumably all such optimizations are done AFTER macro expansion, so
> this doesn't really seem relevant.

Yes, sorry, with time we forgot the level we were considering.

We have to consider two situations:

- with a single thread, the memory stores can be collapsed, it will
make no difference.

- with several threads, or if the memory designated by A is not the
standard Common Lisp memory, but some strange device (eg. failing
RAM, or actual device register), then the semantics of the four
consecutive stores will probably be different from a single store.
But this situations is outside the scope of the Common Lisp standard,
and it's up to the implementation to know what it does.
I assume that there would be implementation specific declarations to
handle these cases:

(let (a
b
c)
(declare (system:device-register (#xff0110000a A))
(system:volatile b))
(create-thread (lambda () (loop (setf b (do-something-with b)))))
(setq a 3) (setq a 3) (setq a 3) (setq a 3) ; not collapsed
(setq b 3) (setq b 3) (setq b 3) (setq b 3) ; not collapsed
(setq c 1) (setq c 2) (setq c 3) (setq c 4)) ; collapsed
;; in the later specific case, C could even be ellided.


--
__Pascal Bourguignon__

0 new messages