From: Vijay L
Subject: getting local bindings
Date: 
Message-ID: <1eaf81aa.0206211111.3c10295b@posting.google.com>
im trying to write my own version of defstruct ((:type list) only for
now)

this is the macro:
(defmacro defstruct^ (name &rest slot-descriptions)
  (list 'defun (make-atom 'make- name) (cons '&key slot-descriptions)
        (list 'let (list (list 'name-list 
                               (list 'mapcar
                                     '(function (lambda (x)
                                                  (if (consp x) 
                                                      (car x)
                                                      x)))
                                     (list 'quote slot-descriptions)))
                         'made-list)
              '(dolist (i name-list)
                 (push (what-fn-to-use?? i) made-list))
              '(nreverse made-list))))

(defun make-atom (&rest args)
  (intern (format nil "~{~A~}" args)))

if at the top level i give
(defstruct^ person
  (name 'paul-simon)
  age
  (sex 'm))

it generates:
(defun make-person (&key (name (quote paul-simon)) age (sex (quote
m)))
  (let ((name-list (mapcar #'(lambda (x) (if (consp x)
                                             (car x)
                                             x)))
                          '((name 'paul-simon) age (sex 'm))))
        made-list)
    (dolist (i name-list)
      (push (what-fn-to-use?? i) made-list))
    (nreverse made-list)))

if somebody can please tell me what `what-fn-to-use??' is i'll be very
very grateful (ofcourse tips on style are always welcome (im a newbie
in lisp))
`eval' and `symbol-value' dont work for local bindings

*** credits to `make-atom' go to Peter Norvig

Thanks,
Vijay

From: Barry Margolin
Subject: Re: getting local bindings
Date: 
Message-ID: <JaLQ8.29$S83.1334@paloalto-snr1.gtei.net>
In article <····························@posting.google.com>,
Vijay L <······@lycos.com> wrote:
>im trying to write my own version of defstruct ((:type list) only for
>now)
>
>this is the macro:
>(defmacro defstruct^ (name &rest slot-descriptions)
>  (list 'defun (make-atom 'make- name) (cons '&key slot-descriptions)

Ugh!  Please use backquote if you expect people to be able to understand
your macros.

>if at the top level i give
>(defstruct^ person
>  (name 'paul-simon)
>  age
>  (sex 'm))
>
>it generates:
>(defun make-person (&key (name (quote paul-simon)) age (sex (quote
>m)))
>  (let ((name-list (mapcar #'(lambda (x) (if (consp x)
>                                             (car x)
>                                             x)))
>                          '((name 'paul-simon) age (sex 'm))))
>        made-list)
>    (dolist (i name-list)
>      (push (what-fn-to-use?? i) made-list))
>    (nreverse made-list)))
>
>if somebody can please tell me what `what-fn-to-use??' is i'll be very
>very grateful (ofcourse tips on style are always welcome (im a newbie
>in lisp))
>`eval' and `symbol-value' dont work for local bindings

You need to get the names at expansion time, not run time, and insert them
directly into the macro expansion.

(defmacro defstruct^ (name &rest slot-descriptions)
  (let ((name-list (loop for slot in slot-descriptions
			 collect (if (listp slot)
				     (first slot)
				     slot))))
    `(defun ,(make-atom 'make- name) (&key ,@slot-descriptions)
       (list ,@name-list))))

-- 
Barry Margolin, ······@genuity.net
Genuity, Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Jordan Katz
Subject: Re: getting local bindings
Date: 
Message-ID: <m3it4c801o.fsf@underlevel.underlevel.net>
Barry Margolin <······@genuity.net> writes:

> In article <····························@posting.google.com>,
> Vijay L <······@lycos.com> wrote:
> >im trying to write my own version of defstruct ((:type list) only for
> >now)
> >
> >this is the macro:
> >(defmacro defstruct^ (name &rest slot-descriptions)
> >  (list 'defun (make-atom 'make- name) (cons '&key slot-descriptions)
> 
> Ugh!  Please use backquote if you expect people to be able to understand
> your macros.

Why is it considered so crucial to use the backquote there?  Is it
only for the purposes of macroexpand?

Thanks,
-- 
Jordan Katz <····@underlevel.net>  |  Mind the gap
From: Thomas Bushnell, BSG
Subject: Re: getting local bindings
Date: 
Message-ID: <87bsa368ra.fsf@becket.becket.net>
Jordan Katz <····@underlevel.net> writes:

> Why is it considered so crucial to use the backquote there?  Is it
> only for the purposes of macroexpand?

It's just massively clearer and easier to read.
From: Rahul Jain
Subject: Re: getting local bindings
Date: 
Message-ID: <87ofe40wnu.fsf@localhost.localdomain>
Jordan Katz <····@underlevel.net> writes:

> Barry Margolin <······@genuity.net> writes:
> > Ugh!  Please use backquote if you expect people to be able to understand
> > your macros.
> 
> Why is it considered so crucial to use the backquote there?  Is it
> only for the purposes of macroexpand?

So that the body of the macro looks like the code it produces, without
huge numbers of LISTs and QUOTEs all over the place obfuscating the
purpose of the code.

-- 
-> -/                        - Rahul Jain -                        \- <-
-> -\  http://linux.rice.edu/~rahul -=-  ············@techie.com   /- <-
-> -X "Structure is nothing if it is all you got. Skeletons spook  X- <-
-> -/  people if [they] try to walk around on their own. I really  \- <-
-> -\  wonder why XML does not." -- Erik Naggum, comp.lang.lisp    /- <-
|--|--------|--------------|----|-------------|------|---------|-----|-|
   (c)1996-2002, All rights reserved. Disclaimer available upon request.
From: Vijay L
Subject: Re: getting local bindings
Date: 
Message-ID: <1eaf81aa.0206220925.64f9198c@posting.google.com>
> Barry Margolin <······@genuity.net> wrote in message news:<JaLQ8.29?
> ·········@paloalto-snr1.gtei.net>...
> >this is the macro:
> >(defmacro defstruct^ (name &rest slot-descriptions)
> >  (list 'defun (make-atom 'make- name) (cons '&key slot-descriptions)
> 
> Ugh!  Please use backquote if you expect people to be able to understand
> your macros.
> 
I apologize. here is the backquote'd version

(defmacro defstruct^ (name &rest slot-descriptions)
  `(defun ,(make-atom 'make- name) ,(cons '&key slot-descriptions)
     (let ((name-list (mapcar #'(lambda (x) (if (consp x)
                                                (car x)
                                              x))
                              ,slot-descriptions))
           made-list)
       (dolist (i name-list)
         (push (what-fn-to-use??? i) made-list))
       (nreverse made-list))))
I thought quoting was stylish, obviously not when somebody else has to
read it
> 
> You need to get the names at expansion time, not run time, and insert them
> directly into the macro expansion.
> 
> (defmacro defstruct^ (name &rest slot-descriptions)
>   (let ((name-list (loop for slot in slot-descriptions
> 			 collect (if (listp slot)
> 				     (first slot)
> 				     slot))))
>     `(defun ,(make-atom 'make- name) (&key ,@slot-descriptions)
>        (list ,@name-list))))

I'll try to do the same thanks. Your version works like a charm. But i
dont understand how the loop works. I dont have any LISP bibles with
me, all the LISP i've learnt is by reading code and tutorials on the
net. I'd appreciate it if you could elaborate on the loop

Thanks,
Vijay L
From: Thomas F. Burdick
Subject: Re: getting local bindings
Date: 
Message-ID: <xcvbsa3nm8r.fsf@apocalypse.OCF.Berkeley.EDU>
······@lycos.com (Vijay L) writes:

> > Barry Margolin <······@genuity.net> wrote in message news:<JaLQ8.29?
> > ·········@paloalto-snr1.gtei.net>...
> > >this is the macro:
> > >(defmacro defstruct^ (name &rest slot-descriptions)
> > >  (list 'defun (make-atom 'make- name) (cons '&key slot-descriptions)
> > 
> > Ugh!  Please use backquote if you expect people to be able to understand
> > your macros.
> > 
> I apologize. here is the backquote'd version
> 
> (defmacro defstruct^ (name &rest slot-descriptions)
>   `(defun ,(make-atom 'make- name) ,(cons '&key slot-descriptions)
>      (let ((name-list (mapcar #'(lambda (x) (if (consp x)
>                                                 (car x)
>                                               x))
>                               ,slot-descriptions))
>            made-list)
>        (dolist (i name-list)
>          (push (what-fn-to-use??? i) made-list))
>        (nreverse made-list))))

For what it's worth, I generally find it more readable to move
computation out of the backquoted part.  It's obviously a style issue,
and what computation gets moved out, and what stays in, is a personal
call.  But it's a big help for me, when coming back and reading my
macros much later.  I'd have written the above as:

  (defmacro defstruct^ (name &rest slot-descriptions)
    (let ((name (make-atom 'make- name))
          (lambda-list (cons '&key slot-descriptions)))
      `(defun ,name ,lambda-list
         (let ((name-list (mapcar #'(lambda (x) (if (consp x)
                                                    (car x)
                                                  x))
                                  ,slot-descriptions))
               made-list)
           (dolist (i name-list)
             (push (what-fn-to-use??? i) made-list))
           (nreverse made-list)))))

> I'll try to do the same thanks. Your version works like a charm. But i
> dont understand how the loop works. I dont have any LISP bibles with
> me, all the LISP i've learnt is by reading code and tutorials on the
> net. I'd appreciate it if you could elaborate on the loop

  (loop for slot in slot-descriptions
        collect (if (listp slot)
                    (first slot)
                    slot))

(loop for <X> in <Y> ...) is similar to DOLIST, it binds <X> to each
element in the list <Y>.  (loop ... collect <Z>) means that for each
iteration, the value of <Z> will be collected into a list, which is
returned as the value of the loop form.  I'd reccomend finding a loop
tutorial online, and when you think you've got the feel for it, read
the entry in the HyperSpec on loop (it's perfectly understandable once
you get the hang of loop, but only then).

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | No to Imperialist war |                        
     ,--'    _,'   | Wage class war!       |                        
    /       /      `-----------------------'                        
   (   -.  |                               
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: Barry Margolin
Subject: Re: getting local bindings
Date: 
Message-ID: <_eGR8.6$317.1330@paloalto-snr1.gtei.net>
In article <····························@posting.google.com>,
Vijay L <······@lycos.com> wrote:
>> You need to get the names at expansion time, not run time, and insert them
>> directly into the macro expansion.
>> 
>> (defmacro defstruct^ (name &rest slot-descriptions)
>>   (let ((name-list (loop for slot in slot-descriptions
>> 			 collect (if (listp slot)
>> 				     (first slot)
>> 				     slot))))
>>     `(defun ,(make-atom 'make- name) (&key ,@slot-descriptions)
>>        (list ,@name-list))))
>
>I'll try to do the same thanks. Your version works like a charm. But i
>dont understand how the loop works. I dont have any LISP bibles with
>me, all the LISP i've learnt is by reading code and tutorials on the
>net. I'd appreciate it if you could elaborate on the loop

The loop does exactly the same thing as your mapcar.  "for slot in
slot-descriptions" means that the variable SLOT iterates through the
elements of the SLOT-DESCRIPTIONS list.  The COLLECT verb adds its
parameter to a list that will be returned.

So if you just read it as if it's English, it does the obvious thing.

-- 
Barry Margolin, ······@genuity.net
Genuity, Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Vijay L
Subject: getting local bindings part 2
Date: 
Message-ID: <1eaf81aa.0206242157.a1166dc@posting.google.com>
Barry Margolin <······@genuity.net> wrote in message news:<················@paloalto-snr1.gtei.net>...
> The loop does exactly the same thing as your mapcar.  "for slot in
> slot-descriptions" means that the variable SLOT iterates through the
> elements of the SLOT-DESCRIPTIONS list.  The COLLECT verb adds its
> parameter to a list that will be returned.
> 
> So if you just read it as if it's English, it does the obvious thing.

Thanks, I'd figured that it was doing something of that sort and i did
it with mapcar. it was a stupid question on my part anyway, i should
have looked for the solutions myself. thanks for you time.

now i have a new problem. i found out that VALUES does not work in
macros, and so if i'm writing my own version of DEFSTRUCT, my macro
should create accessor functions also, i dont know how to do this
short of using EVAL.
heres the code:

(defmacro defstruct^ (name &rest slot-descriptions)
  (let ((name-list (mapcar #'(lambda (x) (if (consp x)
                                             (car x)
                                           x))
                           slot-descriptions))
        (count 0))
    (dolist (ithname name-list)
      (let ((fn-name (make-atom name '- ithname)))
        ;;this is where the accessor function is created
        (eval `(defun ,fn-name (,name)
                 (elt ,name ,count))) 
        (incf count)))
    ;;constructor function
    (eval `(defun ,(make-atom 'make- name) (&key ,@slot-descriptions)
             (list ,@name-list))))
  (list 'quote name))

i've used ELT because i plan to include the (:TYPE VECTOR) option. so
if and when i do i will only have to change the constructor function

using eval in macros is always looked down upon. in fact in the "ALU
Lisp Programming Style" by Mark Krantrowitz and Barry Margolin it says
The general rule of thumb about EVAL is: if you think you need to use
EVAL, you're probably wrong.

I can see that by using eval my structure can never be defined in a
local scope and its functions will always be global. but even
DEFSTRUCT seems to be global, I tried:
1> (let ()
     (defstruct empty
       nothing)
     nil)
NIL

2> (make-empty)
#S(EMPTY NOTHING NIL)

so am i justified in my use of EVAL in my macro, or is there a better
way?

Vijay
If at first you do not succeed, destroy all evidence that you tried
If at first you do succeed, try not to look surprised
From: Tim Moore
Subject: Re: getting local bindings part 2
Date: 
Message-ID: <af94r5$89g$0@216.39.145.192>
On 24 Jun 2002 22:57:23 -0700, Vijay L <······@lycos.com> wrote:
>now i have a new problem. i found out that VALUES does not work in
>macros, and so if i'm writing my own version of DEFSTRUCT, my macro

By this I think you mean that you don't know how to return multiple
forms from a macro.  You do this with progn:

>should create accessor functions also, i dont know how to do this
>short of using EVAL.
>heres the code:
>
>(defmacro defstruct^ (name &rest slot-descriptions)
>  (let ((name-list (mapcar #'(lambda (x) (if (consp x)
>                                             (car x)
>                                           x))
>                           slot-descriptions))
>        (count 0))
>    (dolist (ithname name-list)
>      (let ((fn-name (make-atom name '- ithname)))
>        ;;this is where the accessor function is created
>        (eval `(defun ,fn-name (,name)
>                 (elt ,name ,count))) 
>        (incf count)))
>    ;;constructor function
>    (eval `(defun ,(make-atom 'make- name) (&key ,@slot-descriptions)
>             (list ,@name-list))))
>  (list 'quote name))

(defmacro defstruct^ (name &rest slot-descriptions)
  (let ((name-list (mapcar #'(lambda (x) (if (consp x)
                                             (car x)
                                           x))
                           slot-descriptions))
        (count 0)
        (accessor-fns nil))
    (dolist (ithname name-list)
      (let ((fn-name (make-atom name '- ithname)))
        ;;this is where the accessor function is created
        (push `(defun ,fn-name (,name)
                 (elt ,name ,count))
              accessor-fns) 
        (incf count)))
    `(progn
       ,@(nreverse accessor-fns)
       ;;constructor function
       (defun ,(make-atom 'make- name) (&key ,@slot-descriptions)
         (list ,@name-list))
       ',name)))

>using eval in macros is always looked down upon. in fact in the "ALU
>Lisp Programming Style" by Mark Krantrowitz and Barry Margolin it says
>The general rule of thumb about EVAL is: if you think you need to use
>EVAL, you're probably wrong.

This is good advice, especially when you're writing your first
macros.  You want to delay work as much as possible, putting it into
the forms returned by the macro.  Once in a great while you need to do
some complicated side-effecting thing within the body of a macro, such
as registering something in a hash table.  You almost never need to
call EVAL within a macro.

>I can see that by using eval my structure can never be defined in a
>local scope and its functions will always be global. but even
>DEFSTRUCT seems to be global, I tried:
>1> (let ()
>     (defstruct empty
>       nothing)
>     nil)
>NIL
>
>2> (make-empty)
>#S(EMPTY NOTHING NIL)

defstruct effectively defines functions using defun, so they are
available globally, but they are defined in a local environment.  Your
example doesn't prove anything either way.  Consider:

(let ((default 'foo))
  (defstruct bar
    (baz default)))

Tim
From: Vijay L
Subject: Re: getting local bindings part 2
Date: 
Message-ID: <1eaf81aa.0206260918.17b4b0fa@posting.google.com>
······@sea-tmoore-l.dotcast.com (Tim Moore) wrote in message news:<af94r5> By this I think you mean that you don't know how to return multiple
> forms from a macro.  You do this with progn:

Thanks very much. somehow, somewhere i forgot about PROGN. i knew that
i had to return those DEFUN statements at the last statement of the
macro, and kept thinking it could be done only with VALUES (something
which i came across only very recently). in fact, before your solution
i'd been thinking of doing something like collecting all the DEFUNs
and passing them as arguments to a function which would return them
using VALUES (dont ask me to implement that)

> defstruct effectively defines functions using defun, so they are
> available globally, but they are defined in a local environment.

I wasnt aware that function defined using DEFUN was available
globally, now i know.
What i'd meant was that the structure itself could not be local to a
function alone, again this is because i didnt know that DEFUN
definitions are global

written by Alexey Dejneka
> So your macro defines accessors only in the compiler, but in the
> compiled file there are no functions! If you compile file, containing
> some defstruct^-s, restart lisp and load *compiled* file, you will not
> get any accessors and MAKE-ers.

that was another thing i didnt realize. thanks

Vijay
If at first you do not succeed, destroy all evidence that you tried
If at first you DO succeed, try not to look surprised
(or post it on usenet to get it done)
From: Alexey Dejneka
Subject: Re: getting local bindings part 2
Date: 
Message-ID: <m3hejrstjk.fsf@comail.ru>
Hello,

······@lycos.com (Vijay L) writes:

> (defmacro defstruct^ (name &rest slot-descriptions)
>   (let ((name-list (mapcar #'(lambda (x) (if (consp x)
>                                              (car x)
>                                            x))
>                            slot-descriptions))
>         (count 0))
>     (dolist (ithname name-list)
>       (let ((fn-name (make-atom name '- ithname)))
>         ;;this is where the accessor function is created
>         (eval `(defun ,fn-name (,name)
>                  (elt ,name ,count))) 
>         (incf count)))
>     ;;constructor function
>     (eval `(defun ,(make-atom 'make- name) (&key ,@slot-descriptions)
>              (list ,@name-list))))
>   (list 'quote name))

* (macroexpand-1 '(defstruct^ s a b))
'S

So your macro defines accessors only in the compiler, but in the
compiled file there are no functions! If you compile file, containing
some defstruct^-s, restart lisp and load *compiled* file, you will not
get any accessors and MAKE-ers.

Regards,
Alexey Dejneka

---
"You've said you removed a bug. Why has the code grown?"
From: Thomas A. Russ
Subject: Re: getting local bindings part 2
Date: 
Message-ID: <ymin0thvl31.fsf@sevak.isi.edu>
······@lycos.com (Vijay L) writes:

> now i have a new problem. i found out that VALUES does not work in
> macros,

Not entirely sure what you mean by this.  If you mean that macros
cannot return more than one value and have the interpreter/compiler
use the additional values, you are correct.

The standard way to handle that is to wrap the multiple items
you want to define in a PROGN which is returned.  PROGN is very
helpfully defined in the ANSI standard as being transparent when
it is encountered at top level.

> and so if i'm writing my own version of DEFSTRUCT, my macro
> should create accessor functions also, i dont know how to do this
> short of using EVAL.
> heres the code:
> 
> (defmacro defstruct^ (name &rest slot-descriptions)
>   (let ((name-list (mapcar #'(lambda (x) (if (consp x)
>                                              (car x)
>                                            x))
>                            slot-descriptions))
>         (count 0))
>     (dolist (ithname name-list)
>       (let ((fn-name (make-atom name '- ithname)))
>         ;;this is where the accessor function is created
>         (eval `(defun ,fn-name (,name)
>                  (elt ,name ,count))) 
>         (incf count)))
>     ;;constructor function
>     (eval `(defun ,(make-atom 'make- name) (&key ,@slot-descriptions)
>              (list ,@name-list))))
>   (list 'quote name))


> I can see that by using eval my structure can never be defined in a
> local scope and its functions will always be global. but even

Even worse:  
  The definitions that you have written define the accessor, etc.
functions at macro expansion time.  They don't actually emit any
code.  That means that if  you use this macro in a file and then
compile the file, none of the functions will be in the compiled
file.  You won't have them if you load the compiled file into a
different lisp image.

> so am i justified in my use of EVAL in my macro, or is there a better
> way?

No you are not justified.  Yes, there is a better way, using PROGN:

(defmacro defstruct^ (name &rest slot-descriptions)
   (let ((name-list (mapcar #'(lambda (x) (if (consp x)
                                              (car x)
                                              x))
                            slot-descriptions))
         (count 0)
         (functions ()) )
     (dolist (ithname name-list)
        ;;this is where the accessor function is created
	(push `(defun ,(make-atom name '- ithname) (,name)
                  (elt ,name ,count))
               functions)
         (incf count))
     ;;constructor function
     (push `(defun ,(make-atom 'make- name) (&key ,@slot-descriptions)
              (list ,@name-list))
	   functions)
    ;; Note that the functions are in reverse order.  If you care
    ;; about that, you could always use ,@(nreverse functions) instead.

     `(progn ,@functions
             ',name)))



-- 
Thomas A. Russ,  USC/Information Sciences Institute          ···@isi.edu