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

newbie: using packages and CLOS

84 views
Skip to first unread message

Gregory Bird

unread,
Aug 29, 2002, 11:48:46 AM8/29/02
to
Hello,

I'm trying to modularize an object oriented program that I wrote (for
fun) by using packages and I'm having trouble getting it to work. I'm
new to lisp and I've never used packages before or CLOS for that matter.
I'm not even sure that this is a good idea, but anyway...

Here is what I'm doing:

--- FILE a-class.lsp -------------------------------------------
(defpackage a-class
(:use common-lisp)
(:export "A" "A-NAME" "PRINT-OBJECT"))

(in-package "A-CLASS")

(defclass a ()
((name :accessor a-name
:initarg :name
:initform "a")))

(defmethod print-object ((obj a) stream)
(format stream "~A" (a-name obj)))
----------------------------------------------------------------

--- FILE b-class.lsp -------------------------------------------
(depackage b-class
(:use common-lisp a-class)
(:export "B"))

(in-package "B-CLASS")

(defclass b (a)
((name :initform "b")))
----------------------------------------------------------------

The "b" class is suppose to inherit from "a", and anytime I print either
of them it should print "a" or "b". The OO code works fine when its in
the same file and there are no packages involved.


At the clisp prompt I type:

----------------------------------------------------------------
> (load "a-class.lsp")
...
> (load "b-class.lsp")
...
> (use-package "A-CLASS")
...
> (use-package "B-CLASS")
...
> (setq obj (make-instance 'b))
a
> (typep obj 'b)
T
----------------------------------------------------------------

After making an instance of the "b" class, it still prints out as an "a"
object, even though it knows its a type "b" object. Its almost as if
the new "b" instance isn't gettig initialized correctly. What am I
doing wrong?

TIA !

--
greg
hotmail is used as a spamtrap
crowbent AT optonline DOT net

Barry Margolin

unread,
Aug 29, 2002, 1:56:58 PM8/29/02
to
In article <3D6E425E...@hotmail.com>,

Gregory Bird <crow...@hotmail.com> wrote:
>Hello,
>
>I'm trying to modularize an object oriented program that I wrote (for
>fun) by using packages and I'm having trouble getting it to work. I'm
>new to lisp and I've never used packages before or CLOS for that matter.
> I'm not even sure that this is a good idea, but anyway...

It's often not a good idea to try to make packages so fine-grained. You
should have a single package for all related code, not a package per class.

>Here is what I'm doing:
>
>--- FILE a-class.lsp -------------------------------------------
>(defpackage a-class
> (:use common-lisp)
> (:export "A" "A-NAME" "PRINT-OBJECT"))
>
>(in-package "A-CLASS")
>
>(defclass a ()
> ((name :accessor a-name
> :initarg :name
> :initform "a")))
>
>(defmethod print-object ((obj a) stream)
> (format stream "~A" (a-name obj)))
>----------------------------------------------------------------
>
>
>
>--- FILE b-class.lsp -------------------------------------------
>(depackage b-class
> (:use common-lisp a-class)
> (:export "B"))
>
>(in-package "B-CLASS")
>
>(defclass b (a)
> ((name :initform "b")))
>----------------------------------------------------------------

The problem you're having is that you don't export the symbol NAME from
A-CLASS. The result is that B has two different slots, A-CLASS::NAME,
which it inherited from A, and B-CLASS::NAME. The A-NAME accessor accesses
A-CLASS::NAME, not B-CLASS::NAME. So you either need to export NAME from
A-CLASS, or change the definition of B to:

(defclass b (a)
((a-class::name :initform "b")))

Another way you could solve this is by using :DEFAULT-INITARGS rather than
overriding the slot definition:

(defclass b (a)
(:default-initargs :name "b"))

--
Barry Margolin, bar...@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.

Fred Gilham

unread,
Aug 29, 2002, 3:05:03 PM8/29/02
to

Gregory Bird <crow...@hotmail.com> writes:

Ya dun shot yerself in th' foot!

If you can inspect the object you create, you'll see that you actually
have 2 slots. These slots are actually

a-class::name

and

b-class::name

For example, here's the slightly less than completely helpful output
in CLISP:

(inspect obj)
#1=a: standard object
type: B-CLASS:B
0 [NAME]: "b"
1 [NAME]: "a"

Your printer function only accesses the a-class::name slot.

You could do the following:

b-class.lisp:
----------------------------------------
(defpackage b-class


(:use common-lisp a-class)
(:export "B"))

(in-package "B-CLASS")

(defclass b (a)


((a-class::name :initform "b")))

----------------------------------------

Then you get (noise omitted):

* (compile-file "a-class" :load t)
* (compile-file "b-class" :load t)
* (use-package "A-CLASS")
* (use-package "B-CLASS")
* (setq obj (make-instance 'b))
Warning: Declaring OBJ special.

b
*

BTW, none of the inspectors I tried except the old CLX-based one (not
the motif-based one) I use with CMU Lisp displayed the package names
on the slots. This includes the CMU Lisp text inspector, the CMU Lisp
motif-based inspector, the CLISP inspector and the Allegro text
inspector.

--
Fred Gilham gil...@csl.sri.com
We have yet to find the Galileo who will question
our me-centred universe. --- Christina Odone

Matthew Danish

unread,
Aug 29, 2002, 3:38:08 PM8/29/02
to
The problem is that the symbol which names the NAME slot is not the
same in both packages: there is A-CLASS::NAME and B-CLASS::NAME. A
simple solution would be to export the symbol NAME from the A-CLASS
package (and import it into the B-CLASS package).

--
; Matthew Danish <mda...@andrew.cmu.edu>
; OpenPGP public key: C24B6010 on keyring.debian.org
; Signed or encrypted mail welcome.
; "There is no dark side of the moon really; matter of fact, it's all dark."

Gregory Bird

unread,
Aug 29, 2002, 5:48:55 PM8/29/02
to
Barry Margolin wrote:
> In article <3D6E425E...@hotmail.com>,
> Gregory Bird <crow...@hotmail.com> wrote:
>
>>Hello,
>>
>>I'm trying to modularize an object oriented program that I wrote (for
>>fun) by using packages and I'm having trouble getting it to work. I'm
>>new to lisp and I've never used packages before or CLOS for that matter.
>> I'm not even sure that this is a good idea, but anyway...
>
>
> It's often not a good idea to try to make packages so fine-grained. You
> should have a single package for all related code, not a package per class.
>

Thanks for the tip, I'm still learning what is appropriate for Lisp
development. Is it 'ok' to place each class definition and
corresponding methods in a separate file (without packages)? How do
lisp programmers keep their code organized?

>
>>Here is what I'm doing:

[SNIP MY CODE]

> The problem you're having is that you don't export the symbol NAME from
> A-CLASS. The result is that B has two different slots, A-CLASS::NAME,
> which it inherited from A, and B-CLASS::NAME. The A-NAME accessor accesses
> A-CLASS::NAME, not B-CLASS::NAME. So you either need to export NAME from
> A-CLASS, or change the definition of B to:
>
> (defclass b (a)
> ((a-class::name :initform "b")))
>
> Another way you could solve this is by using :DEFAULT-INITARGS rather than
> overriding the slot definition:
>
> (defclass b (a)
> (:default-initargs :name "b"))
>

Thanks again! Exporting NAME in the A-CLASS package fixed the problem.

--
Gregory Bird
crow...@optonline.net

Gregory Bird

unread,
Aug 29, 2002, 5:57:04 PM8/29/02
to
Fred Gilham wrote:
> (inspect obj)
> #1=a: standard object
> type: B-CLASS:B
> 0 [NAME]: "b"
> 1 [NAME]: "a"
>

Thanks for your help and for introducing me to the INSPECT command. I
didn't reach that chapter yet in CLtL2 :-)

--
Gregory Bird

Christopher C. Stacy

unread,
Aug 29, 2002, 6:46:55 PM8/29/02
to
>>>>> On Thu, 29 Aug 2002 17:48:55 -0400, Gregory Bird ("Gregory") writes:

Gregory> Barry Margolin wrote:
>> In article <3D6E425E...@hotmail.com>,
>> Gregory Bird <crow...@hotmail.com> wrote:
>>
>>> Hello,
>>>
>>> I'm trying to modularize an object oriented program that I wrote
>>> (for fun) by using packages and I'm having trouble getting it to
>>> work. I'm new to lisp and I've never used packages before or CLOS
>>> for that matter. I'm not even sure that this is a good idea, but
>>> anyway...
>> It's often not a good idea to try to make packages so fine-grained.
>> You
>> should have a single package for all related code, not a package per class.
>>

Gregory> Thanks for the tip, I'm still learning what is appropriate for Lisp
Gregory> development. Is it 'ok' to place each class definition and
Gregory> corresponding methods in a separate file (without packages)? How do
Gregory> lisp programmers keep their code organized?

Packages are not associated with files.

You can put the exact same package declaration at the top of all your files.

A package merely creates a namespace where you can put things.
As a file (or other input) is read, whenever an IN-PACKAGE form
is encountered, it just selects which namespace you're reading into.

Erik Naggum

unread,
Aug 29, 2002, 9:36:59 PM8/29/02
to
* Gregory Bird
| Is it OK to place each class definition and corresponding methods in a
| separate file (without packages)?

You should always use `defpackage´ to define the package and `in-package´ to
instruct the reader to use that package for the remainder of the file (or
until another `in-package´, but this is often confusing). `use-package´ is
not a user-level function, contrary to its appealing name; it is used by the
package system internals and is made available to the programmer because you
sometimes want to mess with a package at runtime. Common Lisp has many
functions that are there for the expert user but should not be used casually.

Likewise, when you run your application, use `in-package´ to switch to that
package. You should not let the Common-Lisp-User package use your package.

| How do lisp programmers keep their code organized?

Three Files for the Elven-kings under the sky,
Seven for the Dwarf-lords in their halls of stone,
Nine for Mortal Men doomed to die,
One for the Dark Lord on his dark throne,
In the Land of Mordor where the Users lie.
One `defsystem´ File to rule them all, One `defpackage´ File to find them,
One `defapplication´ File to bring them all and in the darkness bind them,
In the Land of Mordor where the Users lie.

--
Erik Naggum, Oslo, Norway

Act from reason, and failure makes you rethink and study harder.
Act from faith, and failure makes you blame someone and push harder.

Tim Bradshaw

unread,
Aug 29, 2002, 9:53:24 PM8/29/02
to
* Gregory Bird wrote:
> How do
> lisp programmers keep their code organized?

I tend to organize my code as:

a sysdcl file which defines the system (with whatever defsystem...)
and a (fairly simple) system would be:

pkg.lisp - has all package definitions (often just one)
file-1.lisp - code
...
file-n.lisp - more code
package-cometh.lisp - stuff that needs to happen right at the
end (really it would be foo-cometh for a system called foo).
This file won't contain definitions, but will cause things to
happen when the system is loaded, such as registering it,
loading patches and so on. I *think* I got the -cometh
convention from Genera, but I'm not sure.

--tim

Christopher C. Stacy

unread,
Aug 30, 2002, 12:52:34 AM8/30/02
to
>>>>> On 30 Aug 2002 02:53:24 +0100, Tim Bradshaw ("Tim") writes:

Tim> * Gregory Bird wrote:
>> How do
>> lisp programmers keep their code organized?

Tim> I tend to organize my code as:

Tim> a sysdcl file which defines the system (with whatever defsystem...)
Tim> and a (fairly simple) system would be:

Tim> pkg.lisp - has all package definitions (often just one)
Tim> file-1.lisp - code
Tim> ...
Tim> file-n.lisp - more code
Tim> package-cometh.lisp - stuff that needs to happen right at the
Tim> end (really it would be foo-cometh for a system called foo).
Tim> This file won't contain definitions, but will cause things to
Tim> happen when the system is loaded, such as registering it,
Tim> loading patches and so on. I *think* I got the -cometh
Tim> convention from Genera, but I'm not sure.

"COMETH" was the abbreviation for "compile methods".
In Flavors you could call for precompilation of methods.
Our file servers back in the late 1970s supported six-letter file names.

(But yea, forsooth, that sort of thing does want happen before your system
cometh into the world, so I guess that's not a bad name for your file!)

Tim Bradshaw

unread,
Aug 30, 2002, 9:13:21 AM8/30/02
to
* Christopher C Stacy wrote:

> "COMETH" was the abbreviation for "compile methods". In Flavors you
> could call for precompilation of methods. Our file servers back in
> the late 1970s supported six-letter file names.

Excellent! Now I know why it always used to have COMETH files. It's
obvious (and I should have realised because I used to do this sort of
thing with PCL too, where you could somehow cause effective methods to
get compiled).

> (But yea, forsooth, that sort of thing does want happen before your
> system cometh into the world, so I guess that's not a bad name for
> your file!)

Yes, I'm going to keep it, even though it's a bit ye olde...

--tim

Marco Antoniotti

unread,
Aug 30, 2002, 10:20:25 AM8/30/02
to

Gregory Bird <crow...@hotmail.com> writes:

>
> Thanks for the tip, I'm still learning what is appropriate for Lisp
> development. Is it 'ok' to place each class definition and
> corresponding methods in a separate file (without packages)? How do
> lisp programmers keep their code organized?
>

I may be overkiling all the time but I essentially organize my code in
the following way (note that SOURCE: is a logical pathname)

SOURCE:
*.system file
*.conf file (ok, this is a SP! you can do away with it :) )
*-package.lisp file(s)
*.lisp files
impl-depedent subdirectory
acl.lisp
clisp.lisp
cmucl.lisp
lw.lisp
....
other-subdir subdirectory
*-subpackage.lisp

etc etc.

The only rules I really abide to are: one DEFPACKAGE, one DEFSYSTEM
(and one DEFCONFIGURATION) per file, and the presence of the
`impl-dependent' subdirectory where to relegate all implementation
dependent code.

The Java idea of "one (public) class / one file" is useful, but only
up to a certain point within the context of Common Lisp.

Cheers

--
Marco Antoniotti ========================================================
NYU Courant Bioinformatics Group tel. +1 - 212 - 998 3488
715 Broadway 10th Floor fax +1 - 212 - 995 4122
New York, NY 10003, USA http://bioinformatics.cat.nyu.edu
"Hello New York! We'll do what we can!"
Bill Murray in `Ghostbusters'.

Thomas F. Burdick

unread,
Aug 30, 2002, 1:14:16 PM8/30/02
to
Marco Antoniotti <mar...@cs.nyu.edu> writes:

> The Java idea of "one (public) class / one file" is useful, but only
> up to a certain point within the context of Common Lisp.

For someone coming from a Java background, my advice would be to try
to leave that one class / one file idea behind. Most of the Java I've
read has had horrible run-on classes that really should have been
refactored into 4 or more public classes, but weren't, probably
because it's painful to do that in Java. One conceptual unit per file
is a good rule of thumb, but don't be surprised if you have quite a
few public classes per concept.

(On the other hand, for people coming from a C++ background, it's
probably a good thing to consider breaking things up into more files)

--
/|_ .-----------------------.
,' .\ / | No to Imperialist war |
,--' _,' | Wage class war! |
/ / `-----------------------'
( -. |
| ) |
(`-. '--.)
`. )----'

Thomas A. Russ

unread,
Aug 30, 2002, 8:39:07 PM8/30/02
to
Erik Naggum <er...@naggum.no> writes:

> In the Land of Mordor where the Users lie.

Now, if the Users would just stop lying to us, it would make the job of
being an implementer much easier :)

Harald Hanche-Olsen

unread,
Aug 31, 2002, 6:28:08 AM8/31/02
to
+ Erik Naggum <er...@naggum.no>:

| Common Lisp has many functions that are there for the expert user
| but should not be used casually.

Indeed. But how do you learn which is which? I rarely find a hint in
the hyperspec or CLtL2. I only learned that defpackage is the right
choice by reading other people's code. This works, and is of course
very rewarding in its own right, but is also a bit inefficient.
(Another method is to pick it up piecemeal from discussions such as
this one.)

CLOS is an extreme example - learning CLOS from the specification was
a much too daunting task for me given the amount of time I can make
available to such tasks. Now that I finally got around to reading
Sonja Keene's book I can probably go back and learn the rest from the
hyperspec as I need to, but you need that kind of kickstart to get
going. (This from a guy who once learned Simula from reading the
Common Base - but my aging brain is no longer up to such feats.) But
for the rest of CL, I am probably still using many of the "for
experts" functions inappropriately.


| One `defsystem´ File to rule them all, One `defpackage´ File to find them,
| One `defapplication´ File to bring them all and in the darkness bind them,

8)

--
* Harald Hanche-Olsen <URL:http://www.math.ntnu.no/~hanche/>
- Yes it works in practice - but does it work in theory?

Erik Naggum

unread,
Aug 31, 2002, 9:16:01 AM8/31/02
to
* Harald Hanche-Olsen

| Indeed. But how do you learn which is which?

You either have a good mentor or you think about the functionality offered
by the various macros and functions and realize when and where to use them.
This is a long-winded process, of course.

| CLOS is an extreme example - learning CLOS from the specification was a much
| too daunting task for me given the amount of time I can make available to
| such tasks. Now that I finally got around to reading Sonja Keene's book I
| can probably go back and learn the rest from the hyperspec as I need to, but
| you need that kind of kickstart to get going. (This from a guy who once
| learned Simula from reading the Common Base - but my aging brain is no
| longer up to such feats.)

That pretty much mimicks my development with both Simula and CLOS. I am not
sure about the Common Base, but I got hold of a brief report on the language
from Norsk Regnecentral and aced the Simula course at the U of Oslo. CLOS
was basically impenetrable before Keene, even with the aid of the 3rd edition
of Winston & Horn.

Edi Weitz

unread,
Sep 2, 2002, 7:30:45 AM9/2/02
to
Erik Naggum <er...@naggum.no> writes:

> You should always use `defpackage´ to define the package and
> `in-package´ to instruct the reader to use that package for the
> remainder of the file (or until another `in-package´, but this is
> often confusing). `use-package´ is not a user-level function,
> contrary to its appealing name; it is used by the package system
> internals and is made available to the programmer because you
> sometimes want to mess with a package at runtime. Common Lisp has
> many functions that are there for the expert user but should not
> be used casually.
>
> Likewise, when you run your application, use `in-package´ to
> switch to that package. You should not let the Common-Lisp-User
> package use your package.

Sorry for jumping in a little bit late here but could you please
elaborate why USE-PACKAGE shouldn't be used casually?

Up until now I've used USE-PACKAGE like, say, 'use' in Perl or
'import' in Java, i.e. as a convenient means to access symbols
provided by other packages without the need to qualify them
fully. Now, after reading your article, I thought about it and tried
to imagine what could be wrong or dangerous with this approach. Is it
just that I'll clutter up the CL-USER package and thus make it
difficult or impossible to come back to a defined state? Is it because
it's easier to get rid of a whole package than to get rid of symbols
that somehow managed to creep into the CL-USER package or my
application's package? Or is it because symbols that are accesible but
not present are considered dangerous? Or am I missing something
important here?

How am I supposed to deal with situations where I use several
different libraries? Let's suppose I have a library 'foo' which uses
the FOO package and exports some symbols. I also have a library 'bar'
which uses the BAR package and exports some symbols. What would be the
right way to deal with this situation instead of just (USE-PACKAGE
'FOO 'BAR)? Defining my own package BAZ where the DEFPACKAGE statement
includes a :USE option for FOO and BAR? Explicitely importing the
symbols I want to use from FOO and BAR into my BAZ package? And
finally (IN-PACKAGE 'BAZ)?

Thanks in advance,
Edi.

PS: BTW - just checked CLtL2 where Steele says:

"Typically a user, working by default in the USER package, will load
a number of packages into Lisp to provide an augmented working
environment, and then call USE-PACKAGE on each of these packages to
allow easy access to their external symbols. UNUSE-PACKAGE undoes
the effects of a previous USE-PACKAGE."

This pretty much describes my current usage of USE-PACKAGE
although I hadn't read this passage before.

Thomas F. Burdick

unread,
Sep 2, 2002, 12:57:26 PM9/2/02
to
Edi Weitz <e...@agharta.de> writes:

I think Erik was arguing against the use of USE-PACKAGE and for the
use of the :USE argument to DEFPACKAGE, not against the concept of
using packages. Personally, I think it's a good idea to put
everything into one DEFPACKAGE form, so that you can later come back
and see what's in a package in one place.

> How am I supposed to deal with situations where I use several
> different libraries? Let's suppose I have a library 'foo' which uses
> the FOO package and exports some symbols. I also have a library 'bar'
> which uses the BAR package and exports some symbols. What would be the
> right way to deal with this situation instead of just (USE-PACKAGE
> 'FOO 'BAR)? Defining my own package BAZ where the DEFPACKAGE statement
> includes a :USE option for FOO and BAR? Explicitely importing the
> symbols I want to use from FOO and BAR into my BAZ package? And
> finally (IN-PACKAGE 'BAZ)?

IMHO, it's not a bad idea to import just a few symbols from another
package, if that's all you want from it. Just do it right below the
DEFPACKAGE form, in your foo-package.lisp file, and you haven't lost
any maintainability.

> PS: BTW - just checked CLtL2 where Steele says:
>
> "Typically a user, working by default in the USER package, will load
> a number of packages into Lisp to provide an augmented working
> environment, and then call USE-PACKAGE on each of these packages to
> allow easy access to their external symbols. UNUSE-PACKAGE undoes
> the effects of a previous USE-PACKAGE."
>
> This pretty much describes my current usage of USE-PACKAGE
> although I hadn't read this passage before.

Well, CL-USER is kind of an odd exception. It's really a staging
arena (at least during development), so it's quite a bit different
from normal package use. I personally tend to make an explicit
staging area for things, so I'd have made a FOO-USER package, that I'd
just delete when I was done with it; but so long as your development
process doesn't involve routinely restarting the image to clear out
the junk you left in there, you're probably doing fine :)

Paul F. Dietz

unread,
Sep 2, 2002, 1:02:51 PM9/2/02
to
"Thomas F. Burdick" wrote:

> IMHO, it's not a bad idea to import just a few symbols from another
> package, if that's all you want from it. Just do it right below the
> DEFPACKAGE form, in your foo-package.lisp file, and you haven't lost
> any maintainability.

Or just use an :import-from clause in the defpackage form.

The only time defpackage is inadequate is when you're building
a collection of packages with circular dependencies.

Paul

0 new messages