Common Lisp Package: LOCAL-PACKAGE-ALIASES

README:

Local Package Aliases


Allows to define inside of a package aliases to refer other packages.

Provides a reader macro $ to use the aliases (active only in packages having

alias mapping, and does not affect other code).

Portable.

` common-lisp

(ql:quickload :local-package-aliases)

(defpackage com.my-company.some-library

(:use cl)

(:export #:func))

(in-package #:com.my-company.some-library)

(defun func () "hello")

(defpackage some-application (:use cl))

(in-package #:some-application)

(local-package-aliases:set #:com.my-company.some-library #:lib

                       #:some.other.library #:olib)  
 

(read-from-string "$lib:func")

;; => COM.MY-COMPANY.SOME-LIBRARY:FUNC

($lib:func)

;; => "hello"

;; The aliases are not global, they are scoped only to the package

;; where they are defined:

(in-package #:cl-user)

(read-from-string "$lib:func")

;; => ERROR There is no package named "$LIB" .

;; The $ reader macro is non-terminating,

;; therefore it is only activated when $

;; is on the beginning of a token.

;; $ in the middle of a token has no special

;; effect.

(in-package #:some-application)

(read-from-string "just-a-$-symbol")

;; => JUST-A-$-SYMBOL

;; The $ designates alias reference only if the

;; current package has alias mapping defined. If there

;; is no alias mapping in the current package, then

;; $ is interpreted as usually:

(local-package-aliases:set) ;; installs empty aliases mapping

(read-from-string "$-a-sybmol")

;; => $-A-SYMBOL

(in-package #:cl-user)

(read-from-string "$-a-sybmol")

;; => $-A-SYMBOL

;; Therefore it is safe to enable $ macro

;; globally, for example from Lisp init file,

;; without affecting the code using $ for other

;; purposes.

`

To enable the $ macro in your lisp session (may be put into

the lisp initialization file):

` common-lisp

(local-package-aliases:set-aliasing-reader readtable)

`

Another macro character than $ may be used.

See the docstring for local-package-aliases:set-aliasing-reader

for parameters description.

To return to the standard syntax:

` common-lisp

(set-syntax-from-char #\$ #\$ readtable (copy-readtable nil))

`

To make the $ macro enabled when your ASDF system

is compiled by other people, use the :acound-compile argument:

` common-lisp

(asdf:defsystem #:some-application

:depends-on (#:local-package-aliases

           #:com.my-company.some-library) 

:around-compile "local-package-aliases:call-with-aliasing-readtable"

:components ((:file "some-application")))

`

SLIME support


SLIME assumes standard readtable when meets tokens like pkg:symb

and doesn't undersdand our aliases.

In result SLIME symbol completion, slime-edit-definition, function

arguments hints do not work out of box for aliased tokens like $lib:func.

The solution we found is to hook into swank, and wrap evaluation

of every SLIME request with temporary adding the aliases defined

in the current package as nicknames for their corresponding packages.

So, during dynamic extent of every slime request, the aliases become

real package nicknames and SLIME can handle them as usually.

It must be noted that this solution is not entierly transarent:

when working from SLIME not only reader understands the aliases,

but also (find-package :$lib) will find the package.

But of course during normal run-time only reader knows about

the aliases.

Functions hook-into-swank and unhook-from-swank enable/disable

this SLIME support.

To have the SIME support enabled automatically add the following

to your ~/.swank.lisp:

` common-lisp

(when (find-package :local-package-aliases)

(funcall (read-from-string "local-package-aliases:hook-into-swank")))

`

Or this in ~/.emacs:

` common-lisp

(add-hook 'slime-connected-hook

      (lambda ()  
 
        (slime-eval '(cl:when (cl:find-package :local-package-aliases)  
 
                        (cl:funcall (cl:read-from-string "local-package-aliases:hook-into-swank")))))) 

`

Other Package Aliasing Approaches


Here is some information, solutions and ideas I encountered recently

related to package aliases.

The solutions vary in:

  • whether the aliases only honored by reader, or they also

affect functions like cl:find-package, cl:find-symbol

  • is the solution a portable Common Lisp or it relies on patches

or language extensions

  • whether the aliases are scoped to a package, or somehow else

  • whether the solution is specific about aliasing scheme,

or it's a lower-level tool allowing to build various

aliasing approaches.

cl-package-aliases - http://www.cliki.net/cl-package-aliases


Provides patches for 5 lisp implementations to introduce

aliases. Aliases are scoped to package and visible both

for reader and for standard functions like cl:find-symbol.

package-renaming - http://common-lisp.net/gitweb?p=users/frideau/package-renaming.git;a=tree


Tools based on cl:rename-package to temporary give packages

desired short names/nicknames. Portable. To make the renaming

local it is expected to be used with the ASDF's :acound-compile argument.

CL language extensions


There were discussions to develop a CL language extension

and propose it to CL vendors. The extension might be

a hook called by CL to resolve package prefix. I.e. when

CL encounteres a token like pkg:symbol it calls the hook

with "pkg" string and the hook should return a package object

or maybe just a string designating real package name.

Such hook may be called *package-prefix-resolver*.

Alternatively the hook may be passed the full token "pkg:symbol"

and be responsible to resolve both package name and symbol.

Such hook may be named *parse-token-hook*.

There were considerations whether these hooks should be called only

form reader or by cl:find-sybmol and other functions.

Sketch for a CDR: http://paste.lisp.org/display/133561

Discussions on the #lisp irc channel:

http://ccl.clozure.com/irc-logs/lisp/2012-11/lisp-2012.11.05.txt

http://ccl.clozure.com/irc-logs/lisp/2013-01/lisp-2013.01.06.txt

One more possible language extension would be to allow to

fully substitute the lisp reader. In this case there might

be are public library implementing fully compliant CL reader.

Lisp implementation will delegate functions like cl:read,

cl:read-delimeted-list, cl:set-syntax-from-char

and others to the pluggable reader. The reader by default

honors cl:*readtable*, cl:*package* and other variables,

but also allows any custom hooks we need, such as described

above parse-token-hook and/or package-prefix-resolver.

It must be noted that interface between CL and such a pluggable

reader will consist of many functions. Also, the reader should

come with it's own implementations for all the reader macros,

because standard reader macros are not implemented in terms

of public cl:* functions, but use private, not-exported

functions of the CL reader, such as reat-token.

If speak about pluggable reader, it's necessary to mention

the reader-interception project:

http://common-lisp.net/cgi-bin/gitweb.cgi?p=users/frideau/reader-interception.git;a=tree;js=1

It's a portable solution allowing to plug-in your own reader.

It relies on the trick to look at the first character of input,

configure this character temporary as a reader macro, and

then this reader macro may read the full input stream according

to any rules.

Conclusion


The local-package-aliases approach with reader macro seems

to be a decent approach, especially as the syntax change

only affects packages with explicitly configured aliasing

maps.

It is comparable by convenience with package-renaming.

Advantage of local-package-aliases is that it may be enabled

once and forewer in the Lisp initialization file, while

packag-renaming will require you to manually rename packages

every time you switch projects or subsystems during or at the beginning

of Lisp session.

In my opinion a form of package aliasing deserves to be

introduced as a CL extension into all implementations.

The language extension may be specific, targeting only package

aliases (like cl-package-aliases project proposes).

It will encourage consistent coding practice accross

all the CL programs.

To simplify adoption of the extension by the CL implementations,

I believe it would be enought to have aliasing only in reader.

Calls to cl:find-package are rare, we can pass

full package names to it. On the other hand, if cl:find-package

is unaware of aliases, it may complicate support by SLIME.

As for more low-level language extenstions, like pluggable

reader or various hooks, I would welcome them too,

just to make Lisp more programmable and allow programmers

to solve their needs simpler.

Author


Anton Vodonosov, avodonosov@yandex.ru

License


MIT

FUNCTION

Public

CALL-WITH-ALIASING-READTABLE (THUNK)

Convenience function to use in ASDF's :around-compile argument.

SET-ALIASING-READER (TO-READTABLE &OPTIONAL (MACRO-CHAR $) DEFAULT-READTABLE)

Modifies TO-READTABLE so that MACRO-CHAR at the beginning a token in form $ALIAS:SYMBOL or $ALIAS::SYMBOL is used to refere other packages, according to the aliases set in the current packges by LOCAL-PACKAGE-ALIASES:SET. The DEFAULT-READTABLE is used when the current package has no aliases. In this case the hangler for MACRO-CHAR is retrieved from DEFAULT-READTABLE and temporary applied to the current readtable using CL:SET-SYNTAX-FROM-CHAR. The default value for DEFAULT-READTABLE is copy of TO-READTABLE before it's syntax is modified.

UNHOOK-FROM-SWANK

Returns T if the hook handler was uninstalled, and NIL otherwize.

Undocumented

HOOK-INTO-SWANK

Private

CALL-WITH-NICKNAMES (ALIAS-TABLE FN)

Helper function useful to hook into SLIME in order to provide completion, go-to-definition, function parameters help and other SLIME support.

FIND-ALIASED-SYMBOL (TOKEN)

TOEKN is a string in the form alias:symbol or alias::symbol.

SET-ALIAS-TABLE-FOR (FOR-PACKAGE &REST PACKAGE-ALIAS-PAIRS)

PACKAGE-ALIAS-PAIRS is a list in the form (package-designator alias-string package-designator alias-string ...)

Undocumented

ALIAS-TABLE-FOR (PACKAGE)

ALIASES-OF (PACKAGE IN-PACKAGE)

ALIASING-READTABLE (&OPTIONAL (PROTOTYPE-READTABLE *READTABLE*) (MACRO-CHAR $))

APPLY-CASE-MODE (READTABLE-CASE-MODE STR)

ERR (FORMAT-CONTROL &REST FORMAT-ARGUMENTS)

FIND-ALIASED-PACKAGE (ALIAS)

HAS-LOCAL-ALIASES-P (PACKAGE)

READ-PACKAGE-ALIASED-SYMBOL (STREAM CHAR ORIGINAL-READTABLE)

READ-TOKEN (STREAM)

SWANK-BUFFER-PACKAGE

TERMINATING-MACRO-CHAR-P (CHAR)

TERMINATOR-P (CHAR)

WHITESPACE-P (CHAR)

WITH-ALIASES-AS-NICKNAMES-HOOK (NEXT-FN)

MACRO

Public

Undocumented

SET (&REST PACKAGE-ALIAS-PAIRS)

VARIABLE

Private

*PACKAGE-TO-ALIASES-MAP*

Mapping from package object to a hash-table of local aliases active in this package. The hash-table of local aliases maps string alias to a package designator.

CONDITION

Private

Undocumented

ALIASED-REF-ERROR