From: Jeff
Subject: Help with a macro
Date: 
Message-ID: <ccmh51$9pq@odbk17.prod.google.com>
I'm just starting to actually use macros (this is where the power is,
right?), and thought I'd try something simple. I'm sure that what I'm
doing wrong below is very easy to fix and is just a basic
misunderstanding of exactly what macros do "under the hood". I'm sure
the function is self documenting for what I want to accomplish:

(defmacro defenum (lst &key (start 0) (increment 1))
(flet ((enum (sym value)
(if (null sym) value
(progn
`(defconstant ,(car sym) ,value)
(enum (cdr sym) (+ value increment))))))
(enum lst start)))

Any help with this (along with an explanation, please) would be
helpful. And, as always, thanks in advance! ;)

Jeff

P.S. Sorry about the formatting, google groups isn't letting the
indenting take hold for some reason.

From: Kenny Tilton
Subject: Re: Help with a macro
Date: 
Message-ID: <41AHc.63194$a92.3352@twister.nyc.rr.com>
Jeff wrote:
> I'm just starting to actually use macros (this is where the power is,
> right?), and thought I'd try something simple. I'm sure that what I'm
> doing wrong below is very easy to fix and is just a basic
> misunderstanding of exactly what macros do "under the hood". I'm sure
> the function is self documenting for what I want to accomplish:
> 
> (defmacro defenum (lst &key (start 0) (increment 1))
> (flet ((enum (sym value)
> (if (null sym) value
> (progn
> `(defconstant ,(car sym) ,value)
> (enum (cdr sym) (+ value increment))))))
> (enum lst start)))

You did not say anything about what problem you are having with it, nor 
what an example of a call would look like, nor what result you would 
expect from that example of a call. That is an important part of the 
dialog. You are right that it is clear to us what you are up to, but by 
not sharing all that what is not clear is where are you confused.

My guess is you are getting nor further than:

"Warning: While compiling these undefined functions were referenced: 
COMMON-LISP-USER::ENUM."

Which I can understand is a little puzzling, since you have an flet for 
ENUM. The problem is that you want to call enum recursively, so you have 
to use LABELS instead of FLET.

Aside to CL gurus: why did they not just make FLET capable of recursion 
when they recognized the need? Performance?

Back to DEFENUM: LABELS will make this compile and this run:

(defenum (a b c) :start 10)
=> 13

with no constants defined. Hint: macroexpanding the above also returns 
13--it is supposed to return '(progn (defconstant a 10)...etc). Hint 2: 
defenum returns what the last call to enum returns, and it is supposed 
to return '(progn (defconstant a 10)...etc) for the compiler to chomp on.

kt

-- 
Home? http://tilton-technology.com
Cells? http://www.common-lisp.net/project/cells/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
Your Project Here! http://alu.cliki.net/Industry%20Application
From: Peter Seibel
Subject: Re: Help with a macro
Date: 
Message-ID: <m3wu1doz0t.fsf@javamonkey.com>
Kenny Tilton <·······@nyc.rr.com> writes:

> Aside to CL gurus: why did they not just make FLET capable of
> recursion when they recognized the need? Performance?

Without any presumption of promoting myself to "guru" I can answer
than one. Sometimes it's useful[1] to be able to nest FLETs where the
functions defined in one FLET can call out to the functions of the
same name defined in the enclosing FLET, in particular when the FLETs
are part of a macroexpansion.

In particular you can use this in macros that may be nested to create
new flavors of lexically scoped bindings. (This kind of trick,
incidentally, was what I was trying to do when I ran into the topic I
brough up in the recent MACROLET environment thread. Except I wanted
to detect references to non-existant bindings at compile time rather
than runtime as happens here):

  (defmacro thing (name) `(thing-value ',name))

  (defun thing-value (name) (error "No THING binding for ~a" name)) 

  (defmacro with-thing-binding ((name value) &body body)
    `(let ((value ,value))
      (flet ((thing-value (n)
               (if (eq ',name n) 
                 ,value
                 (thing-value n))))
        ,@body)))

This lets us do:

  (with-thing-binding (x 10)
    (with-thing-binding (x 20)
      (with-thing-binding (y 30)
        (list (thing x) (thing y))))) ==> (20 30)

and even capture the "binding" in a closure:

  (funcall (with-thing-binding (x 10) #'(lambda () (thing x)))) ==> 10

-Peter

P.S. Thanks to Kent Pitman who originally pointed out this use of FLET
to me. See his message with Message-ID:
<···············@shell01.TheWorld.com> for the start of a sub-thread
on this topic.

[1] For some values of "useful".

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

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: ·········@random-state.net
Subject: Re: Help with a macro
Date: 
Message-ID: <ccnevs$236cs$1@midnight.cs.hut.fi>
Kenny Tilton <·······@nyc.rr.com> wrote:

> Aside to CL gurus: why did they not just make FLET capable of recursion 
> when they recognized the need? Performance?

No guru, but presumably so that you could do eg:

 (defun foo (...) (...expensive-function...))

 (defmacro with-foo (&body forms)
  `(flet ((foo (...)
           (if ...some-condition...
               ...cheaper-version-of-foo...
               (foo ...))))
       ,@body))

LABELS is there when you need recursion.

Cheers,

 -- Nikodemus                   "Not as clumsy or random as a C++ or Java. 
                             An elegant weapon for a more civilized time."
From: Dan Muller
Subject: Re: Help with a macro
Date: 
Message-ID: <X9AHc.895$dy3.105@newssvr15.news.prodigy.com>
"Jeff" <········@volition-inc.com> writes:

> I'm just starting to actually use macros (this is where the power is,
> right?), and thought I'd try something simple. I'm sure that what I'm
> doing wrong below is very easy to fix and is just a basic
> misunderstanding of exactly what macros do "under the hood". I'm sure
> the function is self documenting for what I want to accomplish:
>
> (defmacro defenum (lst &key (start 0) (increment 1))
>  (flet ((enum (sym value)
>	   (if (null sym) value
>	       (progn
>		 `(defconstant ,(car sym) ,value)
>		 (enum (cdr sym) (+ value increment))))))
>    (enum lst start)))

[formatting added above]

Here's how I would reason about this:

The macro has to return a single form that "stands in place of" the
macro invocation. Since what you want is multiple (defconstant ... )
forms, you'll want to return a PROGN form, with the generated
DEFCONSTANT forms spliced in:

(defmacro defenum (lst &key (start 0) (increment 1))
  `(progn ,@(something)))

The (something) form should return the list of defconstant forms, so
here comes your helper function ENUM:

(defmacro defenum (lst &key (start 0) (increment 1))
  (labels ((enum (sym-list value)
	     (if (null sym-list)
                 ()
                 (cons `(defconstant ,(car sym-list) ,value)
		       (enum (cdr sym-list) (+ value increment))))))
  `(progn ,@(enum lst start))))

Note that you have to use LABELS rather than FLET, because ENUM calls
itself recursively.
From: Thomas Schilling
Subject: Re: Help with a macro
Date: 
Message-ID: <opsavqecketrs3c0@news.CIS.DFN.DE>
Am 9 Jul 2004 09:29:21 -0700 schrieb Jeff <········@volition-inc.com>:

> I'm just starting to actually use macros (this is where the power is,
> right?), and thought I'd try something simple. I'm sure that what I'm
> doing wrong below is very easy to fix and is just a basic
> misunderstanding of exactly what macros do "under the hood". I'm sure
> the function is self documenting for what I want to accomplish:
>
> (defmacro defenum (lst &key (start 0) (increment 1))
> (flet ((enum (sym value)
> (if (null sym) value
> (progn
> `(defconstant ,(car sym) ,value)
> (enum (cdr sym) (+ value increment))))))
> (enum lst start)))
>
> Any help with this (along with an explanation, please) would be
> helpful. And, as always, thanks in advance! ;)

Your macro has to return a list representing the code it's meant to 
generate. (A macro is always called to return some code which is then 
inserted instead of the macro call and then evaluated.)

So a (imperative) rewrite would look like this:

(defmacro defenum (lst &key (start 0) (increment 1))
   "Define an integer constant for each symbol in LST."
   (let ((val start))
     (append '(locally)
	    (loop for sym in lst
		  collect `(defconstant ,sym ,val)
		  do (incf val increment)))))

(macroexpand-1 '(defenum (a b c d))) =>
(LOCALLY
   (DEFCONSTANT A 0)
   (DEFCONSTANT B 1)
   (DEFCONSTANT C 2)
   (DEFCONSTANT D 3)), T

The locally is needed to make the forms be evaluated at the toplevel.

-- 
      ,,
     \../   /  <<< The LISP Effect
    |_\\ _==__
__ | |bb|   | _________________________________________________
From: Kenny Tilton
Subject: Re: Help with a macro
Date: 
Message-ID: <Y9AHc.63198$a92.2926@twister.nyc.rr.com>
Thomas Schilling wrote:
> (defmacro defenum (lst &key (start 0) (increment 1))
>   "Define an integer constant for each symbol in LST."
>   (let ((val start))
>     (append '(locally)
>         (loop for sym in lst
>           collect `(defconstant ,sym ,val)
>           do (incf val increment)))))
> 
> (macroexpand-1 '(defenum (a b c d))) =>
> (LOCALLY
>   (DEFCONSTANT A 0)
>   (DEFCONSTANT B 1)
>   (DEFCONSTANT C 2)
>   (DEFCONSTANT D 3)), T
> 
> The locally is needed to make the forms be evaluated at the toplevel.
> 

PROGN would suffice, btw.

kt

-- 
Home? http://tilton-technology.com
Cells? http://www.common-lisp.net/project/cells/
Cello? http://www.common-lisp.net/project/cello/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
Your Project Here! http://alu.cliki.net/Industry%20Application
From: Alan Crowe
Subject: Re: Help with a macro
Date: 
Message-ID: <867jtbmvqt.fsf@cawtech.freeserve.co.uk>
Jeff explores:
> (defmacro defenum (lst &key (start 0) (increment 1))

This is easier than you realise. Providing you can get the
base case to work

(defenum ()) expands to nil

and the inductive step 

(defenum (ten twelve fourteen) :start 10 :increment 2)

expands to

(defconstant ten 10)
(defenum (twelve fourteen) :start 12 :increment 2)

then the macroexpansion process will take care of the rest.

The only worry is that you have to wrap the code that your
macro generates in a progn.

(progn 
  (defconstant ten 10)
  (defenum (twelve fourteen) :start 12 :increment 2))

so it will end up

(progn
  (defconstant ten 10)
  (progn
    (defconstant twelve 12)
    (progn
      (defconstant fourteen 14)
      nil)))

But "Hyper Spec 3.2.3.1 Processing of Top Level Forms" tells
you that:

    3.If the form is a progn form, each of its body forms is
      sequentially processed as a top level form in the same
      processing mode.

so the recursive nesting of progn's is officially OK.

(defmacro defenum (list &key (start 0)(increment 1))
    (if list
	`(progn (defconstant ,(car list) ,start)
		(defenum ,(cdr list)
		  :start ,(+ start increment)
		  :increment ,increment))))

Alan Crowe
Edinburgh
Scotland