From: Joerg Hoehle
Subject: macro flow from inside to outside
Date: 
Message-ID: <uy8go4np9.fsf@users.sourceforge.net>
Hi,

I wonder whether it's possible to avoid a full code walker[1] when
implementing the following pattern somewhat similar to several
COLLECTOR macros[2] out there.

Syntax: (bag ... (containing item &optional name) ...)

CONTAINING may occur anywhere (at any depth) within the body of BAG
and will add the given item to a possibly named container. The result
is the list of items in the unnamed container, if any.

What makes this "exercise" interesting is
1. that the NAME is obviously not known until the CONTAINING form is
   analyzed. 
2. that the return value also depends on whether an optional name was
   used for any container

The hope is that such code can be compiled using lexical variables for
efficient access instead of using an internal a-list or such. The
straightforward solution would involve having the bag macro code-walk
the complete body, looking for containing forms and creating according
code.

My idea below is to use MACROLET. The difficulty is to have
information (e.g. names of bags) flow between the different parts.

[1] I wish to avoid a full code walker because of complications with
macroexpansion and special forms etc. and because of possible poor
compiler analysis when given fully macroexpanded forms. See thread
<URL http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&threadm=u4qjv6z3n.fsf%40users.sourceforge.net&rnum=1&prev=/groups%3Fas_q%3Doptimization%26safe%3Dimages%26ie%3DISO-8859-1%26as_ugroup%3Dcomp.lang.lisp%26as_uauthors%3Dhoehle%26lr%3D%26hl%3Den >

Here's an simple implementation based on a dynamically-built alist.
(defmacro bag (&body body)
  `(let ((*bags* ()))
     (declare (type list *bags*))	; need not be special
     (macrolet ((containing (x &optional (name :default))
		  (format *trace-output* "Expanding(~S)... " name)
		  `((lambda (value)
		      (push value (cdr (or (assoc ',name *bags* :test #'eq)
					   (let ((loc (list ',name)))
					     (push loc *bags*) loc))))
		      value) ,x))
		(result-expansion ()
		  (format *trace-output* "Stored: ~S~%" *bags*)
		  '(cdr (assoc :default *bags* :test #'eq))))
       ,@body
       (result-expansion))))
I'm not even sure it's correct w.r.t. environment restrictions.
 It seems to work in interpreted mode in CLISP.
(bag) -> nil
(bag (containing 1)) -> (1)
(bag (containing 1) (containing 2)) -> (2 1)
(bag (prin1 (list (containing 1) (containing 2)))) -> (2 1)
(bag (containing 'a) (containing 2 evens) (prin1 *bags*)) -> (a)
(bag (containing 2 evens)) -> nil

We'll ignore the missing accessor for named bags. It's a detail. If
lexical variables could be used (as with LOOP, Iterate or various
collector packages), this would be a non-issue.
We'll ignore ordering of the elements.

(defun zot(n) (bag (containing n)))
(zot 1) -> (1)

Trying to go further
(defmacro bag (&body body)
  ;; Attempt to do more work at macroexpansion time
  `(let ((*bags* ()))
     (declare (type list *bags*))	; need not be special
     (macrolet ((containing (x &optional (name :default))
		  (format *trace-output* "Expanding(~S)... " name)
		  (or (assoc :default *bags* :test #'eq)
		      (let ((loc (list :default)))
			(push loc *bags*) loc))
		  `((lambda (value)
		      (push value (cdr (assoc :default *bags* :test #'eq)))
		      value) ,x))
		(result-expansion ()
		  (format *trace-output* "Stored: ~S~%" *bags*)
		  '(cdr (assoc :default *bags* :test #'eq))))
       ,@body
       (result-expansion))))
causes (zot 1) to fail in CLISP because macrolet-expansion occurs when
(defun zot #) is entered at the REPL. Therefore, *bags* is nil when
zot is finally invoked, and setf cdr fails.

[13]> (defun zot(n) (bag (containing n)))
Expanding(:DEFAULT)... Stored: ((:DEFAULT))
ZOT
[14]> (zot 2)
*** - SYSTEM::%RPLACD: NIL is not a pair

Is this approach a dead end, or possible to correct?

Should macrolet be avoided, because of environment issues, and regular
macros be used instead? How to communicate information (i.e. bag
names) between the parts?

[2] I'm investigating this possibility because I feel the current
implementation of Iterate is severely impacted by the requirements and
effects of a full code walker (cf. recent bug reports in
iterate-devel, among others), and I wonder whether this is necessary.

Thanks for your help,
	Jorg Hohle
Telekom/T-Systems Technology Center

From: Paul F. Dietz
Subject: Re: macro flow from inside to outside
Date: 
Message-ID: <4badnQJVeeO7_TrcRVn-pA@dls.net>
Joerg Hoehle wrote:

> Should macrolet be avoided, because of environment issues, and regular
> macros be used instead? How to communicate information (i.e. bag
> names) between the parts?

The usual approach for communicating extra information at macroexpansion
time is to insert it into the environment using auxiliary macrolets.
The macros that are defined by these auxiliary macrolets never appear
in the user code, but are used (via explicit calls to MACROEXPAND-1)
to access the environment to retrieve the information.

	Paul
From: Kalle Olavi Niemitalo
Subject: Re: macro flow from inside to outside
Date: 
Message-ID: <87mzx4mpe5.fsf@Astalo.kon.iki.fi>
"Paul F. Dietz" <·····@dls.net> writes:

> The macros that are defined by these auxiliary macrolets never appear
> in the user code, but are used (via explicit calls to MACROEXPAND-1)
> to access the environment to retrieve the information.

I worry that *MACROEXPAND-HOOK* may corrupt the information.  Are
there any constraints on what it is allowed to do?  To be safe,
I'd first bind it to #'FUNCALL, or call the MACRO-FUNCTION
directly (though there's no such thing for symbol macros).
From: Peter Seibel
Subject: Re: macro flow from inside to outside
Date: 
Message-ID: <m3mzx4bic1.fsf@javamonkey.com>
Joerg Hoehle <······@users.sourceforge.net> writes:

> Hi,
>
> I wonder whether it's possible to avoid a full code walker[1] when
> implementing the following pattern somewhat similar to several
> COLLECTOR macros[2] out there.
>
> Syntax: (bag ... (containing item &optional name) ...)
>
> CONTAINING may occur anywhere (at any depth) within the body of BAG
> and will add the given item to a possibly named container. The
> result is the list of items in the unnamed container, if any.
>
>
> What makes this "exercise" interesting is
> 1. that the NAME is obviously not known until the CONTAINING form is
>    analyzed. 
> 2. that the return value also depends on whether an optional name was
>    used for any container

Hmmm, the way you specified it it seems like this would do the trick
though I'm sure it not what you actually want:

  (defmacro bag (&body body)
    (let ((bag (gensym)))
      `(let ((,bag ()))
         (flet ((containing (item &optional name)
                  (unless name (push item ,bag))))
           ,@body)
         ,bag)))

I.e. since only the items added to the unnamed container are returned
we can just throw away the rest. But you probably wanted something
that didn't just throw away those valuse. Here' a version that uses
lexical variables, as requested. However it suffers from only allowing
a finite number of named bags. There may be some clever way to work
around that without walking the code. Or you could just live with it,
the same way we live with other limits such as the number of arguments
we can pass to a function.

  (defmacro bag (&body body)
    (let ((bag (gensym))
          (namedbags (loop repeat 10 collect (gensym))))
      `(let ((,bag ())
             ,@(loop for bag in namedbags collect `(,bag ())))
         (flet ((find-bag (name)
                  (cond
                    (name
                     (loop for bag in ',namedbags
                        for bagname = (get bag 'bag-name)
                        when (eql name bagname) return bag
                        when (not bagname) do
                          (setf (get bag 'bag-name) name) and
                        return bag
                        finally (error "Out of bags")))
                    (t ',bag))))
           (macrolet ((containing (item &optional name)
                        `(push ,item ,(find-bag name)))
                      (the-bag (name)
                        (find-bag name)))
             ,@body)
           ,bag))))

This lets you write stuff like:

  CL-USER> (bag
             (dotimes (i 10)
               (if (evenp i)
                 (containing i evens)
                 (containing i odds)))
             (containing (the-bag evens))
             (containing (the-bag odds)))
  ((9 7 5 3 1) (8 6 4 2 0))


-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Lieven Marchand
Subject: Re: macro flow from inside to outside
Date: 
Message-ID: <87brdksct0.fsf@wyrd.be>
Joerg Hoehle <······@users.sourceforge.net> writes:

> I wonder whether it's possible to avoid a full code walker[1] when
> implementing the following pattern somewhat similar to several
> COLLECTOR macros[2] out there.

If you're willing to go outside the ANSI standard, this was one of the
uses of COMPILER-LET. Most CL compilers still support it in a CLTL1
compatibility package.

You can find an example in the Issue 'COMPILER-LET-CONFUSION' in the
CLHS.

Note that the repeated assertion 'Most (if not all) uses of it can be
rewritten using MACROLET or SYMBOL-MACROLET.' is a bit
optimistic. E.g., the SERIES package uses COMPILER-LET extensively and
quite a few people have tried to eliminate it without succeeding.

-- 
An amateur practices until he gets it right,
A professional practices until she can't get it wrong.