In the years I developed a style of package naming conventions. It is heavily influenced by the Java hierarchical packages (and, let's remember, there are CL implementations that support hierarchical packages out there). The style leverages CL nicknames to achieve another interesting effect, which people may find useful: namely, it can alert the developer of different library implementations being loaded in the CL runtime.
Suppose I started working on a new library/system/application which I want to call CLAMP
(it has been in the works for ages). The name is simple enough, so I may start writing something like
(defpackage "CLAMP" (:use "CL") ... (:export "MAKE-CLAMP" ...))
(Note that I prefer to use strings for names in packages; you may use keywords, which have some drawbacks or uninterned symbols like #:clamp
, but I'll comment on them in another post.)
Now, the name CLAMP
isn't particularly distinctive, so you may (it's a big "may") end up with clobbering your package space if you load in your running CL another CLAMP
package coming from elsewhere.
Since I learnt the Java lesson, I decide to rename my package in a more unique way as:
(defpackage "IT.UNIMIB.DISCO.MA.CL.CLAMP" (:use "CL") ... (:nicknames "CLAMP") (:export "MAKE-CLAMP" ...))
The new package name IT.UNIMIB.DISCO.MA.CL.CLAMP
is rather unwieldy, so I added a nice nickname, CLAMP
, which is what I wanted to use as the advertised name of my library. These choices have a few - at first seemingly fortuitous, but subsequently intended - consequences.
First of all, suppose that I have a different (older) version of my library floating around. Suppose also that in this version I have a terribly inefficient implementation of MAKE-CLAMP
. My different version lives in a package named EDU.NYU.CS.MARCO.COMMON-LISP.CLAMP
which also has the CLAMP
nickname. When I try, willingly or inadvertently, to load both packages in the CL runtime, an error is signalled (*), thus alerting me of a naming conflict, which, in this case would also lead to more inefficient code.
So my recipe for naming packages is the following.
- Choose a unique enough package name, maybe following the Java hierarchical scheme.
- Add the advertised name of your package (or library) as a nickname.
(cheers)
(*) Most implementations I know of do signal a package-error
. However, the ANSI spec is silent on the subject.
On a slightly different tangent, if I may share my opinion, package users (not creators) should have greater latitude in choosing/nick-naming any package they might come across. So eg, you don't have to sprinkle the package creator's chosen nickname all over your:symbols, if you decide not to use-package a certain package. You can instead use your own (private) nickname, and so, it can be as short as you like (one letter even). I was quite glad to discover that this is possible within CL itself.
ReplyDelete(defun package-add-nicknames (package nicknames)
(let ((p (find-package package)))
(rename-package p (package-name p)
(union (package-nicknames p)
nicknames
:test 'string=))))
As for versioning, in my opinion, I believe package versions should be handled by module systems. And they should let you use multiple versions of a module within your Lisp at the same time!
80% of the problems with the current cl package system would be fixed if one could declare in a defpackage what nicknames should be used in the defined package.
ReplyDeleteThe problem now is that package authors "push" nicknames. Instead package authors should "pull" the long names into their own packages.