From: DvdAvins
Subject: Newbie needs macro help
Date: 
Message-ID: <20010302020304.28573.00000109@ng-fp1.aol.com>
Well, now that I've probably pissed off some of the people who can help me (by
saying something in another thread that Xah Lee agreed with) I'[ll ask for help
anyway.

To get my feet wet, I'm re-writing in Lisp a program I wrote in Basic about 15
years ago and in C about 10 years ago. In it I have a collection of multi-value
records of a single type. I'm implementing them by defining a structure and
putting the structure instances in a hash table. Instead of using a structure,
I could have used a class, but I didn't see a need for polymorphic methods. I
could have used assoc-lists or property-lists, but I assume structures are more
scalable. The structure and hash table are defined thus:

   (defstruct (Entrant (:conc-name Ent-))
      FullName
      #|(Rating 1500)|#
      Rating
      Score
      Opponents
      TempRating)

   (setf Tourney (make-hash-table))

Now I wanted to define a short-hand way to get the value of a field from an
item in the hash table. I want to be able to pass the hash table key and the
structure field name and retrieve (or set) the value. My first thought was to
define a function, but I was surprised to learn (or am I wrong?) that there is
no function that takes the name of a structure field (or a class member) as a
parameter. The only wat to retrieve the value seems to be with a function where
the name of the field is part of the name of the function. OK, I guess that
means I need a macro...

So what I want is a
   (defmacro EntProp (ID Prop) ...
that returns
   (Ent-Prop (GetHash 'ID Tourney))

What I can't figure out is how to get Ent-Prop as a symbol, rather than a
string. How can I concatenate the prefix Ent- with the argument Prop and refer
to the function with the concatenated name?

If you think I should be using different data structures (to replace either the
defstruct or the hash table) tell me why. But I'd still be curuious how to
solve the problem with my planned implementation.

BTW, the line that initializes rating is commented out and replaced because
Corman Lisp hangs otherwise? Am I doing something stupid there, or is it
Corman? Once Yahoo! finally fixes my account, I can ask on the Corman Lisp
list. But for now this group, the HyperSpec and Graham's ANSI Common Lisp are
all my resources.

From: Paul Rudin
Subject: Re: Newbie needs macro help
Date: 
Message-ID: <wkhf1c3a57.fsf@ntlworld.com>
>>>>>  DvdAvins  <········@aol.com> writes:


  > What I can't figure out is how to get Ent-Prop as a symbol, rather
  > than a string. How can I concatenate the prefix Ent- with the
  > argument Prop and refer to the function with the concatenated
  > name?

You can use INTERN to go from a string to a  symbol.
From: Kent M Pitman
Subject: Re: Newbie needs macro help
Date: 
Message-ID: <sfwy9uofmux.fsf@world.std.com>
········@aol.com (DvdAvins) writes:

> Well, now that I've probably pissed off some of the people who can
> help me (by saying something in another thread that Xah Lee agreed
> with) I'[ll ask for help anyway.

I'm can't believe anyone cares one way or another what Xah says.
Sometimes he says reasonable things, and sometimes he is wholly off
the wall.  I see no method in it and personally just shrug it all off
as entirely harmless.

> To get my feet wet, I'm re-writing in Lisp a program I wrote in
> Basic about 15 years ago and in C about 10 years ago. In it I have a
> collection of multi-value records of a single type. I'm implementing
> them by defining a structure and putting the structure instances in
> a hash table. Instead of using a structure, I could have used a
> class, but I didn't see a need for polymorphic methods.

This isn't the reason for using structures or not, btw.  You can still write
methods on structures.  You can still write methods on primitive classes.
The main distinguishing features between classes and structures are:
 
 * in most implementations, structures are faster
 
 * structures are single inheritance, which is what tends to make them
   faster, since implementations use a trick that the lisp machine flavor
   system called ordered-instance-variables but which didn't make it into
   clos.  that is, accessors can dead-reckon the location of a slot without
   going through a method table because of the single inheritance.

 * structures are harder to redefine
 
 * structure definition syntax is more low-tech and baroque
 
Personally, I recommend always using classes unless efficiency becomes a 
bottleneck, although I am betting some people who would make the exact 
opposite advice.

What you've done is defensible so far, though not the choice I would
have made, even if the reason you did it isn't what I consider a good
one.  The outcome is fine up to here.  But later I hope you will conclude
that classes are better for your purpose.

> I could have
> used assoc-lists or property-lists, but I assume structures are more
> scalable.

Well, they are constant time access for the slots.  alists and plists
will take time proportional to the placement of the key in the list.
Use alists or plists if you either don't know what your keys are, or if
you are going to merge keys from disparate types (getting the effect of
dynamic type mixing), and if you don't need class dispatch in methods.
You sound like you're on the right track here.

> The structure and hash table are defined thus:
> 
>    (defstruct (Entrant (:conc-name Ent-))
>       FullName
>       #|(Rating 1500)|#
> 
 
If you're going to be adding and removing slots dynamically as you go,
you'll find that most implementations support this best for defclass
rather than defstruct.  With defstruct you have to recompile all
callers of all constructors and accessors when you change the slot
arrangement; with defclass you only have to recompile the defclass
form.

>       Rating
>       Score
>       Opponents
>       TempRating)
> 
>    (setf Tourney (make-hash-table))
> 
> Now I wanted to define a short-hand way to get the value of a field from an
> item in the hash table. I want to be able to pass the hash table key and the
> structure field name and retrieve (or set) the value.  My first thought was to
> define a function, but I was surprised to learn (or am I wrong?) that there is
> no function that takes the name of a structure field (or a class member) as a
> parameter.

That's right.  That would involve introspection and the introspective
part of CLOS is not present in the standard.  Most implementations
support a MOP (see "The Art of the MetaObject Protocol" by Gregor
Kiczales -- hope I spelled his name right) and do have a way to do
this, but it is technically not standard.  I will confine my remarks
to what is standard although AMOP gives you a direct way to do this,
since I don't have a copy of AMOP handy.  You can probably order a
copy from Amazon.

Also, if you used classes instead of structures, you could use SLOT-VALUE.
That would make your problem go away.  Some implementations will let you
do SLOT-VALUE on a structure, but I'm not sure if that's required by
the standard for metaclass STRUCTURE.

> The only wat to retrieve the value seems to be with a function where
> the name of the field is part of the name of the function. OK, I guess that
> means I need a macro...
> 
> So what I want is a
>    (defmacro EntProp (ID Prop) ...
> that returns
>    (Ent-Prop (GetHash 'ID Tourney))

Using only the CL standard, and confining yourself to structures, you
could instead of using DEFSTRUCT for your initial class use a macro
that both did the defstruct and saved away the name.


> What I can't figure out is how to get Ent-Prop as a symbol, rather
> than a string.

Well, first, if you are using 'ent- and 'prop and merging them, the result
will be "ENT-PROP" not "Ent-Prop" since the reader is always uppercasing
symbols internally by default to give them a canonical case and to avoid you
having name dyslexia if you aren't careful about your casing.  

To convert a symbol name to an interned symbol in a given package, you can
use INTERN.  This is yet another reason not to use structures by the way,
although it opens a whole can of worms to explain in detail with the knowledge
you appear to have now.  There are a host of tricky problems in using INTERN
and the package system for randomly constructed symbols and this is one of
the key reasons that the :CONC-NAME option was removed when DEFCLASS was
defined as a successor to DEFSTRUCT.  Specifying the symbols for accessors
separate fromt he slot names may seem tedious, but avoids ever having to do
INTERN.  (ALSO, any use of INTERN makes it opaque to tree-shakers what symbols
you are using in your image, since dynamically at runtime you could need a
new symbol.  Effectively, this means when you dump out an executable lisp
image, you are forced to dump out your entire environment because the garbage
collector can't usually prove that you are only using a subset of
available symbols.)

> How can I concatenate the prefix Ent- with the
> argument Prop and refer to the function with the concatenated name?

;;; Save current binding of package for future use 
(defvar *prop-package* *package*) 

(defun prop-accessor-symbol (prop-symbol)
  (intern (concatenate 'string "ENT-" (symbol-name prop-symbol))
          *prop-package*))

> If you think I should be using different data structures (to replace
> either the defstruct or the hash table) tell me why.

Use DEFCLASS.

(defclass Entrant ()
  ((FullName   :initarg :FullName   :accessor Ent-FullName                 )
   (Rating     :initarg :Rating     :accessor Ent-Rating     :initform 1500)
   (Score      :initarg :Score      :accessor Ent-Score                    )
   (Opponents  :initarg :Opponents  :accessor Ent-Opponents                )
   (TempRating :initarg :TempRating :accessor Ent-TempRating               )))

The hash table is fine.

> But I'd still
> be curuious how to solve the problem with my planned implementation.
 
Let me know if I've left something unclear.

> BTW, the line that initializes rating is commented out and replaced because
> Corman Lisp hangs otherwise? Am I doing something stupid there, or is it
> Corman? Once Yahoo! finally fixes my account, I can ask on the Corman Lisp
> list. But for now this group, the HyperSpec and Graham's ANSI Common Lisp are
> all my resources.

Looks to me like it should work.  I suspect an implementation glitch of
some kind.  I recommend you learn about #+ and #- for implementation
conditionals.  Don't know what Corman uses for its conditionals (see
*FEATURES* in CLHS and compare the value in your implementation), but
hopefully it is not :CCL since already two implementations I know of have
used that symbol.  Assuming its :CORMAN, then

 #+Corman Rating
 #-Corman (Rating 1500)

in the original DEFSTRUCT would better express your intent than merely
commenting out the correct code for other implementations where it ought to
work fine.
From: Jason Trenouth
Subject: Re: Newbie needs macro help
Date: 
Message-ID: <csj7atov2b917d7pgqq0icmvrq3tfvoq48@4ax.com>
On Fri, 2 Mar 2001 11:09:58 GMT, Kent M Pitman <······@world.std.com>
wrote:

>  * structures are single inheritance, which is what tends to make them
>    faster, since implementations use a trick that the lisp machine flavor
>    system called ordered-instance-variables but which didn't make it into
>    clos.  that is, accessors can dead-reckon the location of a slot without
>    going through a method table because of the single inheritance.

For interest sake: Dylan allows classes to be declared "primary" to try and
get back the simple indexing lost through multiple inheritance, at least
for one of the inherited sets of slots.

__Jason
From: Kent M Pitman
Subject: Re: Newbie needs macro help
Date: 
Message-ID: <sfwpufwqe4h.fsf@world.std.com>
Jason Trenouth <·····@harlequin.com> writes:

> On Fri, 2 Mar 2001 11:09:58 GMT, Kent M Pitman <······@world.std.com>
> wrote:
> 
> >  * structures are single inheritance, which is what tends to make them
> >    faster, since implementations use a trick that the lisp machine flavor
> >    system called ordered-instance-variables but which didn't make it into
> >    clos.  that is, accessors can dead-reckon the location of a slot without
> >    going through a method table because of the single inheritance.
> 
> For interest sake: Dylan allows classes to be declared "primary" to try and
> get back the simple indexing lost through multiple inheritance, at least
> for one of the inherited sets of slots.

Indeed.  I wished there were also secondary and tertiary and so on, where one
would declare the primary they were bound to but could swap in any secondary,
treating the first n slots as opaque.  (Probably using the primary/secondary
naming is the wrong way to do this, and reserving n slots is better, since
it generalizes better.)
From: Pierre R. Mai
Subject: Re: Newbie needs macro help
Date: 
Message-ID: <87snkwbdfg.fsf@orion.bln.pmsf.de>
········@aol.com (DvdAvins) writes:

> Instead of using a structure, I could have used a class, but I
> didn't see a need for polymorphic methods. I could have used
> assoc-lists or property-lists, but I assume structures are more
> scalable. The structure and hash table are defined thus:

Note that since defstruct[1] also defines classes you can define
polymorphic methods on structures just like you can do for classes
define by defclass.  The main differences between defstruct and
defclass is that classes defined by defstruct have a metaclass of
structure-class, instead of standard-class.  This has the following
consequences:

- You only get single-inheritance (via the :include option to
  defstruct),
- You can't redefine structures to have a different layout in a
  running system,
- You can't change the class of instances using change-class,
- You can't use the standard functions to create instances or access
  slots.

Additionally the use of defstruct automatically generates several
functions and accessors tailored to the structure class.

> Now I wanted to define a short-hand way to get the value of a field
> from an item in the hash table. I want to be able to pass the hash
> table key and the structure field name and retrieve (or set) the
> value. My first thought was to define a function, but I was surprised
> to learn (or am I wrong?) that there is no function that takes the
> name of a structure field (or a class member) as a parameter. The

For classes of type standard-class you have slot-value, which does
exactly what you want, e.g.

(defclass demo ()
  ((a :initarg :a)
   (b :initarg :b)))

(make-instance 'demo :a 1 :b 2)
=> #<DEMO ...>

(slot-value * 'a)
=> 1

(slot-value ** 'b)
=> 2

Your problem is that the standard doesn't require that slot-value also
works for instances with a structure-class metaclass.  So for
structures there is no function to access slots by name.

That being said, implementations are free to add support for using
slot-value with structure instances, and many have done so.  For
example ACL, LispWorks for Win/Lin, CMU CL, CLISP and ECLS have added
this support (and generally all implementations using PCL should also
have support for this).  I don't know about implementations for other
platforms, but I'd be surprised if not many of them also added support
for this.

If you have said support, then you can simply implement your function
like this:

(defstruct entrant ...)

(defvar *tourney* (make-hash-table))

(defun entry-value-by-key (key slot-name)
  (let ((instance (gethash key *tourney*)))
    (unless instance
      (error "No entry found for key ~S." key))
    (slot-value instance slot-name)))

You can also add the corresponding setf function, like this:

(defun (setf entry-value-by-key) (new-value key slot-name)
  (let ((instance (gethash key *tourney*)))
    (unless instance
      (error "No entry found for key ~S." key))
    (setf (slot-value instance slot-name) new-value)))

If the implementation(s) you are interested in don't support using
slot-value on structure instances,  you'd have to resort to one of
several hacks, the details of which are outlined in the old FAQ for
comp.lang.lisp.  But I'd just switch to using defclass in such a
situation.

> So what I want is a
>    (defmacro EntProp (ID Prop) ...
> that returns
>    (Ent-Prop (GetHash 'ID Tourney))
> 
> What I can't figure out is how to get Ent-Prop as a symbol, rather
> than a string. How can I concatenate the prefix Ent- with the
> argument Prop and refer to the function with the concatenated
> name?

This is something you can easily get wrong, for package system and
reader-case issues.  It is also something you should IMHO avoid, and
it is only for legacy reasons that defstruct does this sort of symbol
mangling.  It was corrected with defclass, which lets you specify the
complete names to be used for accessor functions, etc.

If you still need to do it, here's how.  Note that this is a special
hack, since we can't get at the value of the :conc-name option in
retrospect (at least not portably).

(defmacro ent-slot-value (instance slot-name)
  (unless (symbolp slot-name)
    (error "The slot-name must be a symbol and not ~S." slot-name))
  (let* ((structure-package (find-package :THE-STRUCTURE-PACKAGE))
         (accessor-name (concatenate 'string (symbol-name #:Ent-)
                                     (symbol-name slot-name)))
         (accessor-symbol (intern accessor-name structure-package)))
    `(,accessor-symbol ,instance)))

Where :THE-STRUCTURE-PACKAGE is to be the package that is/was/will be
current when you execute the defstruct form (see explanations in the
CLHS entry on defstruct under the :conc-name option).

Again, though, don't do it, IMHO...

> BTW, the line that initializes rating is commented out and replaced because
> Corman Lisp hangs otherwise? Am I doing something stupid there, or is it
> Corman? Once Yahoo! finally fixes my account, I can ask on the Corman Lisp
> list. But for now this group, the HyperSpec and Graham's ANSI Common Lisp are
> all my resources.

(defstruct (Entrant (:conc-name Ent-))
  FullName
  (Rating 1500)
  Score
  Opponents
  TempRating)

Should just work...

Regs, Pierre.

Footnotes: 
[1]  All of the stuff below only applies if you don't provide the type
     option to defstruct.  See CLHS entry on defstruct for
     explanations of this restriction...

-- 
Pierre R. Mai <····@acm.org>                    http://www.pmsf.de/pmai/
 The most likely way for the world to be destroyed, most experts agree,
 is by accident. That's where we come in; we're computer professionals.
 We cause accidents.                           -- Nathaniel Borenstein
From: Tim Bradshaw
Subject: Re: Newbie needs macro help
Date: 
Message-ID: <nkjlmqogw4o.fsf@tfeb.org>
········@aol.com (DvdAvins) writes:

> Now I wanted to define a short-hand way to get the value of a field from an
> item in the hash table. I want to be able to pass the hash table key and the
> structure field name and retrieve (or set) the value. My first thought was to
> define a function, but I was surprised to learn (or am I wrong?) that there is
> no function that takes the name of a structure field (or a class member) as a
> parameter. The only wat to retrieve the value seems to be with a function where
> the name of the field is part of the name of the function. OK, I guess that
> means I need a macro...
> 
> So what I want is a
>    (defmacro EntProp (ID Prop) ...
> that returns
>    (Ent-Prop (GetHash 'ID Tourney))
> 

Well, one approach would be to use DEFCLASS and SLOT-VALUE! 

(defun entprop (id prop)
  (slot-value (gethash id hashtable) prop))

(sorry I may have arg orders backwards, I can never remember).

--tim
From: DvdAvins
Subject: Re: Newbie needs macro help
Date: 
Message-ID: <20010302181336.04325.00000137@ng-mg1.aol.com>
Thank you everybody. The existance of slot-value would be sufficient by itself
to convince to use defclass. Thank you Kent and others for giving me addtional
reasons as well. I don't have time to quote each response, but I've found them
helpful beyond the answer to my specific question.

I imagine I'll be back for more help at some point. I don't like relying on
help in fora where I can't also provide some. That gives me more incventive to
get up to speed quickly.
From: Kent M Pitman
Subject: Re: Newbie needs macro help
Date: 
Message-ID: <sfwy9unrbg3.fsf@world.std.com>
········@aol.com (DvdAvins) writes:

> I imagine I'll be back for more help at some point. I don't like relying on
> help in fora where I can't also provide some.

Heh.  Don't worry about it.  We're into time shifting.  Get what help you need
now, and give some back in 2010.  No one here is keeping score as long as they
feel the people they're talking to are listening.

Besides, for every person who asks a question, there may be three others who
have the same question but don't... 

> That gives me more incventive to get up to speed quickly.

Hopefully just the rapid pace of life in the computer world is enough reason
to do that.  One can't spend long doing much of anything in the world any
more.  (Alas...)
From: Barry Margolin
Subject: Re: Newbie needs macro help
Date: 
Message-ID: <dTPn6.36$_47.20015@burlma1-snr2>
In article <················@naggum.net>, Erik Naggum  <····@naggum.net> wrote:
>  The short answer to your question is the function slot-value.

The other short answer is to read the FAQ.  There's a question/answer in
there about how to access structure slots by name.

-- 
Barry Margolin, ······@genuity.net
Genuity, Burlington, 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.