From: Rob St. Amant
Subject: Nested iteration macro question
Date: 
Message-ID: <f7qmtc$ka2$1@blackhelicopter.databasix.com>
In some code I'm writing, I use a nested iteration in several places:

(loop for (x . rest) on list do
     (loop for y in rest do
	  <body>))

At first glance it seemed reasonable to wrap this up in a
straightforward macro, but then I realized that if I ever use the more
abstract form and want to exit it non-locally, RETURN wouldn't behave
"properly", exiting only the inner loop.  So I did this:

(defmacro do-unordered-pairs ((x y list &optional (return-value nil))
			      &body body)
  (let ((rest (gensym))
	(block-name (gensym)))
    `(block ,block-name
       (flet ((return (&optional form)
		      (return-from ,block-name form)))
	 (loop for (,x . ,rest) on ,list do
	      (loop for ,y in ,rest do
		   (progn ,@body)))
	 ,return-value))))

My question is whether this is reasonable, or whether there's a better
approach (will this break things in ways I'm not thinking of?), or
whether I should give up on such a macro entirely as being a bad
abstraction.

From: Pascal Bourguignon
Subject: Re: Nested iteration macro question
Date: 
Message-ID: <873azjggjk.fsf@voyager.informatimago.com>
·······@ncsu.edu (Rob St. Amant) writes:

> In some code I'm writing, I use a nested iteration in several places:
>
> (loop for (x . rest) on list do
>      (loop for y in rest do
> 	  <body>))
>
> At first glance it seemed reasonable to wrap this up in a
> straightforward macro, but then I realized that if I ever use the more
> abstract form and want to exit it non-locally, RETURN wouldn't behave
> "properly", exiting only the inner loop.  So I did this:
>
> (defmacro do-unordered-pairs ((x y list &optional (return-value nil))
> 			      &body body)
>   (let ((rest (gensym))
> 	(block-name (gensym)))
>     `(block ,block-name
>        (flet ((return (&optional form)
> 		      (return-from ,block-name form)))
> 	 (loop for (,x . ,rest) on ,list do
> 	      (loop for ,y in ,rest do
> 		   (progn ,@body)))
> 	 ,return-value))))
>
> My question is whether this is reasonable, or whether there's a better
> approach (will this break things in ways I'm not thinking of?), or
> whether I should give up on such a macro entirely as being a bad
> abstraction.

RETURN and RETURN-FROM are macros.  You should "override" then locally
with MACROLET.  

Otherwise, for the reason you mention, you should avoid using LOOP in
your macros.  Or if you really wanted to use them, You should rather
name the loops with gensymed symbols, because the user may expect to
return from an outer block named NIL instead of from your macro (but
it's OK to _document_ that your macro defines a block named NIL, only
you didn't put any docstring in your macro...).

(defmacro do-unordered-pairs ((x y list &optional (result-form nil)) 
                              &body decl-and-body)
  "Run BODY with X and Y bound to each unorderded pairs of LIST."
  ;; Note, no mention of a default NIL block.
  (let ((rest (gensym))
        (dummy (gensym)))
     `(loop :named ,dummy :for (,x . ,rest) :on ,list 
            :do (loop :named ,dummy :for ,y :in ,rest 
                      :do (locally ,@decl-and-body)))))

This way, you can  use it as:

(block nil
  (loop :named :loop
        :for list :in list-of-list
        :collect (do-unordered-pairs (x y list (+ 1 1 1))
                    (when (eq x y)    (return 1))
                    (when (equal x y) (return-from :loop 2)))))
--> 1, 2 or (3 ...).



-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

PLEASE NOTE: Some quantum physics theories suggest that when the
consumer is not directly observing this product, it may cease to
exist or will exist only in a vague and undetermined state.
From: Rob St. Amant
Subject: Re: Nested iteration macro question
Date: 
Message-ID: <f7qprj$s14$1@blackhelicopter.databasix.com>
Pascal Bourguignon <···@informatimago.com> writes:

> ·······@ncsu.edu (Rob St. Amant) writes:
>
>> In some code I'm writing, I use a nested iteration in several places:
>>
>> (loop for (x . rest) on list do
>>      (loop for y in rest do
>> 	  <body>))
>>
>> At first glance it seemed reasonable to wrap this up in a
>> straightforward macro, but then I realized that if I ever use the more
>> abstract form and want to exit it non-locally, RETURN wouldn't behave
>> "properly", exiting only the inner loop.  So I did this:
>>
>> (defmacro do-unordered-pairs ((x y list &optional (return-value nil))
>> 			      &body body)
>>   (let ((rest (gensym))
>> 	(block-name (gensym)))
>>     `(block ,block-name
>>        (flet ((return (&optional form)
>> 		      (return-from ,block-name form)))
>> 	 (loop for (,x . ,rest) on ,list do
>> 	      (loop for ,y in ,rest do
>> 		   (progn ,@body)))
>> 	 ,return-value))))
>>
>> My question is whether this is reasonable, or whether there's a better
>> approach (will this break things in ways I'm not thinking of?), or
>> whether I should give up on such a macro entirely as being a bad
>> abstraction.
>
> RETURN and RETURN-FROM are macros.  You should "override" then locally
> with MACROLET.  
>
> Otherwise, for the reason you mention, you should avoid using LOOP in
> your macros.  Or if you really wanted to use them, You should rather
> name the loops with gensymed symbols, because the user may expect to
> return from an outer block named NIL instead of from your macro (but
> it's OK to _document_ that your macro defines a block named NIL, only
> you didn't put any docstring in your macro...).
>
> (defmacro do-unordered-pairs ((x y list &optional (result-form nil)) 
>                               &body decl-and-body)
>   "Run BODY with X and Y bound to each unorderded pairs of LIST."
>   ;; Note, no mention of a default NIL block.
>   (let ((rest (gensym))
>         (dummy (gensym)))
>      `(loop :named ,dummy :for (,x . ,rest) :on ,list 
>             :do (loop :named ,dummy :for ,y :in ,rest 
>                       :do (locally ,@decl-and-body)))))
>
> This way, you can  use it as:
>
> (block nil
>   (loop :named :loop
>         :for list :in list-of-list
>         :collect (do-unordered-pairs (x y list (+ 1 1 1))
>                     (when (eq x y)    (return 1))
>                     (when (equal x y) (return-from :loop 2)))))
> --> 1, 2 or (3 ...).

Thanks for the suggestions and corrections, Pascal, as well as for
identifying assumptions I didn't realize I was making (e.g., that all
iteration forms could be assumed to include an implicit block named
NIL).
From: Pascal Costanza
Subject: Re: Nested iteration macro question
Date: 
Message-ID: <5ge17bF3gb13bU1@mid.individual.net>
Pascal Bourguignon wrote:
> ·······@ncsu.edu (Rob St. Amant) writes:
> 
>> In some code I'm writing, I use a nested iteration in several places:
>>
>> (loop for (x . rest) on list do
>>      (loop for y in rest do
>> 	  <body>))
>>
>> At first glance it seemed reasonable to wrap this up in a
>> straightforward macro, but then I realized that if I ever use the more
>> abstract form and want to exit it non-locally, RETURN wouldn't behave
>> "properly", exiting only the inner loop.  So I did this:
>>
>> (defmacro do-unordered-pairs ((x y list &optional (return-value nil))
>> 			      &body body)
>>   (let ((rest (gensym))
>> 	(block-name (gensym)))
>>     `(block ,block-name
>>        (flet ((return (&optional form)
>> 		      (return-from ,block-name form)))
>> 	 (loop for (,x . ,rest) on ,list do
>> 	      (loop for ,y in ,rest do
>> 		   (progn ,@body)))
>> 	 ,return-value))))
>>
>> My question is whether this is reasonable, or whether there's a better
>> approach (will this break things in ways I'm not thinking of?), or
>> whether I should give up on such a macro entirely as being a bad
>> abstraction.
> 
> RETURN and RETURN-FROM are macros.  You should "override" then locally
> with MACROLET.  

No, you shouldn't do this, due to 11.1.2.1.2 in the HyperSpec. Better 
use new names.


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/
From: Alan Crowe
Subject: Re: Nested iteration macro question
Date: 
Message-ID: <86hcnxshhb.fsf@cawtech.freeserve.co.uk>
·······@ncsu.edu (Rob St. Amant) writes:

> In some code I'm writing, I use a nested iteration in several places:
> 
> (loop for (x . rest) on list do
>      (loop for y in rest do
> 	  <body>))
> 
> At first glance it seemed reasonable to wrap this up in a
> straightforward macro, but then I realized that if I ever use the more
> abstract form and want to exit it non-locally, RETURN wouldn't behave
> "properly", exiting only the inner loop.  So I did this:
> 
> (defmacro do-unordered-pairs ((x y list &optional (return-value nil))
> 			      &body body)
>   (let ((rest (gensym))
> 	(block-name (gensym)))
>     `(block ,block-name
>        (flet ((return (&optional form)
> 		      (return-from ,block-name form)))
> 	 (loop for (,x . ,rest) on ,list do
> 	      (loop for ,y in ,rest do
> 		   (progn ,@body)))
> 	 ,return-value))))
> 
> My question is whether this is reasonable, or whether there's a better
> approach (will this break things in ways I'm not thinking of?), or
> whether I should give up on such a macro entirely as being a bad
> abstraction.

The right approach here is to put the body code in an FLET
so that it stays outside the scope of the loop clauses that
are feeding it data via the parameters. 

(defmacro dopairs ((var1 var2 list-form &optional return-form)
                   &body code)
  (let ((function-name (gensym))
        (data-holder (gensym)))
    `(let ((,data-holder ,list-form))
      (flet ((,function-name (,var1 ,var2)
               ,@code))
        (loop for (x . rest) on ,data-holder do
              (loop for y in rest do (,function-name x y)))
        ,return-form))))

The expansions look like this

(macroexpand-1 '(dopairs (u v (make a list) (compute result))
                  (do this)
                  (do that)))
=>
CL-USER> (macroexpand-1 '(dopairs (u v (make a list) (compute result))
                          (do this)
                          (do that)))
(LET ((#:G1616 (MAKE A LIST)))
  (FLET ((#:G1615 (U V)
           (DO THIS)
           (DO THAT)))
    (LOOP FOR (X . REST) ON #:G1616 DO (LOOP FOR Y IN REST DO (#:G1615 X Y)))
    (COMPUTE RESULT)))

The programmer who uses this macro supplies forms in three
ways.

1)the init form (make a list) is in the lexical environment with
nothing added by the macro

2)the body forms (do this)(do that) are in a lexical
  environment with variable bindings of #:G1616, U, and V
  added by the macro.  #:G1616 is a gensym and U and V are
  the user supplied names that are intended to be there

3)The return value (compute result) is in a lexical
  environment with a variable binding for #:G1616 and a
  function binding for #:G1615 supplied by the
  macro. Gensyms, so OK.

Has this actually worked? The kind of stuff we are worried
about is what happens if dopairs is used inside a loop and
the programmer tries to break out of the loop with a
LOOP-FINISH inside the dopairs?

CL-USER> (loop with sevens = 0
               for list on '(1 2 3 4 5)
               do (format t "~&Doing ~A." list)
               (dopairs (u v list)
                 (print (list u v))
                 (when (= (+ u v) 7) (incf sevens))
                 (when (= sevens 3) (loop-finish))))

Doing (1 2 3 4 5).
(1 2) 
(1 3) 
(1 4) 
(1 5) 
(2 3) 
(2 4) 
(2 5) 
(3 4) 
(3 5) 
(4 5) 
Doing (2 3 4 5).
(2 3) 
(2 4) 
(2 5) 

Yep, the (loop-finish) busted us right out of the loop, we
didn't just terminate the dopairs on (2 3 4 5) and carry on
to Doing (3 4 5).

As a programmer I expect a dopairs macro to be surrounded by
an implicit block named nil, just like dotimes or dolist so
that a return breaks me out just one level. So I don't share
Rob's concern about his original macro-expansion capturing
the block named nil.

However, I don't expect (loop-finish) to work within a
dopairs, not unless there is an enclosing loop and
(loop-finish) terminates the enclosing loop macro. So
fletting the body of a loopy macroexpansion seems to be a
necessary technique.

Alan Crowe
Edinburgh
Scotland
From: Jo Lisper
Subject: Re: Nested iteration macro question
Date: 
Message-ID: <f7tsm3$d4k$1@wildfire.prairienet.org>
 > ·······@ncsu.edu (Rob St. Amant) writes:
 >> (defmacro do-unordered-pairs ...
 >>        (flet ((return (&optional form)
 >> 		      (return-from ,block-name form)))
 >> 	...
 >> My question is whether this is reasonable, or whether
 >> there's a better approach

Alan Crowe wrote:
 > The right approach here is to put the body code in an FLET
 > so that it stays outside the scope of the loop clauses that
 > are feeding it data via the parameters.

"right" is a strong word for that in CL.  Rob's original idea
is fine, except that Pascal B. is right that the return command
needs to be a macro, and I would agree with Pascal C. that it
should have a unique name instead of shadowing existing ones.
Your FLET is the standard functional approach, but Rob's macro
accepts all the variable names required for the loops, so there's
no problem.  All he has to do is export return-from-do-ordered-pairs
or whatever he calls his return macro if he puts the definition in
a separate package.

-- 
Dan
www.prairienet.org/~dsb/
From: Alan Crowe
Subject: Re: Nested iteration macro question
Date: 
Message-ID: <86sl7gk9eg.fsf@cawtech.freeserve.co.uk>
Jo Lisper <··········@cyberspace.net> writes:

> Alan Crowe wrote:
>  > The right approach here is to put the body code in an FLET
>  > so that it stays outside the scope of the loop clauses that
>  > are feeding it data via the parameters.
> 
> "right" is a strong word for that in CL.

I plead justification. Here it is.

1) code pls, thx

Nobody has posted complete code for an alternative way of
handling loop-finish. I'm not willing to try because I judge
that it will be tricky and time consuming.

2) brittleness

The basic issue is that you have a macro, let's call it LOUP,
that sets up a lexical environment with some useful names,
such as LOUP-FINISH, already set up. 

This is controversial. One persons "useful facility" is
another persons "hideous gotcha". I'm refering to the risk
of name capture in straight forward coding. There is a more
subtle problem.

Common Lisp style is to use defmacro to create CL+ and code
in CL+, not CL. That is not quite it. One uses defmacro more
than once. One creates CL++ by writing macros that expand
into CL+, and write the application in CL++. 

The subtle problem is that anaphoric macros are hostile to
this layering. If I create CL++ with (defmacro donodules
...) and donodules wraps a body of code in LOUP, what is
supposed to happen to LOUP-FINISH? I've hidden the name
capture problem in a way that enables the creation of hard
to find bugs.

In theory I have two choices, fletting the body code of
macros with loupy expansions, or wrapping the body code to
do double capture and diversion.

Leaving aside the trickiness and ugliness of double capture
and diversion, it is also brittle. You have to be very sure
that you have covered all of the lexical bindings because of
the nastiness of the bugs that result it you miss one.

In the specific case of CL:LOOP this is possible. You can
check the specification and write your macro using double
capture and diversion. Since CL is, fortunately, a dead
language, CL:LOOP isn't going to change so the technique
does work.

The problem is that Common Lisp is a terrifying zombie
language that is shambling forwards, eating programmers
brains and assimilating other languages' idioms. Or maybe it
is like the risen Christ, leading programmers to salvation,
take your pick. Either way you need to decide what to do
about NEW-PROJECT:LOUP.

NEW-PROJECT:LOUP is liable to gain extra pronouns as work
progresses and these are mostly upwards compatible
enhancements. Macros that expand into LOUP by fletting the
body code continue to work. Macros that expand into LOUP by
double capture and diversion break.

So fletting the body code is the correct approach.

Alan Crowe
Edinburgh
Scotland
From: Dan Bensen
Subject: Re: Nested iteration macro question
Date: 
Message-ID: <f80sqk$os6$1@wildfire.prairienet.org>
Alan Crowe wrote:
 > 1) code pls, thx
Rob included code in his original post.  I copied it verbatim,
and it ran just fine on sbcl once I renamed RETURN.
I take back what I said about it having to be a macro.
Rob's code is "right" and "correct".
Also, here's an even simpler solution:

(defmacro do-unordered-pairs ((x y list &optional (return-value nil))
			      &body body)
   (let ((rest (gensym)))
     `(block do-unordered-pairs
       (loop for (,x . ,rest) on ,list do
        (loop for ,y in ,rest do
	(progn ,@body)))
       ,return-value)))

(format t "~A~%"
   (do-unordered-pairs (a b '(1 5 2 8 6))
     (when (> a 7)
       (format t "~%Got a val>7: ~D.~%" a)
       (return-from do-unordered-pairs "Done."))
     (format t " (~D,~D) " a b)
   ))
=>
  (1,5)  (1,2)  (1,8)  (1,6)  (5,2)  (5,8)  (5,6)  (2,8)  (2,6)
Got a val>7: 8.
Done.

-- 
Dan
www.prairienet.org/~dsb/
From: Alan Crowe
Subject: Re: Nested iteration macro question
Date: 
Message-ID: <86ejizl2a4.fsf@cawtech.freeserve.co.uk>
Dan Bensen <··········@cyberspace.net> writes:
> (defmacro do-unordered-pairs ((x y list &optional (return-value nil))
> 			      &body body)
>    (let ((rest (gensym)))
>      `(block do-unordered-pairs
>        (loop for (,x . ,rest) on ,list do
>         (loop for ,y in ,rest do
> 	(progn ,@body)))
>        ,return-value)))
> 

This code drops the loop-finish problem on the floor. For
example it is clear what this code is supposed to do

         (loop for list in '((1 2 3)
                             (3 4 5)
                             (5 6 7)
                             (7 8 9)
                             (10 11 12)
                             (12 13 14)
                             (14 15 16))
               collect (let ((accumulator '()))
                        (do-unordered-pairs (x y list accumulator)
                          (when (= (+ x y) 12) 
                            (loop-finish))
                          (push (cons x y) accumulator))))

=> (((2 . 3) (1 . 3) (1 . 2)) ((4 . 5) (3 . 5) (3 . 4)))

because it is supposed to give up when x+y starts getting
too big.

But with do-unordered pairs as above, (loop-finish) is
unexpectly captured, letting the outer loop continue and
actually produce 

(((2 . 3) (1 . 3) (1 . 2)) ((4 . 5) (3 . 5) (3 . 4)) ((6 . 7) (5 . 6))
 ((8 . 9) (7 . 9) (7 . 8)) ((11 . 12) (10 . 12) (10 . 11))
 ((13 . 14) (12 . 14) (12 . 13)) ((15 . 16) (14 . 16) (14 . 15)))

It becomes necessary to write something like

         (loop for list in '((1 2 3)
                             (3 4 5)
                             (5 6 7)
                             (7 8 9)
                             (10 11 12)
                             (12 13 14)
                             (14 15 16))
               collect (flet ((stop()(loop-finish)))
                         (let ((accumulator '()))
                           (do-unordered-pairs (x y list accumulator)
                             (when (= (+ x y) 12) (stop))
                             (push (cons x y) accumulator)))))

Is that a gotcha to be avoided or do you just shrug?

Alan Crowe
Edinburgh
Scotland
From: Dan Bensen
Subject: Re: Nested iteration macro question
Date: 
Message-ID: <f82uaf$drv$1@wildfire.prairienet.org>
Alan Crowe wrote:
> This code drops the loop-finish problem on the floor. 

Good point, it looks like you're right.  I was going to
suggest replacing LOOP with DO loops, but then RETURN is
captured.  It's good to know there's a way out of the problem.

-- 
Dan
www.prairienet.org/~dsb/
From: Rob St. Amant
Subject: Re: Nested iteration macro question
Date: 
Message-ID: <f83aqa$4io$1@blackhelicopter.databasix.com>
Thanks for all the thoughtful responses: I've learned more about
robust macro construction, loops, and what's considered good
programming practice.
From: Thomas F. Burdick
Subject: Re: Nested iteration macro question
Date: 
Message-ID: <1185222219.798974.11700@d55g2000hsg.googlegroups.com>
On Jul 23, 2:42 pm, Alan Crowe <····@cawtech.freeserve.co.uk> wrote:
> Dan Bensen <··········@cyberspace.net> writes:
> > (defmacro do-unordered-pairs ((x y list &optional (return-value nil))
> >                          &body body)
> >    (let ((rest (gensym)))
> >      `(block do-unordered-pairs
> >        (loop for (,x . ,rest) on ,list do
> >         (loop for ,y in ,rest do
> >    (progn ,@body)))
> >        ,return-value)))
>
> This code drops the loop-finish problem on the floor.

Right, and there's an easy fix in our dear friend lambda:

(defmacro do-unordered-pairs ((x y list &optional ret) &body body)
  `(block do-unordered-pairs
     (let ((list ,list)
           (body (lambda (,x ,y) ,@body)))
       (loop for (x . rest) on list
             do (loop for y in rest do (funcall body x y))))
     ,ret))
From: Dan Bensen
Subject: Re: Nested iteration macro question
Date: 
Message-ID: <f8377l$gl4$1@wildfire.prairienet.org>
Thomas F. Burdick wrote:
> On Jul 23, 2:42 pm, Alan Crowe <····@cawtech.freeserve.co.uk> wrote:
>> This code drops the loop-finish problem on the floor.
> Right, and there's an easy fix in our dear friend lambda

Or our other dear friend flet, which Alan already mentioned. ;)

-- 
Dan
www.prairienet.org/~dsb/