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

lexical package aliases?

86 views
Skip to first unread message

D Herring

unread,
Feb 5, 2012, 3:15:46 AM2/5/12
to
functioning prototype: with-package-aliases.lisp
(defun exchange (q p form)
"walk form, replacing all symbols in package q with symbols in
package p (modify in place)"
;; todo: handle circular forms, other types
(typecase form
(symbol
(when (eql q (symbol-package form))
(setf form (intern (symbol-name form) p))))
(list
(setf (car form) (exchange q p (car form)))
;; recurse
(when (cdr form)
(setf (cdr form) (exchange q p (cdr form))))))
form)

(defmacro with-package-aliases (aliases &body body)
"allow package aliases in body, aliases should use internal
(package::symbol) notation, all packages must already exist"
(dolist (pair aliases)
(assert (= (length pair) 2))
(let ((q (find-package (first pair)))
(p (find-package (second pair))))
(exchange q p body)))
`(progn ,@body))


example: test-with-package-aliases.lisp
#.(or (find-package :x) (make-package :x :use nil))

(with-package-aliases ((:x :cl))
(x::print "hi"))


Fundamental flaws?
Suggested improvements?

Thanks,
Daniel

Pascal J. Bourguignon

unread,
Feb 5, 2012, 11:30:22 AM2/5/12
to
D Herring <dher...@at.tentpost.dot.com> writes:

> example: test-with-package-aliases.lisp
> #.(or (find-package :x) (make-package :x :use nil))
>
> (with-package-aliases ((:x :cl))
> (x::print "hi"))
>
>
> Fundamental flaws?

I would say yes, it's an ugly hack.


> Suggested improvements?

Use a reader macro instead of a macro.

[with-package-aliases ((:x :cl))
(x:print "hi")]

should let you avoid creating a package named X and interning symbol
there. It will also be harder to implement, sorry about that. But it
will be cleaner to use.


One reason to use package aliases (or nicknames so far) is to "shadow" a
package. For example, I could have my own implementation of print in
MY-CL, and write:

[with-package-aliases ((:cl :my-cl)) ; should work too.
(in-package "CL")
(print "hi")] ; should be read as (my-cl:print "hi")


Have fun!

--
__Pascal Bourguignon__ http://www.informatimago.com/
A bad day in () is better than a good day in {}.

RG

unread,
Feb 5, 2012, 12:43:03 PM2/5/12
to
In article <877h01x...@kuiper.lan.informatimago.com>,
"Pascal J. Bourguignon" <p...@informatimago.com> wrote:

> D Herring <dher...@at.tentpost.dot.com> writes:
>
> > example: test-with-package-aliases.lisp
> > #.(or (find-package :x) (make-package :x :use nil))
> >
> > (with-package-aliases ((:x :cl))
> > (x::print "hi"))
> >
> >
> > Fundamental flaws?
>
> I would say yes, it's an ugly hack.
>
>
> > Suggested improvements?
>
> Use a reader macro instead of a macro.
>
> [with-package-aliases ((:x :cl))
> (x:print "hi")]
>
> should let you avoid creating a package named X and interning symbol
> there. It will also be harder to implement,

Not really. All you have to do is something like:

(defun WPA-reader-foo (stream p q)
(let ((old-nicknames (package-nicknames p)))
(rename-package p p (cons q old-nicknames))
(prog1 (read stream) (rename-package p p old-nicknames))))

Or something like that.

But why on earth would you want with-package-aliases?

rg

Pascal J. Bourguignon

unread,
Feb 5, 2012, 1:26:22 PM2/5/12
to
RG <rNOS...@flownet.com> writes:

> Not really. All you have to do is something like:
>
> (defun WPA-reader-foo (stream p q)
> (let ((old-nicknames (package-nicknames p)))
> (rename-package p p (cons q old-nicknames))
> (prog1 (read stream) (rename-package p p old-nicknames))))
>
> Or something like that.

Wouldn't work: you may call (indirectly) a function in the renamed
package, and it my try to intern a symbol in the wrong package due to
the renaming.


> But why on earth would you want with-package-aliases?

Along with the reader macro, it would keep the aliasing in the lexical
scope.

Tim Bradshaw

unread,
Feb 5, 2012, 1:56:07 PM2/5/12
to
"Pascal J. Bourguignon" <p...@informatimago.com> wrote:

> Along with the reader macro, it would keep the aliasing in the lexical
> scope.

A long time ago, I implemented something which made package names be
dependent on *package*. So, if *package* was org.tfeb.cl-user, "cl:if"
might read as org.tfeb.cl:if. This was not completely portable, but worked
in a good number of implementations, and did what I wanted, which is
dynamic package aliases (not lexical).

I might revive this as I have some other stuff to revive. I also have a
sketch of a Pitman-style "substandard" to provide a (sub)standard place to
hook package lookup which would allow experimentation with this sort of
thing. I got a bit demotivated about finishing it as I suspect my views on
namespaces are completely different to most Lisp people (I think the CL
package system is good enough but needs a convention around dns-structured
names, most people seem happy with flat names but would like to replace the
package system with something else).

Pascal J. Bourguignon

unread,
Feb 5, 2012, 2:28:51 PM2/5/12
to
We share this opinion.

RG

unread,
Feb 6, 2012, 1:57:32 AM2/6/12
to
In article <8739apx...@kuiper.lan.informatimago.com>,
"Pascal J. Bourguignon" <p...@informatimago.com> wrote:

> RG <rNOS...@flownet.com> writes:
>
> > Not really. All you have to do is something like:
> >
> > (defun WPA-reader-foo (stream p q)
> > (let ((old-nicknames (package-nicknames p)))
> > (rename-package p p (cons q old-nicknames))
> > (prog1 (read stream) (rename-package p p old-nicknames))))
> >
> > Or something like that.
>
> Wouldn't work: you may call (indirectly) a function in the renamed
> package, and it my try to intern a symbol in the wrong package due to
> the renaming.

I don't understand what it means to "call a function in a package."
Packages don't contain functions, they contain symbols.

I presume you mean something like this:

(with-package-aliases ((:x :cl))
(funcall (intern "PRINT" :x) "hi")))

but that doesn't work in Daniel's original implementation either. It
also opens up some very thorny issues. Is this supposed to work?

(defun foo () (x::print "hi"))

(with-package-aliases ((:x :cl)) (foo))


> > But why on earth would you want with-package-aliases?
>
> Along with the reader macro, it would keep the aliasing in the lexical
> scope.

Huh? Which lexical scope? This is (per your suggestion) a reader macro
we're talking about here. There is no lexical environment in a reader
macro.

rg

Pascal J. Bourguignon

unread,
Feb 6, 2012, 2:14:18 AM2/6/12
to
RG <rNOS...@flownet.com> writes:
>> > But why on earth would you want with-package-aliases?
>>
>> Along with the reader macro, it would keep the aliasing in the lexical
>> scope.
>
> Huh? Which lexical scope? This is (per your suggestion) a reader macro
> we're talking about here. There is no lexical environment in a reader
> macro.

But there is! A reader macro deals with text, and defines a textual
scope, which is the definition of a lexical scope. All the characters
between the closing parenthesis of the first list inside
[with-package-aliases, and the closing ], define the lexical scope
where the package aliases are in effect.

With:

(defpackage "X" (:use "CL"))
(in-package "X")
(defun print (obj) (list obj obj))
(in-package "CL-USER")
(defun f () (x::print 'hi))


(with-package-aliases ((:x :cl))
(x::print (f)))

would print:

HI
HI

while:

[with-package-aliases ((:x :cl))
(x::print (f))]

would print:

(HI HI)

Kaz Kylheku

unread,
Feb 6, 2012, 2:51:59 AM2/6/12
to
On 2012-02-06, RG <rNOS...@flownet.com> wrote:
> I don't understand what it means to "call a function in a package."
> Packages don't contain functions, they contain symbols.

Packages don't contain functions, Lexicons contain functions! :)

Kaz Kylheku

unread,
Feb 6, 2012, 3:08:00 AM2/6/12
to
On 2012-02-06, Pascal J. Bourguignon <p...@informatimago.com> wrote:
> [with-package-aliases ((:x :cl))
> (x::print (f))]

Hmm, I could roll this aliasing functionality into the PKG reader macro.
Does this have some earth-shattering utility? You can instantly re-target
a subset of some block of code to another package.

What if x::print is spelled just print in some places in the form? (I.e. the
symbol is present in the current package). Those occurences should still be
transformed according to the alias, for consistency, not only the qualified
symbols, no?

Also, speaking of interning, if x::foo occurs, but cl::foo does not exist,
do you create it?

RG

unread,
Feb 6, 2012, 3:35:41 AM2/6/12
to
In article <87haz4w...@kuiper.lan.informatimago.com>,
"Pascal J. Bourguignon" <p...@informatimago.com> wrote:

> RG <rNOS...@flownet.com> writes:
> >> > But why on earth would you want with-package-aliases?
> >>
> >> Along with the reader macro, it would keep the aliasing in the lexical
> >> scope.
> >
> > Huh? Which lexical scope? This is (per your suggestion) a reader macro
> > we're talking about here. There is no lexical environment in a reader
> > macro.
>
> But there is! A reader macro deals with text, and defines a textual
> scope, which is the definition of a lexical scope.

No, it isn't. At least that's not the definition of lexical scope in
the ANSI CL standard. That definition is:

lexical scope n. scope that is limited to a spatial or textual region
within the establishing form.

There is no "establishing form" in a reader macro.

But fine, we can go with your definition for now...

> All the characters
> between the closing parenthesis of the first list inside
> [with-package-aliases, and the closing ], define the lexical scope
> where the package aliases are in effect.
>
> With:
>
> (defpackage "X" (:use "CL"))
> (in-package "X")
> (defun print (obj) (list obj obj))

That's an error. You can't rebind symbols in the CL package.

Perhaps you meant something more like this:

(make-package :x)
(make-package :y)

(in-package :x)
(defun foo () 'x-foo)
(defun baz () 'x-baz)

(in-package :y)
(defun baz () 'y-baz)
(defun bar () 'y-bar)

(in-package :cl-user)

(with-package-aliases ((:x :y))
(x::foo)
(x::baz)
(x::bar)
)

Running this code under Daniel's reference implementation is instructive.

> (in-package "CL-USER")
> (defun f () (x::print 'hi))
>
>
> (with-package-aliases ((:x :cl))
> (x::print (f)))
>
> would print:
>
> HI
> HI
>
> while:
>
> [with-package-aliases ((:x :cl))
> (x::print (f))]
>
> would print:
>
> (HI HI)

It's hard to say what [w-p-a ...] would do becase because we don't have
a working version of it yet in this thread.

But the problem you'd encounter following the strategy I suggested has
nothing to do with lexical scope, it has to do with the fact that you
can't use the name of an existing package as a nickname for another
package. But that is also easily solved (assuming the goal is to
reproduce the behavior of Daniel's reference implementation) by
temporarily renaming the existing package.

I still fail to see the point of this exercise.

rg

Marco Antoniotti

unread,
Feb 6, 2012, 4:44:42 AM2/6/12
to
On Sunday, February 5, 2012 7:56:07 PM UTC+1, Tim Bradshaw wrote:
> "Pascal J. Bourguignon"
I think I remember it. It looked good.

BTW. The place for "sub-standard" is the CDR.

MA

Pascal J. Bourguignon

unread,
Feb 6, 2012, 12:28:52 PM2/6/12
to
Kaz Kylheku <k...@kylheku.com> writes:

> On 2012-02-06, Pascal J. Bourguignon <p...@informatimago.com> wrote:
>> [with-package-aliases ((:x :cl))
>> (x::print (f))]
>
> Hmm, I could roll this aliasing functionality into the PKG reader macro.
> Does this have some earth-shattering utility? You can instantly re-target
> a subset of some block of code to another package.

I didn't say it was useful.


> What if x::print is spelled just print in some places in the form? (I.e. the
> symbol is present in the current package). Those occurences should still be
> transformed according to the alias, for consistency, not only the qualified
> symbols, no?

Yes.


> Also, speaking of interning, if x::foo occurs, but cl::foo does not exist,
> do you create it?

You can't.


Another way to do it is to use in-package, outside of files.

(defpackage "X-USER" (:use "X"))
(in-package "X-USER")
(load "source.lisp")

instead of:

(in-package "CL-USER")
(load "source.lisp")


with a file source.lisp not containing (in-package "WHATEVER").

Pascal J. Bourguignon

unread,
Feb 6, 2012, 12:31:26 PM2/6/12
to
RG <rNOS...@flownet.com> writes:

> I still fail to see the point of this exercise.

Indeed, [with-package-aliases ...] would be useless, since you can as
well edit the source code, or use in-package outside of files.

D Herring

unread,
Feb 6, 2012, 11:55:37 PM2/6/12
to
On 02/05/2012 11:30 AM, Pascal J. Bourguignon wrote:
> D Herring<dher...@at.tentpost.dot.com> writes:
>
>> example: test-with-package-aliases.lisp
>> #.(or (find-package :x) (make-package :x :use nil))
>>
>> (with-package-aliases ((:x :cl))
>> (x::print "hi"))
>>
>>
>> Fundamental flaws?
>
> I would say yes, it's an ugly hack.

Eh, hardly a fundamental issue, and beauty is in the eye of the
beholder. Mine was a solution implemented in under 10 minutes with a
few lines of code. Try that in most other languages.


Requiring the heavy machinery of a reader macro is even uglier in some
regards. Also fraught with peril when other reader macros are in
effect. (For a recent example, your [with-p-a] notation would
conflict with Kaz's lisp-1 notation.)


Regarding why:
I frequently bump into situations where symbol names conflict, proper
package nicknames are too heavy (global change), and the full package
name is obnoxious. An abbreviation having lexical scope is just about
right.

The specific problem that made me revisit this was a pattern-matching
mini-language (for a parser). It needs to have cl:or, allow keyword
:or, and support parser:or (actual name of parser package is longer).
The parser currently uses longer names (like parser-or), but that
gets ugly real fast. I would prefer p:or, but p::or seemed more
portable (reader doesn't complain about missing symbols).

Maybe in this specific case it would be better to have the parser use
symbols like &or; but that doesn't scale in general (the whole reason
we have packages).


I've got a couple easy features to implement that should make my hack
more palatable, but they may not happen for a few days.

- Daniel

D Herring

unread,
Feb 21, 2012, 12:32:17 AM2/21/12
to
FWIW, here's my current prototype. It hasn't been tested with
anything real yet. (In its intended setting, I decided to import
parser:or and explicitly type cl:or where needed.)

web: http://git.androdna.com/?p=lisp/package-aliases.git

git: http://git.androdna.com/git/lisp/package-aliases.git

tarball:
http://git.androdna.com/?p=lisp/package-aliases.git;a=snapshot;h=master;sf=tgz

- Daniel
0 new messages