From: charlieb
Subject: Macro style question.
Date: 
Message-ID: <bpaqfk$1m757o$1@ID-208832.news.uni-berlin.de>
Hi all,
	In my previous macro question posts I have asked for help defining a 
macro that re-arranges its arguments into an flet just like the first 
three lines of the example below. That is no problem (now) but there's 
still the little matter of the creation of (list #'a #'b 'c) from '(a b 
c). In trivial examples (like the one below) it is easy to see how you 
might do it but with examples with a more complicated &body it will 
become a bit hit and miss.

(mess-with-syms
	((a (print 'a) (print 'aa))
	 (b (print 'b) (print 'bb)))
	(create-list-using-syms '(a b c)))

=>

(flet
	((a () (print 'a) (print 'aa))
	 (b () (print 'b) (print 'bb)))
	(create-list-using-symbols (list #'a #'b 'c)))


Is it acceptable style to do something like

(defmacro functionise (list) list)

(mess-with-syms
	((a (print 'a) (print 'aa))
	 (b (print 'b) (print 'bb)))
	(create-list-using-syms (functionise '(a b c))))

and then have the mess-with-syms macro look for (functionise ...).
Even if this is ok style it still feels kinda nasty so if you have any 
suggestions for a better approach to this problem please let me know.
	It feels nasty because it seems to have inconsistent syntax. I don't 
necessarily want the user to know that this'll expand into an flet and I 
certainly don't expect them to know that they must use #' for symbols in 
the first argument. It's not as though those symbols needed to have #' 
so why should the ones in the body. This is not a criticism of lisp I 
just want non-lispers to be able to use it easily without imposing 
arbitrary seeming rules on them also it just seems like a lazy macro 
that goes halfway to being friendly then stops.

Cheers
Charlie.

From: Kalle Olavi Niemitalo
Subject: Re: Macro style question.
Date: 
Message-ID: <87d6boknaa.fsf@Astalo.kon.iki.fi>
Here are some more variations on Monday's theme.

The first one makes the set of lexically bound functions
searchable at runtime, so that FUNCTIONISE can evaluate its
argument and (let ((symbols '(a b c))) (functionise symbols))
works as expected:

  (declaim (inline convert-function-symbol))
  (defun convert-function-symbol (symbol)
    symbol)

  (defmacro functionise (symbols-form)
    `(mapcar #'convert-function-symbol ,symbols-form))

  (defmacro with-more-function-symbols (list &body body)
    `(flet ((convert-function-symbol (symbol)
              (case symbol
                ,@(loop for symbol in list
                        collect `((,symbol) #',symbol))
                (otherwise (convert-function-symbol symbol)))))
       (declare (inline convert-function-symbol))
       ,@body))

The above version of WITH-MORE-FUNCTION-SYMBOLS also grabs the
function bindings at its point of invocation.  For example,

  (flet ((a () 42))
    (with-more-function-symbols (a)
      (flet ((a () 69))
        (funcall (first (functionise '(a)))))))

evaluates to 42, not 69.  If your MESS-WITH-SYMS always both
binds the functions and registers them, and users don't play with
FLET themselves, this won't make any difference.

Even though this version of FUNCTIONISE is used much like a
function, it must still be a macro, so that its expansion can
capture the local CONVERT-FUNCTION-SYMBOL.

I tried to leave enough hints to the compiler so that it can
evaluate the MAPCAR at compile time if the argument is a
constant.  I didn't check whether this actually works.


The second variation keeps the set of bindings in an association
list.

  (define-symbol-macro function-symbols '())

  (defmacro functionise (symbols-form)
    `(loop for symbol in ,symbols-form
           collect (or (cdr (assoc symbol function-symbols))
                       symbol)))

  (defmacro with-more-function-symbols (list &body body)
    `(let ((function-symbols
            (nconc (list ,@(loop for symbol in list
                                 collect `(cons ',symbol #',symbol)))
                   function-symbols)))
       ,@body))

I set the initial value with DEFINE-SYMBOL-MACRO rather than
DEFVAR, so that I don't get a special variable.  This also means
I can't assign to FUNCTION-SYMBOLS, but I can still rebind it
and that's enough.

The second variation looks like it might cons more than the first
one.  I didn't check whether it really does.


The third variation uses a special variable.  This way,
FUNCTIONISE can be made a function.

  (defvar *function-symbols* '())

  (defun functionise (symbols)
    (loop for symbol in symbols
          collect (or (cdr (assoc symbol *function-symbols*))
                      symbol)))

  (defmacro with-more-function-symbols (list &body body)
    `(let ((*function-symbols*
            (nconc (list ,@(loop for symbol in list
                                 collect `(cons ',symbol #',symbol)))
                   *function-symbols*)))
       ,@body))

The semantics of dynamic bindings are not always appropriate,
however.
From: charlieb
Subject: Re: Macro style question.
Date: 
Message-ID: <bpimb0$1naveo$1@ID-208832.news.uni-berlin.de>
Thank-you for you comments but I think you misunderstood how far I want 
this to go. I'll post the code that troubles me:

(defmacro with-production ((rules-var &rest rule-list)
			   &rest body)
   (flet ((functionise (sym) `#',sym))
     (let* ((rule-functions ; flet part
	    (mapcar
		(lambda (rule) (append `(,(car rule)) `(())
					(cddr rule)))
		    (remove-if-not #'caddr rule-list)))
	
	   (rule-expansions ; symbols and their expansions e.g. (a a b)
	    (mapcar (lambda (rule)
		      (if (cddr rule)
			  (cons 'list
				(cons (functionise (car rule))
				      (mapcar
				       (lambda (item)
					 (if (assoc item rule-functions)
		    		            (functionise item) `',item))
				       (cadr rule))))
			`',(cons (car rule) (cadr rule))))
		    rule-list))
	
	   (rule-function-symbols ; A list of the symbols that are 						; 
functions
	    (mapcar (lambda (item) (functionise (car item)))
		    (remove-if-not #'cddr rule-list)))
	   (rule-symbols (mapcar (lambda (item) `',(car item))
				 (remove-if-not #'caddr rule-list))))
       `(flet
	   ,rule-functions
	 (let ((,rules-var (list ,@rule-expansions)))
	   ,@body)))))

(defmacro eval-production (list)
   (let ((item (gensym)))
     `(dolist (,item ,list t)
        (when (functionp ,item)
	 (funcall ,item)))))

Then you can do e.g.
(defun test-with (pme)
   (with-production (rules
;; The second arg for each of the rules is it's expansion rule
;; e.g. a -> a b
		    (a (a b) (print pme))
		    (b (b a) (print pme))
		    (c (c c)))
		     (eval-production (list #'a #'b #'a #'a 'c))))

What I'd like to be able to say is :
(defun test-with (pme)
   (with-production (rules
		    (a (a b) (print pme))
		    (b (b a) (print pme))
		    (c (c c)))
		     (eval-production (functionise a b a a c))))

I have doubts about functionise because it is context sensitive i.e. it 
needs to already be within an flet to be able to tell which symbols are 
functions there. I'm no macro expert but I would bet that you can't use 
the functional context of the macro at macroexpand time.

The approaches you have shown require you to set up the flet and also to 
tell the macro, "I have set up these flets". This is the duplication 
that I am trying to avoid. In the first test-with the declaration is 
implicit in the (function)ing os certain symbols but not others.

Cheers.
Charlie.

Kalle Olavi Niemitalo wrote:

> Here are some more variations on Monday's theme.
> 
> The first one makes the set of lexically bound functions
> searchable at runtime, so that FUNCTIONISE can evaluate its
> argument and (let ((symbols '(a b c))) (functionise symbols))
> works as expected:
> 
>   (declaim (inline convert-function-symbol))
>   (defun convert-function-symbol (symbol)
>     symbol)
> 
>   (defmacro functionise (symbols-form)
>     `(mapcar #'convert-function-symbol ,symbols-form))
> 
>   (defmacro with-more-function-symbols (list &body body)
>     `(flet ((convert-function-symbol (symbol)
>               (case symbol
>                 ,@(loop for symbol in list
>                         collect `((,symbol) #',symbol))
>                 (otherwise (convert-function-symbol symbol)))))
>        (declare (inline convert-function-symbol))
>        ,@body))
> 
> The above version of WITH-MORE-FUNCTION-SYMBOLS also grabs the
> function bindings at its point of invocation.  For example,
> 
>   (flet ((a () 42))
>     (with-more-function-symbols (a)
>       (flet ((a () 69))
>         (funcall (first (functionise '(a)))))))
> 
> evaluates to 42, not 69.  If your MESS-WITH-SYMS always both
> binds the functions and registers them, and users don't play with
> FLET themselves, this won't make any difference.
> 
> Even though this version of FUNCTIONISE is used much like a
> function, it must still be a macro, so that its expansion can
> capture the local CONVERT-FUNCTION-SYMBOL.
> 
> I tried to leave enough hints to the compiler so that it can
> evaluate the MAPCAR at compile time if the argument is a
> constant.  I didn't check whether this actually works.
> 
> 
> The second variation keeps the set of bindings in an association
> list.
> 
>   (define-symbol-macro function-symbols '())
> 
>   (defmacro functionise (symbols-form)
>     `(loop for symbol in ,symbols-form
>            collect (or (cdr (assoc symbol function-symbols))
>                        symbol)))
> 
>   (defmacro with-more-function-symbols (list &body body)
>     `(let ((function-symbols
>             (nconc (list ,@(loop for symbol in list
>                                  collect `(cons ',symbol #',symbol)))
>                    function-symbols)))
>        ,@body))
> 
> I set the initial value with DEFINE-SYMBOL-MACRO rather than
> DEFVAR, so that I don't get a special variable.  This also means
> I can't assign to FUNCTION-SYMBOLS, but I can still rebind it
> and that's enough.
> 
> The second variation looks like it might cons more than the first
> one.  I didn't check whether it really does.
> 
> 
> The third variation uses a special variable.  This way,
> FUNCTIONISE can be made a function.
> 
>   (defvar *function-symbols* '())
> 
>   (defun functionise (symbols)
>     (loop for symbol in symbols
>           collect (or (cdr (assoc symbol *function-symbols*))
>                       symbol)))
> 
>   (defmacro with-more-function-symbols (list &body body)
>     `(let ((*function-symbols*
>             (nconc (list ,@(loop for symbol in list
>                                  collect `(cons ',symbol #',symbol)))
>                    *function-symbols*)))
>        ,@body))
> 
> The semantics of dynamic bindings are not always appropriate,
> however.
From: Kent M Pitman
Subject: Re: Macro style question.
Date: 
Message-ID: <sfwy8ub56bv.fsf@shell01.TheWorld.com>
charlieb <··@privacy.net> writes:

> 		(lambda (rule) (append `(,(car rule)) `(())
> 					(cddr rule)))

Instead of this append, you want just

 (list* (car rule) '() (cddr rule))

or (probably better):

 `(,(car rule) () ,@(cddr rule))
From: charlieb
Subject: Re: Macro style question.
Date: 
Message-ID: <bpismr$1phkca$1@ID-208832.news.uni-berlin.de>
Kent M Pitman wrote:

> charlieb <··@privacy.net> writes:
> 
> 
>>		(lambda (rule) (append `(,(car rule)) `(())
>>					(cddr rule)))
> 
> 
> Instead of this append, you want just
> 
>  (list* (car rule) '() (cddr rule))
> 
> or (probably better):
> 
>  `(,(car rule) () ,@(cddr rule))
> 
Thanks, that nasty was left over from a previous incarnation.