From: Jeff
Subject: Re: Help with a macro
Date: 
Message-ID: <ccmlkf$2d3@odah37.prod.google.com>
Thanks everyone for the help. I understand what was happening now.
Thanks for pointing out LABELS in place of FLET, too. Just for
completion, the following is what I have, which works just as I wanted
it to:

(defmacro defenum (lst &key (start 0) (increment 1))
"declare a list of enumerated constants"
(labels ((enum (lst value)
(if (null lst) `(,value)
(let ((x (car lst)))
(cons `(defconstant ,x ,value) (enum (cdr lst)
(+ value increment)))))))
`(progn ,@(enum lst start))))

Jeff

Dan Muller wrote:
> "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: Peter Seibel
Subject: Re: Help with a macro
Date: 
Message-ID: <m3smc1oyez.fsf@javamonkey.com>
"Jeff" <········@volition-inc.com> writes:

> Thanks everyone for the help. I understand what was happening now.
> Thanks for pointing out LABELS in place of FLET, too. Just for
> completion, the following is what I have, which works just as I wanted
> it to:
>
> (defmacro defenum (lst &key (start 0) (increment 1))
> "declare a list of enumerated constants"
> (labels ((enum (lst value)
> (if (null lst) `(,value)
> (let ((x (car lst)))
> (cons `(defconstant ,x ,value) (enum (cdr lst)
> (+ value increment)))))))
> `(progn ,@(enum lst start))))

And for iteration fans everywhere, here's a solution that uses LOOP
instead of a recursive function to iterate over your list of names:

  (defmacro defenum (names &key (start 0) (increment 1))
    `(progn
      ,@(loop for name in names
              for value from start by increment
              collect `(defconstant ,name ,value))))

I should also point out (because someone else will if I don't) that in
Common Lisp there is rarely a need for this kind of enumerated
constant--unless you actually need constants that *also* have a
numeric value you can just use symbols; they have a unique identity
and the plesant attribute of having names. Of course if you're just
playing around with this to get a feel for macros that's not really
important.

-Peter

P.S. You might find it interesting to look at a couple chapters from
the book I'm writing on Common Lisp which will published by Apress
whenever the heck I finish it. I'd be interested to know if you find
them useful or confusing. All the chapter's I've finshed first drafts
of are up on the web at:

  <http://www.gigamonkeys.com/book/>

while the two chapters on macros are at:

  <http://www.gigamonkeys.com/book/macros-standard-control-constructs.html>

and:

  <http://www.gigamonkeys.com/book/macros-defining-our-own.html>

-Peter

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

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Robert St. Amant
Subject: Re: Help with a macro
Date: 
Message-ID: <lpnbriphvnz.fsf@haeckel.csc.ncsu.edu>
Peter Seibel <·····@javamonkey.com> writes:

> "Jeff" <········@volition-inc.com> writes:
> 
> > Thanks everyone for the help. I understand what was happening now.
> > Thanks for pointing out LABELS in place of FLET, too. Just for
> > completion, the following is what I have, which works just as I wanted
> > it to:
> >
> > (defmacro defenum (lst &key (start 0) (increment 1))
> > "declare a list of enumerated constants"
> > (labels ((enum (lst value)
> > (if (null lst) `(,value)
> > (let ((x (car lst)))
> > (cons `(defconstant ,x ,value) (enum (cdr lst)
> > (+ value increment)))))))
> > `(progn ,@(enum lst start))))
> 
> And for iteration fans everywhere, here's a solution that uses LOOP
> instead of a recursive function to iterate over your list of names:
> 
>   (defmacro defenum (names &key (start 0) (increment 1))
>     `(progn
>       ,@(loop for name in names
>               for value from start by increment
>               collect `(defconstant ,name ,value))))
> 
> I should also point out (because someone else will if I don't) that in
> Common Lisp there is rarely a need for this kind of enumerated
> constant--unless you actually need constants that *also* have a
> numeric value you can just use symbols; they have a unique identity
> and the plesant attribute of having names. Of course if you're just
> playing around with this to get a feel for macros that's not really
> important.

I've done something like this in the past, but only to call C
functions that dealt with enumerated data types through a foreign
function interface.  It's kind of ironic, I suppose.

-- 
Rob St. Amant
http://www4.ncsu.edu/~stamant
From: Peter Seibel
Subject: Re: Help with a macro
Date: 
Message-ID: <m3hdshovmt.fsf@javamonkey.com>
·······@haeckel.csc.ncsu.edu (Robert St. Amant) writes:

> Peter Seibel <·····@javamonkey.com> writes:
>
>> "Jeff" <········@volition-inc.com> writes:
>> 
>> > Thanks everyone for the help. I understand what was happening now.
>> > Thanks for pointing out LABELS in place of FLET, too. Just for
>> > completion, the following is what I have, which works just as I wanted
>> > it to:
>> >
>> > (defmacro defenum (lst &key (start 0) (increment 1))
>> > "declare a list of enumerated constants"
>> > (labels ((enum (lst value)
>> > (if (null lst) `(,value)
>> > (let ((x (car lst)))
>> > (cons `(defconstant ,x ,value) (enum (cdr lst)
>> > (+ value increment)))))))
>> > `(progn ,@(enum lst start))))
>> 
>> And for iteration fans everywhere, here's a solution that uses LOOP
>> instead of a recursive function to iterate over your list of names:
>> 
>>   (defmacro defenum (names &key (start 0) (increment 1))
>>     `(progn
>>       ,@(loop for name in names
>>               for value from start by increment
>>               collect `(defconstant ,name ,value))))
>> 
>> I should also point out (because someone else will if I don't) that in
>> Common Lisp there is rarely a need for this kind of enumerated
>> constant--unless you actually need constants that *also* have a
>> numeric value you can just use symbols; they have a unique identity
>> and the plesant attribute of having names. Of course if you're just
>> playing around with this to get a feel for macros that's not really
>> important.
>
> I've done something like this in the past, but only to call C
> functions that dealt with enumerated data types through a foreign
> function interface.  It's kind of ironic, I suppose.

Yes. That is one use. Though depending which code you control you
might still be able to get away with using symbols as the primary
value within Lisp by storing the value C wants as on the symbol's
plist:

  (defmacro defenum (names &key (start 0) (increment 1))
    `(progn
      ,@(loop for name in names
              for value from start by increment
              collect `(defconstant ,name ',name)
              collect `(setf (getf (symbol-plist ',name) 'c-value) ,value))
      (defun c->lisp (value)
        (case value
          ,@(loop for name in names
                  for value from start by increment
                  collect `(,value ',name))))))

  (defun lisp->c (name) (getf (symbol-plist name) 'c-value))


-Peter  

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

         Lisp is the red pill. -- John Fraser, comp.lang.lisp