[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

package changes

    Date: 12 May 83 03:06:44 EDT
    From: DILL@CMU-CS-C

    I propose modifying Fahlman's proposal as below.


      The only reason I can see to have these functions is to build big
    packages out of little ones.  Here is a more tasteful solution
    that requires no additional functions:

    a) Define a package to represent the whole system, and export from it
    all the symbols that users will want to import from it.  

    b) For the little packages composing the system, build them in the
    natural way, except IMPORT all the symbols from the package of part a.

    Another way to group a bunch of packages would be to build another
    package which imports symbols from the grouped package, and then
    selectively exports them for importation by users.

Or just have the little packages USE-PACKAGE the big package (this is how it
works in the Lisp machine now, except that there is a spurious requirement of
hierarchy rather than heterarchy).  Or have the big package not export any
symbols on its own, but have each of the little packages include an EXPORT
statement with the big package as its second argument, taking their own
symbols and exporting them as externals of the big package rather than as
externals of the particular little package.  If they want they could export
symbols from both; it ought to work for a symbol to be an external of more
than one package.

In this case of course one needs to make a conscious decision what the
home package of these symbols should be, which is really a conscious
decision of what prefix one wants to see when the symbol is printed with
a qualified name.  You could want to see either the name of the big
conglomerate package, or the name of the specific internal package that
actually defines the symbol.  An advantage of flushing INCLUDE-PACKAGE
is that it makes the need for this decision more apparent.

The gain in simplification of the implementation from flushing INCLUDE-PACKAGE
is extremely small; I would guess between 15 and 20 lines of code.  But I
am in favor of this anyway, purely for documentation reasons.

    With the addition of a structured package name space of some kind, the
    names of the "internal" packages could also be isolated in order to
    avoid conflicts with user-level package names.

We seem to have decided to avoid things like non-global package naming for
now.  It can easily be added later, but there are more user-interface issues
than you would possibly believe beforehand, as we have found out through sad
experience with the Lisp machine package system.

    2.  Flush DECLARE-PACKAGE.  Require that ALL symbols exported by a
    package be exported before being imported (in any way) into other
    packages (modulo debugging).

I never liked declare-package, and it does seem that the more we look at it
the more problems occur.  Unless Scott's next proposal manages to cleverly fix
all the problems with declare-package, I am in favor of flushing it.  I've
pointed out before how in the simple cases of smallish programs, even including
mutually-referencing packages, one can have the package-setup forms and the
program-defining forms all in a single file while still conforming to the rule
that a package must be set up before it is used.  It only requires that LOAD
be callable from inside LOAD (pace pdp10 Maclisp).

Note: in the above I am using the ugly expression "set up" because in the Lisp
machine this is called "declaring a package", while in the recent Common Lisp
package discussion "declaring a package" has come to mean something else.  I
specifically mean "set up" to be what the author of the package does, with
MAKE-PACKAGE or IN-PACKAGE or whatever it ends up being called.  Don't call

    3.  Define (USE-PACKAGE P) to IMPORT the symbols in package P's export
    list at the time that USE-PACKAGE is called ("copy" style).  

I pointed out in November why copy semantics is inherently unworkable
for packages.  But you may not have seen those comments.

								 Mistakes in
    export lists will have to be remedied by tracking down the packages that
    have imported them.  This may seem like a pain, 

Indeed it may.  I think it is important for the package system to provide
aid in tracking down such things.  This is why reference rather than copy
semantics is preferred.  In fact, I'll add to my previous proposal,
that to be workable SHADOW must record what it did in a structure slot in
the package, that IMPORT must do the same thing.

    I also propose the following function:

    @defun[Fun {load-package} Args {@i[package-name]} @Optional {@i[file-name]}]

    If the package @i[package-name] exists, do nothing.  Otherwise, load the
    file named by @i[file-name].  If this argument is not supplied, load the
    package from a file established by the system as the standard source for
    that package, signalling an error if there is no file established for
    @i[package-name].  If loading the file did not cause the package to
    exist, signal an error.

    USE-PACKAGE should call this function if the package it needs is not
    defined.  Add an optional filename argument to USE-PACKAGE.

    Note that a load can be forced by calling LOAD explicitly.

Essentially what you are proposing here is to conflate the ideas of
"package" and "module" (laser manual page 291).  The Lisp machine originally
worked this way, but we abandoned it some years ago.  I'm not sure that I
can come up with an explanation of why this is not a good idea that would
convince anyone not familiar with that history.  Certainly in the simplest
case modules (the units of separate maintenance, compilation, and loading)
and packages (the units of separate symbol naming) are related one-to-one.  But it
is quite possible for a large module to involve more than one package, and
it is also quite possible for a single package to contain more than one
module.  So it doesn't really make sense to replace these two distinct ideas
with one single idea.

Some Lisp machine background that may not be familiar to everyone on the
list:  We use the admittedly-overworked word "system" for what the laser
manual calls a "module".  This is because a system is a compound structure
whose elements are called modules.  A system is the unit of compilation,
loading, and especially maintenance; thus a system is "a program."  A
module is the unit of dependency; thus one module may require loading
a second module into the compiler, to define macros or whatever, before
it may be compiled.  Of course under both of these is a third unit, the
file, which is the atomic unit of compilation and loading but is otherwise
not intrinsically interesting.  Systems can also be recursively constructed
out of other systems.

We have a general registry of "systems" and the files that must be loaded
in order to load the system.  Thus one would not normally supply a file
name when demanding the loading of a system, but would simply let the
general registry figure it out.  There is a directory with one file for
each system, the name of the file being derived in an obvious way from
the name of the system.  This file contains a Lisp form that specifies
where the files of the system reside (sometimes using logical pathnames);
this includes the file that contains the definition of the internal
structure of the system, i.e. its inter-module dependencies and
its package "set up".  Other implementations would choose to do this
in somewhat different ways.  But Common Lisp can quite reasonably
require every implementation to provide some primitive "library"
mechanism of this type, so that one can call some function with a name
like REQUIRE and the name of "a program", and be assured that that
program is present in the Lisp environment.



Flush include-package.
Keep use-package the way it is.
Flush declare-package.
Keep the provide/require stuff from the laser manual.
Write documentation on how to use all this stuff, for both
 simple cases and complex cases.