━━━━━━━━━━━━━━━━━━━━━ PLN CDR DRAFT: SPEC Gleefre ━━━━━━━━━━━━━━━━━━━━━ 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-p' — /generalized 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]. [defpackage] See section 3.1 4.2 make-package ──────────────── See [make-package]. [make-package] See section 3.2 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]