From: Tahsin Mzaik
Subject: Accessing a field of a Lisp structure
Date: 
Message-ID: <DPX_J3+@ee.eng.ohio-state.edu>
Hello Lisp experts:

I have a common lisp question:

I would like to write a macro that can access a particular field 
in a structure. 

Example: Let the structure be:
      (defstruct test a b c)

      (make-test instance)

I would like to wite a macro 'access-field' that I can invoke
something like  

      (access-field instance 'a)

in order to access field a of the structure. I have tried the
following, but it does not work:

      (defmacro access-field (instance field)
        '(setq (test-,field ,instance) any-value)
      )


Any ideas? Thanks for any reply.

From: David Loewenstern
Subject: Re: Accessing a field of a Lisp structure
Date: 
Message-ID: <1991Apr10.215214.2827@cbnewsl.att.com>
In article <·······@ee.eng.ohio-state.edu> ·····@ee.eng.ohio-state.edu (Tahsin Mzaik) writes:
>Hello Lisp experts:
>
>I have a common lisp question:
>
>I would like to write a macro that can access a particular field 
>in a structure. 
>
>Example: Let the structure be:
>      (defstruct test a b c)
>
>      (make-test instance)
>
>I would like to wite a macro 'access-field' that I can invoke
>something like  
>
>      (access-field instance 'a)
>
>in order to access field a of the structure. I have tried the
>following, but it does not work:
>
>      (defmacro access-field (instance field)
>        '(setq (test-,field ,instance) any-value)
>      )

Try:
(defmacro access-field (instance field)
  `(,(intern (format nil "~A-~A" 'test field)) ,instance))

Note that this will allow 
(access-field instance a)
to work but not, for example
(access-field instance (get-slot-name-from-some-wierd-place))

>
>
>Any ideas? Thanks for any reply.
From: Milt Epstein
Subject: Re: Accessing a field of a Lisp structure
Date: 
Message-ID: <28047A1F.44CE@ibma0.cs.uiuc.edu>
In <·····················@cbnewsl.att.com> ·····@cbnewsl.att.com (David Loewenstern) writes:

>In article <·······@ee.eng.ohio-state.edu> ·····@ee.eng.ohio-state.edu (Tahsin Mzaik) writes:
>>
>>I would like to write a macro that can access a particular field 
>>in a structure. 
>>
>>Example: Let the structure be:
>>      (defstruct test a b c)
>>
>>      (make-test instance)
>>
>>I would like to wite a macro 'access-field' that I can invoke
>>something like  
>>
>>      (access-field instance 'a)
>
>Try:
>(defmacro access-field (instance field)
>  `(,(intern (format nil "~A-~A" 'test field)) ,instance))
>
>Note that this will allow 
>(access-field instance a)
>to work but not, for example
>(access-field instance (get-slot-name-from-some-wierd-place))

Someone else also posted this question just a couple of days ago, to
which Barry Margolin responded suggesting a function like:

(defun access-field (instance field)
  (case field
    (a (test-a instance))
    (b (test-b instance))
    (c (test-c instance))))

Note that if you use a function, it doesn't matter where you get the
slot-name from.

I sent a message to Barry suggesting the INTERN-FORMAT version, and he
pointed out a couple of things:

1. It's better to use FIND-SYMBOL than INTERN, since the symbol (the
structure accessor function) should already exist.

2. The CASE version appeared to be much faster (he did some timing
test on a Symbolics).

-- 
Milt Epstein
Department of Computer Science
University of Illinois
·······@cs.uiuc.edu
From: David Loewenstern
Subject: Re: Accessing a field of a Lisp structure
Date: 
Message-ID: <1991Apr11.215248.9075@cbnewsl.att.com>
In article <·············@ibma0.cs.uiuc.edu> ·······@sunc4.cs.uiuc.edu (Milt Epstein) writes:
>In <·····················@cbnewsl.att.com> ·····@cbnewsl.att.com (David Loewenstern) writes:
>
>>In article <·······@ee.eng.ohio-state.edu> ·····@ee.eng.ohio-state.edu (Tahsin Mzaik) writes:
>>>
>>>I would like to write a macro that can access a particular field 
>>>in a structure. 
>>>
>>>Example: Let the structure be:
>>>      (defstruct test a b c)
>>>
>>>      (make-test instance)
>>>
>>>I would like to wite a macro 'access-field' that I can invoke
>>>something like  
>>>
>>>      (access-field instance 'a)
>>
>>Try:
>>(defmacro access-field (instance field)
>>  `(,(intern (format nil "~A-~A" 'test field)) ,instance))
>>
>>Note that this will allow 
>>(access-field instance a)
>>to work but not, for example
>>(access-field instance (get-slot-name-from-some-wierd-place))
>
>Someone else also posted this question just a couple of days ago, to
>which Barry Margolin responded suggesting a function like:
>
>(defun access-field (instance field)
>  (case field
>    (a (test-a instance))
>    (b (test-b instance))
>    (c (test-c instance))))
>
>Note that if you use a function, it doesn't matter where you get the
>slot-name from.

Oh, I agree.  It is, however, slower.
>
>I sent a message to Barry suggesting the INTERN-FORMAT version, and he
>pointed out a couple of things:
>
>1. It's better to use FIND-SYMBOL than INTERN, since the symbol (the
>structure accessor function) should already exist.

Maybe.  The cost of using find-symbol is that the macro writer
must add some error handling code, or else (access-field instance oops)
will generate some unreadable nonsense about test-NIL being undefined.

Besides, while it is good coding style, and helps the compiler generate
better code, to define before using a structure, 
it is legal to compile (access-field instance a) or
even (test-a instance) before loading the definition of test.

>
>2. The CASE version appeared to be much faster (he did some timing
>test on a Symbolics).

This, however, I do not believe.  After all, the macro 

(access-field instance a)

expands during compile time directly into
(test-a instance)

The function access-field *calls* test-a and so cannot possibly
be faster than test-a alone.

Worse, the macro (access-field instance c) expands into (test-c instance),
and during run-time is no slower than (access-field instance a).  The
function access-field is O(n), where n is the number of slots in the
test structure.

The only way the function could be faster than the macro is if you
are including the macroexpansion time with the execution time.

Comments, Barry?

>
>-- 
>Milt Epstein
>Department of Computer Science
>University of Illinois
>·······@cs.uiuc.edu

David Loewenstern
AT&T Bell Laboratories
14B-253 
Whippany, NJ 07981
email: ·····@whutt.att.com || whutt!davel
at&t: 201-386-6516
From: Barry Margolin
Subject: Re: Accessing a field of a Lisp structure
Date: 
Message-ID: <1991Apr14.073241.23653@Think.COM>
In article <·····················@cbnewsl.att.com> ·····@cbnewsl.att.com (David Loewenstern) writes:
>In article <·············@ibma0.cs.uiuc.edu> ·······@sunc4.cs.uiuc.edu (Milt Epstein) writes:
>>I sent a message to Barry suggesting the INTERN-FORMAT version, and he
>>pointed out a couple of things:
>>
>>1. It's better to use FIND-SYMBOL than INTERN, since the symbol (the
>>structure accessor function) should already exist.
>
>Maybe.  The cost of using find-symbol is that the macro writer
>must add some error handling code, or else (access-field instance oops)
>will generate some unreadable nonsense about test-NIL being undefined.

Both FIND-SYMBOL and INTERN will return the appropriate symbol if a valid
slot name was specified.  If a nonexistent slot name was specified, INTERN
will result in an undefined function error regarding TEST-<bad-slot-name>,
while FIND-SYMBOL will result in an attempt to funcall NIL.

>>2. The CASE version appeared to be much faster (he did some timing
>>test on a Symbolics).
>
>This, however, I do not believe.  After all, the macro 
>
>(access-field instance a)
>
>expands during compile time directly into
>(test-a instance)
>
>The function access-field *calls* test-a and so cannot possibly
>be faster than test-a alone.

Ahh, now I see your confusion.  I didn't write a macro, I wrote a function.
A macro can't be used for what the original poster wanted.  He wanted a
function that takes the slot name as an argument, so that the slot can be
specified at runtime.  His example was something like:

(defun prt (person slot)
  (print (user-slot person slot)))

Your macro version doesn't seem to be very useful to me.  If the slot name
has to be known at compile time then it is just as easy to write the call
to the accessor directly.  What's the difference between

(access-field instance a)

and

(test-a instance)

?  The original post asked for something that would be

(access-field instance 'a)

That little quote makes a BIG difference.

>The only way the function could be faster than the macro is if you
>are including the macroexpansion time with the execution time.

Now that I've explained, I think you see why the CASE version is faster.
The FORMAT-INTERN version calls FORMAT and conses a string each time it is
called.

--
Barry Margolin, Thinking Machines Corp.

······@think.com
{uunet,harvard}!think!barmar
From: David Loewenstern
Subject: Re: Accessing a field of a Lisp structure
Date: 
Message-ID: <1991Apr14.181950.21605@cbnewsl.att.com>
In article <······················@Think.COM> ······@think.com (Barry Margolin) writes:
>In article <·····················@cbnewsl.att.com> ·····@cbnewsl.att.com (David Loewenstern) writes:
>>In article <·············@ibma0.cs.uiuc.edu> ·······@sunc4.cs.uiuc.edu (Milt Epstein) writes:
>>>I sent a message to Barry suggesting the INTERN-FORMAT version, and he
>>>pointed out a couple of things:
>>>
>>>1. It's better to use FIND-SYMBOL than INTERN, since the symbol (the
>>>structure accessor function) should already exist.
>>
>>Maybe.  The cost of using find-symbol is that the macro writer
>>must add some error handling code, or else (access-field instance oops)
>>will generate some unreadable nonsense about test-NIL being undefined.
>
>Both FIND-SYMBOL and INTERN will return the appropriate symbol if a valid
>slot name was specified.  If a nonexistent slot name was specified, INTERN
>will result in an undefined function error regarding TEST-<bad-slot-name>,
>while FIND-SYMBOL will result in an attempt to funcall NIL.
Exactly. So the version with Find-Symbol, when run, will generate a 
TEST-NIL error, and the version with INTERN, when run, will generate a
TEST-<mumble> error.  Test-<mumble> is of course more useful for debugging,
although either method is lazier and less preferable than placing
explicit error-checking code in the macro definition.

>
>>>2. The CASE version appeared to be much faster (he did some timing
>>>test on a Symbolics).
>>
>>This, however, I do not believe.  After all, the macro 
>>
>>(access-field instance a)
>>
>>expands during compile time directly into
>>(test-a instance)
>>
>>The function access-field *calls* test-a and so cannot possibly
>>be faster than test-a alone.
>
>Ahh, now I see your confusion.  I didn't write a macro, I wrote a function.
>A macro can't be used for what the original poster wanted.  He wanted a
>function that takes the slot name as an argument, so that the slot can be
>specified at runtime.  His example was something like:

Ahh, now I see your confusion.  The poster I responded to wasn't the one
who wanted a function.  He said:

>>>I would like to wite a macro 'access-field' that I can invoke
>>>something like  
>>>
>>>      (access-field instance 'a)
>>

>
>(defun prt (person slot)
>  (print (user-slot person slot)))
>
>Your macro version doesn't seem to be very useful to me.  If the slot name
>has to be known at compile time then it is just as easy to write the call
>to the accessor directly.  What's the difference between
>
>(access-field instance a)
>
>and
>
>(test-a instance)
>
>?  The original post asked for something that would be
>
>(access-field instance 'a)
>
>That little quote makes a BIG difference.

Since the poster specifically asked for a macro, not a function, I
assumed the quote was to be considered syntactic sugar placed there
for consistency with function syntax.


As far as utility, I can think of a reason:
unity of syntax between function and macro, coupled with a need
for speed of access, as in:
(defmacro access-field (struc slot)
  (if (OR (ATOM slot) (EQ (FIRST slot) 'QUOTE))
      `(,(intern (format nil "~a-~a" 'test
                         (IF (ATOM slot) slot (SECOND slot)))
        ,struc)
      `(access-field-function ,struc ,slot)))  

I have actually seen this done in a real program.  Why?  So tools
like the following could be created:

(defmacro most-recent (struc slot) `(CDAR (access-field ,struc ,slot)))
(defmacro value-at-time (struc slot time)
  `(CDR (ASSOC ,time (access-field ,struc ,slot))))
and so forth.

>
>>The only way the function could be faster than the macro is if you
>>are including the macroexpansion time with the execution time.
>
>Now that I've explained, I think you see why the CASE version is faster.
>The FORMAT-INTERN version calls FORMAT and conses a string each time it is
>called.
>
 
Actually, for structures with *very large* numbers of slots, format-intern
is still faster, but I certainly didn't mean for format-intern to be used
when the formatting was done at run time.  For normally-sized structures,
looking up the slot using CASE is faster, and even for huge structures,
format-intern won't be any faster than a hash-table.  The CASE method
is a bit of a pain if the structure is still under development,
however, and is no better than format-intern when the slot-to-accessor
translation is to be done at compile-time.

>--

>Barry Margolin, Thinking Machines Corp.
>
>······@think.com
>{uunet,harvard}!think!barmar


Barry, no one is questioning your understanding of Lisp structures.  
It appears, however, that one of us misunderstood the question.

David Loewenstern
AT&T Bell Laboratories
14B-253 
Whippany, NJ 07981
email: ·····@whutt.att.com || whutt!davel
at&t: 201-386-6516
From: Barry Margolin
Subject: Re: Accessing a field of a Lisp structure
Date: 
Message-ID: <1991Apr16.062652.15944@Think.COM>
In article <······················@cbnewsl.att.com> ·····@cbnewsl.att.com (David Loewenstern) writes:
>Exactly. So the version with Find-Symbol, when run, will generate a 
>TEST-NIL error, and the version with INTERN, when run, will generate a
>TEST-<mumble> error.  Test-<mumble> is of course more useful for debugging,
>although either method is lazier and less preferable than placing
>explicit error-checking code in the macro definition.

In the functional case, the call to ACCESS-FIELD will be on the stack, so
it's not hard to tell which erroneous field name was specified.  But I
admit that the difference between using FIND-SYMBOL and INTERN for this
kludge is not really that important; I was only being complete, and trying
to find as many things wrong with the INTERN/FORMAT version as I could.

>Ahh, now I see your confusion.  The poster I responded to wasn't the one
>who wanted a function.  He said:
>
>>>>I would like to wite a macro 'access-field' that I can invoke
>>>>something like  
>>>>
>>>>      (access-field instance 'a)

>Since the poster specifically asked for a macro, not a function, I
>assumed the quote was to be considered syntactic sugar placed there
>for consistency with function syntax.

First of all, I was responding to a different posting.  But even if I had
responded to the above posting, I probably would have assumed the poster
had misused the term "macro", and meant "function", since he quoted the
slot name.  Even if he did want a macro, I still would have assumed that
the slot name argument is intended to be evaluated at runtime.

>As far as utility, I can think of a reason:
>unity of syntax between function and macro, coupled with a need
>for speed of access, as in:
>(defmacro access-field (struc slot)
>  (if (OR (ATOM slot) (EQ (FIRST slot) 'QUOTE))
>      `(,(intern (format nil "~a-~a" 'test
>                         (IF (ATOM slot) slot (SECOND slot)))
>        ,struc)
>      `(access-field-function ,struc ,slot)))  

I don't like the special-casing of (ATOM slot) in this.  It screws up:

(let ((slot-name (prompt-user-for-slot-name)))
  (access-field structure slot-name))

This will end up trying to access the slot named "slot-name".  A better
version would be:

(defmacro access-field (struc slot)
  (if (constantp slot)
      `(,(intern (format nil "~a-~a" 'test (eval slot)) 'test-pkg)
        ,struc)
      `(access-field-function ,struc ,slot)))

>I have actually seen this done in a real program.  

And I'll bet they usually forget to include the second argument to INTERN.
This is necessary to allow the invocation of the macro to be in a different
package from the structure definition.

Note that the macro could use CASE at expansion time, analogously with my
functional version:

(defmacro access-field (struc slot)
  (if (constantp slot)
      `(,(ecase (eval slot)
	   (a 'test-a)
	   (b 'test-b)
	   (c 'test-c))
        ,struc)
      `(access-field-function ,struc ,slot)))


>						    Why?  So tools
>like the following could be created:
>
>(defmacro most-recent (struc slot) `(CDAR (access-field ,struc ,slot)))
>(defmacro value-at-time (struc slot time)
>  `(CDR (ASSOC ,time (access-field ,struc ,slot))))
>and so forth.

The few times I've wanted macros like this, I've passed the entire accessor
name as a parameter, so that the macro isn't tied to a particular structure
type.  Or I write functions and pass #'<accessor> as a parameter.  Contrary
to what some Schemers believe, not all CLers avoid funargs.

>The CASE method
>is a bit of a pain if the structure is still under development,

One thing I've been meaning to mention is that you could write a macro that
expands into a DEFSTRUCT invocation followed by a definition of a generic
field accessor for the structure.  The slot description arguments to the
new macro would be used to define the generic field accessor.  Then you
don't have to update two places when you add or delete fields.  Something
like:

(defun defstruct-with-generic-accessor (name-and-options &body slots)
  `(progn
     (defstruct ,name-and-options .,slots)
     ,(let* ((name (if (atom name-and-options)
		       name-and-options
		       (car name-and-options)))
	     (options (and (listp name-and-options)
			   (cdr name-and-options)))
	     (conc-name (assoc :conc-name options))
	     (slot-names (mapcar #'(lambda (slot)
				     (if (atom slot)
					 slot
					 (car slot)))
				 slots)))
	(setq conc-name
	      (cond ((null conc-name) (format nil "~a-" name))
		    ((second (conc-name)))
		    (t "")))
	`(defun ,(intern (format nil "ACCESS-~a" name)) (,name slot)
	   (ecase slot
	     .,(mapcar #'(lambda (slot-name)
			   `(,slot-name (,(intern (format nil "~a~a"
						          conc-name slot-name))
				    slot)))
		       slot-names))))))
--
Barry Margolin, Thinking Machines Corp.

······@think.com
{uunet,harvard}!think!barmar