Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Structures

8 views
Skip to first unread message

dstein64

unread,
Mar 29, 2008, 3:31:53 PM3/29/08
to
I've been trying to learn Lisp for a few weeks now. I've read most of
Practical Common Lisp, and noticed that there is no mention of
structures or defstruct. I did not read the sections on CLOS, so I am
assuming that objects can probably achieve all that structures did,
but it seems that a lot of legacy code, and code that I am writing for
my Artificial Intelligence class will use structures.

Is it still common to use structures in common lisp code? Is there any
reason I should avoid them? It seems that some of the newer guides do
not mention them. Thanks!

Tayssir John Gabbour

unread,
Mar 29, 2008, 4:06:23 PM3/29/08
to
On Mar 29, 8:31 pm, dstein64 <DStei...@gmail.com> wrote:
> Is it still common to use structures in common lisp code? Is there
> any reason I should avoid them? It seems that some of the newer
> guides do not mention them. Thanks!

My personal experience is that structures are very uncommon. (Maybe
someone has more accurate arguments than I pro/con because they're
just not a serious part of my world.)

However, I use it when dealing with peoples' highly list-oriented
code, where they store info in lists like:

(name address city state)

and it's horrible because you have to remember what position the
city's supposed to be in or whatever. Structures come to the rescue
because they have this handy feature where a struct can really be a
list:

CL-USER> (defstruct (location (:type list))
name address city state)
LOCATION
CL-USER> (make-location)
(NIL NIL NIL NIL)
CL-USER> (make-location :address 10)
(NIL 10 NIL NIL)
CL-USER> (location-name '(1 2 3 4))
1
CL-USER> (location-address '(1 2 3 4))
2


A lot of people don't like OOP, and they will use an old fashioned
list-oriented style like this. Or maybe their IDE doesn't just make
writing defclass instant, so they're lazy. Well, this feature of
structures lets me handle their code without needing to perform
serious surgery.


Tayssir

tim

unread,
Mar 29, 2008, 5:01:35 PM3/29/08
to
On Sat, 29 Mar 2008 13:06:23 -0700, Tayssir John Gabbour wrote:

> On Mar 29, 8:31 pm, dstein64 <DStei...@gmail.com> wrote:
>> Is it still common to use structures in common lisp code? Is there
>> any reason I should avoid them? It seems that some of the newer
>> guides do not mention them. Thanks!
>

I started off using lists for my compiler but eventually I got sick of
remembering that (nth 11 data-item) was level-nbr, etc. Especially I got
sick of forgetting these pieces of information. So I switched to structs to
hold the various parse tree elements. This has worked quite well.

* Gives fields meaningful names.
* Provides some error checking at run time.

Lisp allows structures to incorporate other structures: "(:include
included-structure-name {slot-description}*)". This is good because all my
grammar structures can share a common header.

On the down side it makes the code more verbose at times. One problem with
completely dynamic typing is that the compiler does not know the types of
data and so you always have to tell it at some cost in tokens you have to
type in. Also defstruct by default names the accessors
[struct-name]-[field-name] which makes for long names.

Another downside is that generic routines that walk trees and lists no
longer work because they don't understand structures. I wrote a structure
walking function that supports a visitor type pattern to get around this.

Tim

Pascal Bourguignon

unread,
Mar 29, 2008, 5:49:27 PM3/29/08
to
dstein64 <DSte...@gmail.com> writes:

> I've been trying to learn Lisp for a few weeks now. I've read most of
> Practical Common Lisp, and noticed that there is no mention of
> structures or defstruct. I did not read the sections on CLOS, so I am
> assuming that objects can probably achieve all that structures did,
> but it seems that a lot of legacy code, and code that I am writing for
> my Artificial Intelligence class will use structures.
>
> Is it still common to use structures in common lisp code?

Yes. But perhaps more in Q&D exploratory code than in production code.

> Is there any reason I should avoid them?

No reason whatsoever. On the contrary, they even might be slightly
more efficient than CLOS objects.

> It seems that some of the newer guides do
> not mention them. Thanks!

What you can do with structures you cannot with CLOS objects:

You can use lists or vectors to store your data, and define a
structure to access the fields.

That is, it's very practical to be able to define literal data such as:

(defparameter *db* '(("Bart" "Simpson" "Skateboard")
("Lisa" "Simpson" "Microscope")
("Wendy" "Darling" "Doll")))

(defstruct (child (:type list)) first-name surname toy)

(dolist (child *db*)
(format t "~A likes to play with a ~(~A~).~%"
(child-first-name child)
(child-toy child)))

Bart likes to play with a skateboard.
Lisa likes to play with a microscope.
Wendy likes to play with a doll.


Which is much better than:

(dolist (child *db*)
(format t "~A likes to play with a ~(~A~).~%"
(car child) (caddr child)))

(defparameter *pentagon* '( #(0 0 0)
#(1 0 0)
#(1 1 0)
#(0 1 0)
#(0.3 1.3) ))

(defstruct (point (:type vector) x y z))
(dolist (point *pentagon*)
(draw-point (point-x point) (point-y point)))


Even for normal structures, defstruct may be more practical than defclass:

(defstruct employee first-name surname address salary)

vs.

(defclass employee ()
((first-name :accessor employee-first-name :initarg :first-name :initform nil)
(surname :accessor employee-surname :initarg :surname :initform nil)
(address :accessor employee-address :initarg :address :initform nil)
(salary :accessor employee-salary :initarg :salary :initform nil)))
(defmethod print-object ((self employee) stream)
(format stream "#S(~S :FIRST-NAME ~S :SURNAME ~S :ADDRESS ~S :SALARY ~S)"
(employee-first-name self)
(employee-surname self)
(employee-address self)
(employee-salary self))
self)
(defmethod copy-employee ((self employee))
(make-instance 'employee
:first-name (employee-first-name self)
:surname (employee-surname self)
:address (employee-address self)
:salary (employee-salary self)))
(defun make-employee (&rest args)
(apply (function make-instance) 'employee args))
(defun employeep (object) (typep object 'employee))
;; ...


So you can understand that when you want to stay in the flow, you'll
rather use defstruct than defclass...

defstruct can even do simple inheritance, by inclusion of fields of a
"super-struct".


So defstruct has certainly its uses. But as your application grows,
often you will switch from defstruct to defclass when you start
needing the features only CLOS provides. The right thing to do, is to
start with your own macro, that can expand to defstruct at first, and
when your application grows, you switch to defclass.

(defmacro defentity (name ...) ... `(progn (defstruct ...) ...))

Often, when you define various application specific structures or
objects, you define at the same time a few other functions or methods,
so it's natural to use a macro already. After all, lists, vectors,
structures or objects, they're CS notions, not application domain
notions. Nobody cares about which is used. So you should abstract
them away, and hide them in application level macros.

--
__Pascal Bourguignon__ http://www.informatimago.com/

PLEASE NOTE: Some quantum physics theories suggest that when the
consumer is not directly observing this product, it may cease to
exist or will exist only in a vague and undetermined state.

Ken Tilton

unread,
Mar 29, 2008, 6:20:32 PM3/29/08
to

tim wrote:
> On Sat, 29 Mar 2008 13:06:23 -0700, Tayssir John Gabbour wrote:
>
>
>>On Mar 29, 8:31 pm, dstein64 <DStei...@gmail.com> wrote:
>>
>>>Is it still common to use structures in common lisp code? Is there
>>>any reason I should avoid them? It seems that some of the newer
>>>guides do not mention them. Thanks!

Structures are fine and much faster than CLOS, go for it. What I find is
that often I will end up needing something CLOS offers and something
that starts as a struct eventually becomes a CLOS class.

>>
>
> I started off using lists for my compiler but eventually I got sick of
> remembering that (nth 11 data-item) was level-nbr, etc. Especially I got
> sick of forgetting these pieces of information. So I switched to structs to
> hold the various parse tree elements. This has worked quite well.
>
> * Gives fields meaningful names.
> * Provides some error checking at run time.
>
> Lisp allows structures to incorporate other structures: "(:include
> included-structure-name {slot-description}*)". This is good because all my
> grammar structures can share a common header.
>
> On the down side it makes the code more verbose at times. One problem with
> completely dynamic typing is that the compiler does not know the types of
> data and so you always have to tell it at some cost in tokens you have to
> type in. Also defstruct by default names the accessors
> [struct-name]-[field-name] which makes for long names.

Hunh? As you noted, that is merely the default. It is no big deal to
supply the :conc-name option.

kenny

--
http://smuglispweeny.blogspot.com/
http://www.theoryyalgebra.com/

"In the morning, hear the Way;
in the evening, die content!"
-- Confucius

Kent M Pitman

unread,
Mar 29, 2008, 7:30:18 PM3/29/08
to
dstein64 <DSte...@gmail.com> writes:

I recommend using classes always (even though they are sometimes
slower, depending on circumstance and implementation) and optimizing
your code once you get to the point that you have a deliverable
application that is not acceptably fast because of its speed, and
where replacing the class with a struct will fix that.

In other words, I don't think the difference in speed of structs
(which is sometimes noticeable, but again depending on situation) is
worth the flexibility cost, and I think that many people prematurely
optimize these things in a way that inhibits their ability to think
about problems usefully.

Often, in the end, one doesn't need the full power of [standard]
classes. But one might. And if you put an obstacle in your path (by
having things implemented initially as structs), the barrier to using
the full features of classes is often the revamping of a bunch of
stupid syntax, such that you might avoid it when you shouldn't. It's
better to focus on optimization when it's known that it will matter.

So many applications are thrown away before they are ever deployed,
and not for reasons of speed, that I just don't think it matters.

tim

unread,
Mar 29, 2008, 9:18:07 PM3/29/08
to

The language has a spec of 1,000 pages and a huge number of productions.
Some structures can be reused across multiple productions but many can't.
So there are many structures and many fields. Also if overriding one needs
to check that the name is not a duplicate, used elsewhere, which is more
work.

(* trivial large)
=> large

With objects it might not be so large a job because generic
functions get built that can be shared (same name) across many different
classes.

I haven't used this because I am concerned about speed of execution,
without much evidence I admit.

Tim

Ken Tilton

unread,
Mar 29, 2008, 10:15:52 PM3/29/08
to

tim wrote:
> On Sat, 29 Mar 2008 18:20:32 -0400, Ken Tilton wrote:
>
>
>>tim wrote:
>>
>>>On Sat, 29 Mar 2008 13:06:23 -0700, Tayssir John Gabbour wrote:
>>
>>Hunh? As you noted, that is merely the default. It is no big deal to
>>supply the :conc-name option.
>>
>>kenny
>>
>
>
> The language has a spec of 1,000 pages and a huge number of productions.
> Some structures can be reused across multiple productions but many can't.
> So there are many structures and many fields. Also if overriding one needs
> to check that the name is not a duplicate, used elsewhere, which is more
> work.
>
> (* trivial large)
> => large
>
> With objects it might not be so large a job because generic
> functions get built that can be shared (same name) across many different
> classes.

Nice speech, bad assumption: what makes you think I was going to supply
the value nil along with the :conc-name option? Or did I guess wrong at
the point you were making? You never quite made it explicit, but it
sounded as if you were concerned about name clashes.

Allow me to abbreviate the confusion:

(defstruct (newsgroup (:conc-name ng-))
name address messages)

As for performance, CLOS is not slow, structs are just much faster. The
argument against premature optimization is not one in favor of seeing a
high-performance situation and deliberately using something slower than
necessary. So if someone happens to have a need to allocate a kazillion
thingys, their only problem is coming up with an abbreviateion for
thingy when they use defstruct. I would go with th-.

hth, kenny

tim

unread,
Mar 30, 2008, 1:32:47 AM3/30/08
to
On Sat, 29 Mar 2008 22:15:52 -0400, Ken Tilton wrote:

> Nice speech, bad assumption: what makes you think I was going to supply
> the value nil along with the :conc-name option? Or did I guess wrong at
> the point you were making? You never quite made it explicit, but it
> sounded as if you were concerned about name clashes.
>
> Allow me to abbreviate the confusion:
>
> (defstruct (newsgroup (:conc-name ng-))
> name address messages)

I thought in that case I may as well just make the structure names short
as define the :conc-name. It's not really that big a deal. I just
mentioned it as one downside of using structs vs objects.

Optimization
************

A few people have mentioned premature optimization. This is something I
have to fight against, having grown up in a world where CPU time cost over
$1.50 per second and disk space was $30/mb/month. I just don't yet have a
good sense of what is going to get me in trouble later and what is not.

Last month I spent two days restructuring my code so it would be easy to
split it into multiple threads (using SBCL threads). I felt if I left it
until later I would have more code to fix up later on. Still not sure that
was a good investment at that stage.

How do other people manage these dilemmas?

Tim

danb

unread,
Mar 30, 2008, 2:19:11 AM3/30/08
to
tim wrote:
> Optimization

> I just don't yet have a good sense of what is
> going to get me in trouble later and what is not.
> How do other people manage these dilemmas?

For threads, try to minimize data sharing. Don't
iterate anywhere you can map. Even iteration can
sometimes be faked by generating a list of integers
and mapping onto that.

For premature optimization, don't worry about
getting in trouble. Just get the program working
however you can, and profile it if it's too slow.
If you find yourself obsessing over low-level
performance issues, find something better to
obsess over. Try to improve the overall design,
or find more interesting projects to work on
if that's an option.

--Dan

------------------------------------------------
Dan Bensen
http://www.prairienet.org/~dsb/

Vassil Nikolov

unread,
Jun 24, 2008, 12:36:26 AM6/24/08
to

On Sat, 29 Mar 2008 12:31:53 -0700 (PDT), dstein64 <DSte...@gmail.com> said:
| ...

| objects can probably achieve all that structures did

Even though it applies to relatively few cases, it is worth noting
that structures are the only standard facility (in Common Lisp) that
allows user-defined types [*] with a readable print syntax. In
other words, a structure is the thing when the programmer wants the
lisp reader itself, and with just the standard readtable, to produce
typed values from literals. A structure may have just one slot, of
course, like in (EQUALP #S(APPLES :$ 17) #S(ORANGES :$ 17)).

Structure types can be used with generic function dispatch, of
course, so one wouldn't have to change DEFMETHOD signatures if
switching from structures to classes.

BOA-constructors are nice, too.

[*] that work with CHECK-TYPE, TYPECASE, TYPEP without the need of
an extra DEFTYPE with a SATISFIES definition

---Vassil.


--
Peius melius est. ---Ricardus Gabriel.

Russell McManus

unread,
Jun 24, 2008, 9:47:47 AM6/24/08
to

Vassil Nikolov <vnikolo...@pobox.com> writes:

> Even though it applies to relatively few cases, it is worth noting
> that structures are the only standard facility (in Common Lisp) that
> allows user-defined types [*] with a readable print syntax. In
> other words, a structure is the thing when the programmer wants the
> lisp reader itself, and with just the standard readtable, to produce
> typed values from literals. A structure may have just one slot, of
> course, like in (EQUALP #S(APPLES :$ 17) #S(ORANGES :$ 17)).

It is possible to use defclass to define types with a readable print
syntax. Here is some example code:

(in-package :cl-user)

(defclass point ()
((x :initarg :x :reader get-x)
(y :initarg :y :reader get-y)))

(defmethod print-object ((point point) stream)
(format stream "#.~S" `(make-instance 'point
:x ,(get-x point)
:y ,(get-y point))))

(let* ((p1 (make-instance 'point :x 1 :y 2))
(s (with-output-to-string (s) (format s "~S" p1)))
(p2 (read-from-string s)))
(values (get-x p2) (get-y p2)))

Pascal Costanza

unread,
Jun 24, 2008, 10:02:35 AM6/24/08
to

Not quite. It's almost impossible to define a readable syntax for CLOS
objects when they are part of circular structures. That's trivial for
struct instances, because there it comes for free.


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/

Barry Margolin

unread,
Jun 24, 2008, 3:04:56 PM6/24/08
to
In article <87iqvzg...@thelonious.cl-user.org>,
Russell McManus <russell...@yahoo.com> wrote:

> Vassil Nikolov <vnikolo...@pobox.com> writes:
>
> > Even though it applies to relatively few cases, it is worth noting
> > that structures are the only standard facility (in Common Lisp) that
> > allows user-defined types [*] with a readable print syntax. In
> > other words, a structure is the thing when the programmer wants the
> > lisp reader itself, and with just the standard readtable, to produce
> > typed values from literals. A structure may have just one slot, of
> > course, like in (EQUALP #S(APPLES :$ 17) #S(ORANGES :$ 17)).
>
> It is possible to use defclass to define types with a readable print
> syntax. Here is some example code:
>
> (in-package :cl-user)
>
> (defclass point ()
> ((x :initarg :x :reader get-x)
> (y :initarg :y :reader get-y)))
>
> (defmethod print-object ((point point) stream)
> (format stream "#.~S" `(make-instance 'point
> :x ,(get-x point)
> :y ,(get-y point))))

True, but sharpsign-dot is a really gross hack that one should rarely
depend on. Safety-conscious programmers will disable it when reading
from external sources, because it allows anything to happen in the
context of reading.

--
Barry Margolin, bar...@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***

Russell McManus

unread,
Jun 24, 2008, 3:12:13 PM6/24/08
to
Pascal Costanza <p...@p-cos.net> writes:

>> It is possible to use defclass to define types with a readable print
>> syntax. Here is some example code:
>>
>> (in-package :cl-user)
>>
>> (defclass point () ((x :initarg :x :reader get-x)
>> (y :initarg :y :reader get-y)))
>>
>> (defmethod print-object ((point point) stream)
>> (format stream "#.~S" `(make-instance 'point
>> :x ,(get-x point)
>> :y ,(get-y point))))
>>
>> (let* ((p1 (make-instance 'point :x 1 :y 2))
>> (s (with-output-to-string (s) (format s "~S" p1)))
>> (p2 (read-from-string s)))
>> (values (get-x p2) (get-y p2)))
>
> Not quite. It's almost impossible to define a readable syntax for CLOS
> objects when they are part of circular structures. That's trivial for
> struct instances, because there it comes for free.

Interesting. I just tried the below, and it seemed to work.

Clearly I'm missing something. Would you be willing to describe the
problem in more detail?

(defclass parent ()
((child :initarg :child :reader get-child)))

(defmethod print-object ((parent parent) stream)
(format stream "#.~S" `(make-instance 'parent
:child ,(get-child parent))))

(defclass child ()
((parent :initarg :parent :reader get-parent)))

(defmethod print-object ((child child) stream)
(format stream "#.~S" `(make-instance 'child
:parent ,(get-parent child))))

(let* ((*print-circle* t)
(parent (make-instance 'parent))
(child (make-instance 'child :parent parent)))
(setf (slot-value parent 'child) child)
child)

-russ

Pascal Costanza

unread,
Jun 24, 2008, 4:47:40 PM6/24/08
to
Russell McManus wrote:

> I just tried the below, and it seemed to work.
>
> Clearly I'm missing something. Would you be willing to describe the
> problem in more detail?
>
> (defclass parent ()
> ((child :initarg :child :reader get-child)))
>
> (defmethod print-object ((parent parent) stream)
> (format stream "#.~S" `(make-instance 'parent
> :child ,(get-child parent))))
>
> (defclass child ()
> ((parent :initarg :parent :reader get-parent)))
>
> (defmethod print-object ((child child) stream)
> (format stream "#.~S" `(make-instance 'child
> :parent ,(get-parent child))))
>
> (let* ((*print-circle* t)
> (parent (make-instance 'parent))
> (child (make-instance 'child :parent parent)))
> (setf (slot-value parent 'child) child)
> child)

Sure, the printing works. But did you try to actually read the result? ;)

The problem here is that there is a mismatch between the different read
times:

#1=(... #.(... #1# ...))

The Lisp reader attempts to resolve the #1# reference at a time where
the #1= label doesn't exist yet.

I don't know of any way how to get around this for CLOS classes (which
is a pity).

Pascal Costanza

unread,
Jun 24, 2008, 4:48:30 PM6/24/08
to

Yes, that too.

Pascal Costanza

unread,
Jun 24, 2008, 4:51:52 PM6/24/08
to

...well maybe there is a way to define a CLOS metaclass that stores
slots in embedded structs, which may enable the reuse of the built-in
mechanism for dealing with circular struct instances. But there may be
some other unresolvable issues lurking around. Hard to tell without
trying it.

Russell McManus

unread,
Jun 24, 2008, 7:49:18 PM6/24/08
to

Pascal Costanza <p...@p-cos.net> writes:

>> The Lisp reader attempts to resolve the #1# reference at a time
>> where the #1= label doesn't exist yet.
>>
>> I don't know of any way how to get around this for CLOS classes
>> (which is a pity).

Thanks for the explanation. Perhaps the reader should be extensible
in this direction. It's hard to imagine that the reader can be
extended in so many ways, but not in this way.

I've often thought that CL should have a *reader* variable which would
be kind of like *debugger-hook*, so that the user could portably
replace the reader. It would make it possible to retrofit this kind
of functionality into any cl. I haven't thought through the myriad
issues, of course...

-russ

Vassil Nikolov

unread,
Jun 25, 2008, 12:54:02 AM6/25/08
to

On Tue, 24 Jun 2008 08:47:47 -0500, Russell McManus <russell...@yahoo.com> said:
| It is possible to use defclass to define types with a readable print
| syntax. Here is some example code:
| ...

| (defmethod print-object ((point point) stream)
| (format stream "#.~S" `(make-instance 'point
| :x ,(get-x point)
| :y ,(get-y point))))

Independently of the sharpsign-dot matter, I think it is important
that with structures, the printability comes "for free", without any
extra effort by the programmer. Even when this effort is not too
painful, it has a cost not only in the work to do it, but also in
its fragility, since it is only up to the programmer to update it
whenever the class definition(s) change(s). (Or in the work to
automate the latter, which does not seem particularly easy.)

Now, when we know that we'll be using some kind of values a lot,
we'll also know it is worth to implement a character macro syntax
for it, but for rarely used values, or in the exploratory phase, we
may prefer self-descriptive literals to plain strings, for a little
extra robustness or readability at low cost. For example, if we
have

(defun find-location (city &optional street) ...)

we might prefer

(find-location #s(city :$ sofia) #s(street :$ paris)) ;[1, 2]

to

(find-location 'sofia 'paris) ;makes us wonder if someone forgot
; to erase a value being replaced

We can still easily allow the latter as well e.g. for interactive
use. For example, whereever we only work with the strings (e.g. for
database access), we can write ($ CITY) and ($ STREET) instead of
just CITY and STREET, with something like

(defgeneric $ (x) (:documentation "Produce the namestring of the argument."))
(defmethod $ ((x city)) (string (city-$ x)))
(defmethod $ ((x street)) (string (street-$ x)))
(defmethod $ ((x string)) x)
(defmethod $ ((x symbol)) (string x))

Again, this is not library-grade code, but "optimized" for
exploration, to get us going quickly while still allowing some
measure of "tagging" what is going on. Once the design will have
crystallized, `$' can just be redefined (possibly as a macro) if
there will be any of the prototype code left that will not have been
rewritten.

_________
[1] This is not equivalent to

(find-location :city 'sofia :street 'paris)

since we want the values to be tagged, not just the arguments.
So when a value gets printed, we know what it is without having
to write any code ourselves to give us the type.

[2] Or

(find-location #s(city :$ paris) #s(street :$ sofia))

if you prefer Lutetia to Serdica.

Marco Antoniotti

unread,
Jun 25, 2008, 4:49:45 AM6/25/08
to
On Jun 25, 6:54 am, Vassil Nikolov <vnikolov+use...@pobox.com> wrote:


... and it is not equivalent to

(use-package "UNIFY") ; As in CL-UNIFICATION http://common-lisp.net/project/cl-unification/

(find-location #T(city city-name 'paris) #T(street street-name
'sofia))

Which already works portably (provided you write an appropriate FIND-
LOCATION which takes two UNIFY:STRUCTURE-OBJECT-TEMPLATE

Cheers
--
Marco

Didier Verna

unread,
Jun 25, 2008, 4:54:09 AM6/25/08
to
Russell McManus <russell...@yahoo.com> wrote:

> (defclass parent ()
> ((child :initarg :child :reader get-child)))
>
> (defmethod print-object ((parent parent) stream)
> (format stream "#.~S" `(make-instance 'parent
> :child ,(get-child parent))))
>
> (defclass child ()
> ((parent :initarg :parent :reader get-parent)))
>
> (defmethod print-object ((child child) stream)
> (format stream "#.~S" `(make-instance 'child
> :parent ,(get-parent child))))
>
> (let* ((*print-circle* t)
> (parent (make-instance 'parent))
> (child (make-instance 'child :parent parent)))
> (setf (slot-value parent 'child) child)
> child)

This exhausts the stack for me. Actually, this doesn't work either:

CL-USER> (defstruct parent (child))
PARENT
CL-USER> (defstruct child (parent))
CHILD
CL-USER> (let* ((*print-circle* t)
(parent (make-parent))
(child (make-child :parent parent)))
(setf (parent-child parent) child)
child)
; Evaluation aborted.


And now, I'm totally confused... I must be missing something obvious.

--
5th European Lisp Workshop at ECOOP 2008, July 7: http://elw.bknr.net/2008/

Didier Verna, did...@lrde.epita.fr, http://www.lrde.epita.fr/~didier

EPITA / LRDE, 14-16 rue Voltaire Tel.+33 (0)1 44 08 01 85
94276 Le Kremlin-Bicętre, France Fax.+33 (0)1 53 14 59 22 did...@xemacs.org

Pascal J. Bourguignon

unread,
Jun 25, 2008, 6:26:25 AM6/25/08
to
Didier Verna <did...@lrde.epita.fr> writes:

Yes. When the PRINT in the REPL wants to print the result of your LET
form, the binding to *PRINT-CICLE* will have been restored.

Either:

(setf *print-circle* t)
(let* ((parent (make-parent))


(child (make-child :parent parent)))
(setf (parent-child parent) child)
child)


or:

(let* ((*print-circle* t)
(parent (make-parent))
(child (make-child :parent parent)))
(setf (parent-child parent) child)

(print child)
(values))


Vivement les vacances!

--
__Pascal Bourguignon__

Didier Verna

unread,
Jun 25, 2008, 7:53:16 AM6/25/08
to
p...@informatimago.com (Pascal J. Bourguignon) wrote:

> Yes. When the PRINT in the REPL wants to print the result of your LET
> form, the binding to *PRINT-CICLE* will have been restored.

Doh! Oh boy, I need to get some sleep...

> Vivement les vacances!

Tu m'étonnes ! ;-)

--
5th European Lisp Workshop at ECOOP 2008, July 7: http://elw.bknr.net/2008/

EPITA / LRDE, 14-16 rue Voltaire Tel.+33 (0)1 44 08 01 85

94276 Le Kremlin-Bicêtre, France Fax.+33 (0)1 53 14 59 22 did...@xemacs.org

John Thingstad

unread,
Jun 25, 2008, 8:34:31 AM6/25/08
to
>
> Vivement les vacances!
>
>
>

Et tu Pascal!

--------------
John Thingstad

Richard M Kreuter

unread,
Jun 25, 2008, 2:14:46 PM6/25/08
to
Russell McManus <russell...@yahoo.com> writes:

> I've often thought that CL should have a *reader* variable which would
> be kind of like *debugger-hook*, so that the user could portably
> replace the reader.

One venerable way to do this is to have a readtable in which every
character is associated with a read-macro that calls into a custom
parser.

--
RmK

Parth Malwankar

unread,
Jun 26, 2008, 2:46:16 AM6/26/08
to
On Jun 24, 7:02 pm, Pascal Costanza <p...@p-cos.net> wrote:
> Russell McManus wrote:

In my limited experience with lisp (I am still quite new) I found
structures
to be quite convenient to use. I get read/write support, predicate
checking,
attribute access, inheritance, method specialization all for free.

'defstruct ...' declarations are much shorter than 'defclass ...' but
this wasn't that big an issue as its trivial to create your own
'defklass ...'
macro which expands to 'defclass ...'.

Doing a little bit of reading I found that structures are nothing but
a specialized class in Common Lisp.


[3]> (defclass a () ((x)))
#<STANDARD-CLASS A>
[4]> (defstruct b ())
B

[7]> (class-of (make-b))
#<STRUCTURE-CLASS B>
[8]> (class-of (class-of (make-b)))
#<STANDARD-CLASS STRUCTURE-CLASS>
[9]>

As per my understanding, classes are useful:
- when you want to add slots or change class at runtime
- use multiple inheritance

In case all the slots are known at design time and single inheritance
suffices, is it might make sense to use structures.

Would appreciate comments in case I have missed out some other
advantages
of classes.

Thanks.
Parth

Rainer Joswig

unread,
Jun 26, 2008, 3:26:44 AM6/26/08
to
In article
<f0690486-8f89-4001...@i36g2000prf.googlegroups.com>,
Parth Malwankar <parth.m...@gmail.com> wrote:

Which has been done before. For example in the original CLX
implementation one can decide to create classes or structures
for all the X11 objects before compile time.

>
> Doing a little bit of reading I found that structures are nothing but
> a specialized class in Common Lisp.

Well, the first version of Common Lisp (described in CLtL1) had
no CLOS (-> no classes and no generic functions). CLOS
had been added later and when CLOS was added the question
of classes for existing types came up. So Common Lisp has
now also classes for some non-CLOS types. For example
there is a class for the type NUMBER, but not for (integer 3 5).
The classes have been added so that generic functions can
also dispatch on some non-CLOS data objects. Thus one can
dispatch over NUMBER but not (integer 3 5).

>
>
> [3]> (defclass a () ((x)))
> #<STANDARD-CLASS A>
> [4]> (defstruct b ())
> B
>
> [7]> (class-of (make-b))
> #<STRUCTURE-CLASS B>
> [8]> (class-of (class-of (make-b)))
> #<STANDARD-CLASS STRUCTURE-CLASS>
> [9]>
>
> As per my understanding, classes are useful:
> - when you want to add slots or change class at runtime
> - use multiple inheritance
>
> In case all the slots are known at design time and single inheritance
> suffices, is it might make sense to use structures.
>
> Would appreciate comments in case I have missed out some other
> advantages
> of classes.

CLOS has the MOP (Meta-Object Protocol). With its functionality
one can look inside classes and objects (Introspection).
Also the CLOS MOP allows changing/extending the way CLOS works.
So it is possible to get information about the slots in a class.
It is possible write different slot-allocation strategies
(think database, sparse slots, ...).

The drawbacks of CLOS has made users ask their vendors for
added functionality. So various Common Lisp implementations
add some features to improve slot access time (say, sealed classes).
STRUCTURES usually have faster access, since slot positions are
known at compile time. CLOS implementations have been adding
such a feature.

For style guide one can also define:

* use CLOS by default (when there is a choice between structures and classes).
* use structures only when portable faster slot access is necessary.

Classes have also some other features:

* class slots
* classes can be defined before the superclasses
* classes have a metaclass
* there is a single function to make instances: make-instance

For the bigger picture of CLOS, it is best to read AMOP
('The Art of the Metaobject Protocol') which truely
is an excellent book which gives all kinds of enlightenment.

>
> Thanks.
> Parth

--
http://lispm.dyndns.org/

Pascal Costanza

unread,
Jun 26, 2008, 3:33:21 AM6/26/08
to
Parth Malwankar wrote:

> As per my understanding, classes are useful:
> - when you want to add slots or change class at runtime
> - use multiple inheritance
>
> In case all the slots are known at design time and single inheritance
> suffices, is it might make sense to use structures.
>
> Would appreciate comments in case I have missed out some other
> advantages of classes.

Slots of classes can be unbound, which can be a very useful feature.

Slot accessors are generic functions, and you can define
before/after/around (and even primary) methods on slot accessors.

Instances of classes can change their class at runtime.

You have a distinction between instance slots and class slots (not that
useful though).

You can define default initargs in classes (sometimes useful).

Classes have a well-defined metaobject protocol (for implementations
that support the CLOS MOP). That part of the CLOS MOP is the best
understood, most widely supported and most useful one.

Russell McManus

unread,
Jun 26, 2008, 9:08:08 AM6/26/08
to

Yeah, I remembered this trick after I posted, but I guess I hid that
neuron on purpose earlier.

-russ

Thomas A. Russ

unread,
Jun 26, 2008, 12:27:17 PM6/26/08
to
Parth Malwankar <parth.m...@gmail.com> writes:

> Would appreciate comments in case I have missed out some other
> advantages of classes.

Well, in a large system there is the additional advantage that the
accessors associated with classes are generic functions. With
structures, they are ordinary functions, which means that you have to be
careful to make sure that the names are distinct.

Now, the default behavior of structs helps with this by building slot
accessor names by prepending the structure name. But when used together
with inheritance, this leads to a mix of access styles that requires you
to be aware of the level of slot you want:

(person-name s1)
(student-takes-classes s1)
(animal-species s1)

assuming some sort of simple animal>person>student hierarchy


--
Thomas A. Russ, USC/Information Sciences Institute

Pascal Costanza

unread,
Jun 26, 2008, 2:06:30 PM6/26/08
to

That can be changed with the conc-name option in defstruct.

Marco Antoniotti

unread,
Jun 26, 2008, 3:13:45 PM6/26/08
to
On Jun 26, 8:06 pm, Pascal Costanza <p...@p-cos.net> wrote:
> Thomas A. Russ wrote:
> > Parth Malwankar <parth.malwan...@gmail.com> writes:
>
> >> Would appreciate comments in case I have missed out some other
> >> advantages of classes.
>
> > Well, in a large system there is the additional advantage that the
> > accessors associated with classes are generic functions.  With
> > structures, they are ordinary functions, which means that you have to be
> > careful to make sure that the names are distinct.
>
> > Now, the default behavior of structs helps with this by building slot
> > accessor names by prepending the structure name.  But when used together
> > with inheritance, this leads to a mix of access styles that requires you
> > to be aware of the level of slot you want:
>
> >     (person-name s1)
> >     (student-takes-classes s1)
> >     (animal-species s1)
>
> > assuming some sort of simple   animal>person>student hierarchy
>
> That can be changed with the conc-name option in defstruct.
>

Yes. But note also that in TAR's example

(student-species s1)

will work.

Cheers
--
Marco

Thomas A. Russ

unread,
Jun 27, 2008, 3:45:45 PM6/27/08
to
Marco Antoniotti <mar...@gmail.com> writes:

> On Jun 26, 8:06滋pm, Pascal Costanza <p...@p-cos.net> wrote:
> > Thomas A. Russ wrote:

> > > 滋 滋 (person-name s1)
> > > 滋 滋 (student-takes-classes s1)
> > > 滋 滋 (animal-species s1)
> >
> > > assuming some sort of simple 滋 animal>person>student hierarchy


> >
> > That can be changed with the conc-name option in defstruct.
> >
>
> Yes. But note also that in TAR's example
>
> (student-species s1)
>
> will work.

Wow. Learn something new every day. I didn't realize that struct
inclusion resulted in the generation of new accessors for all of the
inherited slots.

That actually makes something we have been doing a bit easier!

0 new messages