PLN CDR draft: spec

1. Introduction

This is a specification for the Package-Local Nicknames extension in Common Lisp.

1.1. Rationale

Package-local nicknames make it possible to use short and easy-to-use names without potentially introducing name conflicts as can happen with usual nicknames.

1.2. Current state

Package-local nicknames are implemented in some form in SBCL, CCL, ECL, Clasp, ABCL, Allegro CL, LispWorks. There is also a pending MR for the CLISP implementation.

Unfortunately, there are multiple inconsistencies between implementations. All of them lose print-read consistency to some extent, and there are multiple edge cases that aren't always implemented correctly or in the same way.

1.3. Goal

The purpose of this document is to standardize the Package-Local Nicknames extension and to address some existing issues.

[TODO] This CDR also aims to provide an extensive test suite for the extension.

2. Description

A package-local nickname (or a local nickname) defined in some designated package has the same effects as a usual package nickname (later referred to as a global nickname), except that these effects only apply when *package* is bound to that designated package.

This means that a call to find-package with a local nickname that is defined in the current package returns the package nicknamed by this nickname. This also affects all implied calls to find-package, including those performed by the Lisp reader.

In addition, to maintain print-read consistency, the Lisp printer is affected by local nicknames defined in the current package. For details see Issue 2.

A local nickname is allowed to shadow a package name or a global nickname, except for the names #:CL, #:COMMON-LISP and #:KEYWORD which must always refer to their packages. The consequences of adding local nicknames to the packages #:COMMON-LISP and #:KEYWORD are also undefined.

3. API

3.1. defpackage

3.1.1. Description

The defpackage options are extended to include the local-nicknames-option:

local-nicknames-option ::= (:local-nicknames (nickname package)*)

Each pair specifies a local nickname nickname for the corresponding package.

This option may appear more than once.

3.1.2. Arguments and Values:

nickname — a string designator.

package — a package designator.

3.1.3. Exceptional situations

An error of type package-error is signaled when a package designated by the package does not exist.

Name conflict errors are handled by the underlying calls to make-package and add-package-local-nickname.

3.1.4. Implementation dependent

The consequences are undefined when a local nickname is specified for the package that is being defined. (See Issue 4.)

The consequences are undefined when supplied local nicknames are at variance with the current state of the package. An implementation might choose to remove all existing local nicknames at the beginning of each redefinition of the package.

3.2. make-package

3.2.1. Description

(Contains proposals: see Issue 6.)

The make-package lambda list is extended to include an additional keyword argument :local-nicknames:

local-nicknames ::= ((nickname package)*)

local-nicknames specifies zero or more local nicknames to be defined in the new package.

3.2.2. Arguments and Values:

local-nicknames — a list of pairs of form (nickname package). The default is an empty list.

nickname — a string designator.

package — a package designator.

3.2.3. Exceptional situations

An error of type package-error is signaled when a package designated by the package does not exist.

If the nickname is one of the names #:CL, #:COMMON-LISP or #:KEYWORD, an error of type package-error is signaled.

If two or more local nicknames result in a name conflict, a correctable error of type package-error is signaled. A name conflict occurs when multiple local nicknames have same nicknames (equal by string=) but different packages.

A name conflict between multiple local nicknames may be resolved in favor of either nickname being defined.

3.2.4. Implementation dependent

The consequences are undefined when a local nickname is specified for the package that is being defined. (See Issue 4.)

3.3. add-package-local-nickname

(add-package-local-nickname nickname actual-package &optional designated-package)
  => designated-package-object

3.3.1. Arguments and Values

nickname — a string designator.

actual-package — a package designator.

designated-package — a package designator. The default is the current package.

designated-package-object — a package.

3.3.2. Description

Defines a package-local nickname nickname for the actual-package in the designated-package.

[Also see Issue 1.] Returns the package designated by the designated-package.

If the nickname is already defined, checks that it is defined for the package designated by the actual-package. If a name conflict occurs, restarts abort and continue can be used to correct the error.

If the continue restart is invoked, the existing local nickname is removed and the new nickname is defined.

If the abort restart is invoked, the existing nickname is not removed, and the new nickname is not defined.

3.3.3. Exceptional situations

An error of type package-error is signaled when a package designated by the actual-package or the designated-package does not exist.

If the nickname is one of the names #:CL, #:COMMON-LISP or #:KEYWORD, an error of type package-error is signaled.

If the nickname is already defined to be a local nickname for another package different from the actual-package, a correctable error of type package-error is signaled.

3.3.4. Implementation dependent

The consequences are undefined when the designated-package designates the #:COMMON-LISP package or the #:KEYWORD package.

(Contains proposals: see Issue 5.)

If the nickname shadows the package name or one of the global nicknames of the designated-package, a style warning might be issued.

3.4. remove-package-local-nickname

(remove-package-local-nickname old-nickname &optional designated-package)
  => nickname-removed-p

3.4.1. Arguments and Values

old-nickname — a string designator.

designated-package — a package designator. The default is the current package.

nickname-removed-pgeneralized boolean.

3.4.2. Description

If old-nickname is defined to be a local nickname in the designated-package, it is removed.

[Also see Issue 1.] Returns true if it removes a nickname, and NIL otherwise.

3.4.3. Exceptional situations

An error of type package-error is signaled when a package designated by the designated-package does not exist.

3.5. package-local-nicknames

(package-local-nicknames package-designator)
  => local-nicknames-alist
local-nicknames-alist ::= ((nickname . package)*)

3.5.1. Arguments and Values

package-designator — a package designator.

local-nicknames-alist — an alist.

nickname — a string.

package — a package.

3.5.2. Description

Returns an alist describing local nicknames defined in the package designated by the package-designator.

3.5.3. Exceptional situations

An error of type package-error is signaled when a package designated by the package-designator does not exist.

3.5.4. Notes

The returned alist must be safe to be modified by the user.

3.6. package-locally-nicknamed-by-list

(package-locally-nicknamed-by-list package-designator)
  => packages-list

3.6.1. Arguments and Values

package-designator — a package designator.

packages-list — a list of package objects.

3.6.2. Description

Returns a list of packages that have a local nickname defined for the package designated by the package-designator.

3.6.3. Exceptional situations

An error of type package-error is signaled when a package designated by the package-designator does not exist.

3.6.4. Notes

The returned list must be safe to be modified by the user.

4. Affected symbols

4.1. defpackage

See defpackage.

4.2. make-package

4.3. find-package

(Contains proposals: see Issue 3, Issue 8.)

When the argument to find-package is a local nickname defined in the current package, it returns the package nicknamed by this nickname.

This also affects all implied calls to find-package, including but not limited to those performed by the lisp reader as well as those performed by defpackage, make-package, export, find-symbol, import, rename-package, shadow, shadowing-import, delete-package, with-package-iterator, unexport, unintern, in-package, unuse-package, use-package, do-symbols, do-external-symbols, do-all-symbols, intern, package-name, package-nicknames, package-shadowing-symbols, package-use-list, package-used-by-list.

add-package-local-nickname, remove-package-local-nickname, package-local-nicknames and package-locally-nicknamed-by are also affected.

The only exception is the tilde slash directive of format, which should not use local nicknames from any package when looking up the specified symbol.

4.4. rename-package

When a package is renamed with rename-package, it retains all local nicknames it has defined, as well as all local nicknames by which it is nicknamed.

4.4.1. Implementation dependent

(Contains proposals: see Issue 5.)

If the new-name or one of the new-nicknames is shadowed by one of the local nicknames of the package being renamed, a style warning might be issued.

4.5. delete-package

When a package is deleted with delete-package, all local nicknames defined in that package are removed, as well as all local nicknames by which it is nicknamed.

This also means that a deleted package must not be available via calls to package-locally-nicknamed-by-list and package-local-nicknames.

4.6. format

See Issue 8.

4.7. \*features\*

If an implementation supports package-local nicknames, it should add symbols :package-local-nicknames and :cdr-NN (per CDR 14) to *features*.

5. Examples

[TODO]

Author: Gleefre

Created: 2024-07-07 Sun 13:23