Working with Packages
Packages represent namespaces for Lisp symbols. You can think of them like an "area-code" for symbols.
In the tutorial, all of the example code that was provided started with the (in-package :gdl-user) statement. All of the symbols created when objects were defined, belonged to the :gdl-user package. Because all source code files stared with the (in-package :gdl-user) statement we don't need to qualify any of the symbols, we just use their name. But if we wanted to use a symbol from another package that wasn't used by :gdl-user we would need to qualify that symbol with a package name.
To create a package, we
- Call the Gendl macro define-package
- Define the name of the new package
- Optionally provide a list of packages it will :use. Note that define-package automatically includes the following packages:
- Optionally define a list of symbols that the package will :export
(in-package :gdl-user)(define-package :my-app)
Then in the REPL we can use the in-package function to switch to out new package
GDL-USER> (in-package :my-app)#<Package "MY-APP">
The :export option
When we define a new package we can :export symbols from that package for external use. Whilst symbols from that package can still be accessed even if the aren't exported, it isn't considered to be good practice to do this, it's up to the package owner to choose to export or expose which symbols are for external use. A good watchword is
- "Not Exported = Unsupported"
Extending our define-package example to :export a symbol my-slot from our package would look like this
(in-package :gdl-user)(define-package :my-app(:export #:my-slot))
In some cases it is convenient to :export a symbol after define-package has been evaluated. This may be the case, for example, where we have a number of functions to be :exported and it is easier to manage the :export from within the functions source code file. To do this we can use the Common Lisp Special Operator eval-when as shown in the example below
(in-package :functions)(eval-when (load eval) (export 'average))(defun average (lis)(/ (apply '+ lis) (length lis)))
In this example the function average is exported from the :functions package when either a top-level form in a compiled file is being loaded (load) or when the expression would be evaluated anyway (eval)
The :use option
To access a symbol which has been exported from a package we need to use the fully qualified name for that symbol - in other words its package and symbol seperated by a colon. However, if we :use a package when we create a new package, we can access exported symbols using their symbol only. Creating a new package which uses the package created earlier would look like this
(in-package :gdl-user)(define-package :my-other-app(:use :my-app))
Bringing it all together
Let's consider 3 packages, package-a, package-b and package-c. Package package-a exports a symbol x and also defines another symbol y. Package package-b doesn't :export any symbols and doesn't :use any other packages. Package package-c :uses package package-a
GDL-USER> (define-package package-a(:export x))#<Package "PACKAGE-A">
GDL-USER> (define-package package-b)#<Package "PACKAGE-B">GDL-USER> (define-package package-c(:use a))#<Package "PACKAGE-C">
GDL-USER> (in-package package-a)#<Package "PACKAGE-A">
PACKAGE-A> (setq x 1)1
PACKAGE-A> (setq y 2)1
PACKAGE-A> (in-package package-b)#<Package "B">
PACKAGE-B> x; Evaluation aborted on #<UNBOUND-VARIABLE #x2103CD8FCD>.
PACKAGE-B> y; Evaluation aborted on #<UNBOUND-VARIABLE #x2103CD8FCD>.
PACKAGE-B> package-a:y; Evaluation aborted on #<SIMPLE-ERROR #x2103CA28BD>.
PACKAGE-B> (in-package c)#<Package "C">
PACKAGE-C> y; Evaluation aborted on #<UNBOUND-VARIABLE #x2103CD8FCD>.
PACKAGE-C> package-a:y; Evaluation aborted on #<SIMPLE-ERROR #x2103CA28BD>.
When we are in package package-b, which does not :use package package-a
- we can access exported symbols from package package-a but need to qualify the symbol with the package name and a single colon
- we can access unexported symbols from package package-a but need to qualify the symbol with the package name and double colon
When we are in package package-c, which does :use package package-a
- we can access exported symbols from package package-a just by their name
- we can access unexported symbols from package package-a but need to qualify the symbol with the package name and a double colon