In the previous episode, we’ve obtained a list of all symbols with the function OBLIST, and looked at their internal slots with SYMBOL->CELLS. While this was useful to examine a single package like, say, COMMON-LISP-USER, it didn’t tell us what other packages were available.

In this post, we’ll revisit OBLIST, rewriting it with the somewhat unlispy LOOP facility. Then we’ll see how to get a list of all packages, and how to obtain a package description where available.

Revisiting OBLIST

As a reminder, here’s how we defined OBLIST previously:

[lisp];;; OBLIST returns a list of all available symbols

(defun oblist (&optional (packagename "COMMON-LISP-USER"))
"Return a list of all symbols in PACKAGENAME"
(let ((the-symbols ()))
;; DO-SYMBOLS is a common-lisp function
(do-symbols (var packagename)
(setf the-symbols (cons var the-symbols)))
(setf the-symbols (sort the-symbols #’string-lessp))
the-symbols))[/lisp]

You can see how we iterated over the symbols of a specified package using the ANSI Common Lisp function DO-SYMBOLS.

Poor man’s OBLIST

Without any kind of programming, we could simply have used the ANSI Common Lisp function APROPOS-LIST (or its cousin APROPOS) with an empty string as argument:

[lisp];;; Poor man’s OBLIST using APROPOS-LIST

(defun poor-man-oblist (&optional (substring ""))
"Return a list of all symbols containing SUBSTRING as a substring."
(sort (apropos-list substring) #’string-lessp))[/lisp]

Invoke it as (poor-man-oblist) to get a long list of symbols from all possible packages. Note that this is not exactly the same as our previous OBLIST, because POOR-MAN-OBLIST iterates over all symbols, whereas OBLIST iterated only over the symbols of a specific package (and the symbols that package uses, i.e. imports).

If you know already what you’re looking for (and this is not always easy, isn’t it?), provide a substring to POOR-MAN-OBLIST. For example, what kind of symbols does Clozure Common Lisp provide that contain “APROPOS” as a substring?

CL-USER> (poor-man-oblist "apropos")
(CCL::%APROPOS-SUBSTRING-P CCL::*APROPOS-CASE-SENSITIVE-P*
 CCL::*APROPOS-INDENT-TO-SEARCH-STRING* APROPOS CCL::APROPOS-AUX APROPOS-LIST
 CCL::APROPOS-LIST-AUX SWANK:APROPOS-LIST-FOR-EMACS
 CCL::APROPOS-STRING-INDENTED SWANK::APROPOS-SYMBOLS
 SWANK::MAKE-APROPOS-MATCHER)                                                 
 
CL-USER>
Another programmed OBLIST

Suppose you didn’t know about APROPOS-LIST nor DO-SYMBOLS. How would you implement OBLIST?

A nice way to do this is the LOOP facility. Because we will be reusing the following code in three functions, we define the helper macro LOOP-AS-PACKAGE like this:

[lisp];;; This is a helper macro for the followig PACKAGE-*-SYMBOLS functions.

(defmacro loop-as-package (package symbol-type)
`(loop
for symname being each ,symbol-type of ,package
collect symname into reslist
finally
(return (setf reslist
(sort reslist #’string-lessp)))))[/lisp]

This macro, when expanded, will collect all symbols of a package into a sorted list, and will return this list.

So let’s put this macro to use in the following functions:

[lisp];;; PACKAGE-ALL-SYMBOLS returns a list of ALL symbols of a given package
;;; PACKAGE-PRESENT-SYMBOLS returns only a list of present symbols
;;; PACKAGE-EXTERNAL-SYMBOLS returns a list of exported symbols

(defun package-all-symbols (&optional (package "COMMON-LISP-USER"))
"Return a list of symbols in PACKAGE name."
(loop-as-package package symbol))

(defun package-present-symbols (&optional (package "COMMON-LISP-USER"))
"Return a list of present symbols in PACKAGE name."
(loop-as-package package present-symbol))

(defun package-external-symbols (&optional (package "COMMON-LISP-USER"))
"Return a list of external symbols in PACKAGE name."
(loop-as-package package external-symbol))[/lisp]

The only difference between the three functions is the second argument to our macro LOOP-AS-PACKAGE:

  • if we provide symbol, the LOOP will return a list of all symbols of a package, be they imported from other packages or not.
  • if we provide present-symbol, the LOOP will return only a list of symbols in the desired package that have not been imported with USE.
  • providing external-symbol to the LOOP will return only a list of symbols that the desired package exports, i.e. external symbols only.

How is this useful?

To get an effect similar to our original OBLIST, we simply call PACKAGE-ALL-SYMBOLS, with or without arguments. Try it on the default package COMMON-LISP-USER by calling (package-all-symbols) without arguments: you will see a long list of symbols, including all symbols from the USEed packages, from COMMON-LISP in particular. This is what OBLIST was all about.

If you’re only interested in the symbols you defined in the current session, what you want is the list of present symbols, i.e. symbols interned in COMMON-LISP-USER but not imported via the USE directive, neither directly nor indirectly. Or, put another way, you want all symbols that are in a package, but not those that were imported from elsewhere. PACKAGE-PRESENT-SYMBOLS is what you want now.

Here’s an example using cmucl 20c after importing and compiling the file package.lisp (see below for the complete file):

CL-USER> (package-present-symbols)
(|2011-07-03| BEING DOC-ALL-PACKAGES EACH EXTERNAL-SYMBOL FINALLY FOR INTO
 LOOP-AS-PACKAGE NAME NAME-ALL-PACKAGES OF PACKAGE-ALL-SYMBOLS
 PACKAGE-DESCRIBE-STRING PACKAGE-DOC-STRING PACKAGE-EXTERNAL-SYMBOLS
 PACKAGE-PRESENT-SYMBOLS POOR-MAN-OBLIST PRESENT-SYMBOL RESLIST SSTREAM
 SUBSTRING SYMBOL-TYPE SYMNAME)
 
CL-USER>

As you can see, we’re far from the longish list returned by PACKAGE-ALL-SYMBOLS. If you look at the file below, you’ll notice that all those symbols except the first are indeed present.

You probably know that packages define an external interface by exporting a subset of all their symbols, i.e. by making them external. If we want to look at the public/external interface of a specified package, we need a list of external symbols, and nothing else. This is what PACKAGE-EXTERNAL-SYMBOLS is for:

CL-USER> (package-external-symbols "COMMON-LISP-USER")
NIL
 
CL-USER> (package-external-symbols "PROFILE")
(PROFILE:*DEFAULT-REPORT-TIME-PRINTFUNCTION* PROFILE:*INSERT-SPACEREPORTS*
 PROFILE:*NO-CALLS* PROFILE:*NO-CALLS-LIMIT* PROFILE:*TIMED-FUNCTIONS*
 PROFILE:DELETE-SPACEREPORTS PROFILE:PRINT-SPACEREPORTS PROFILE:PROFILE
 PROFILE:PROFILE-ALL PROFILE:REPORT-TIME PROFILE:REPORT-TIME-CUSTOM
 PROFILE:RESET-SPACEREPORTS PROFILE:RESET-TIME PROFILE:UNPROFILE
 PROFILE:WITH-SPACEREPORT)
 
CL-USER>

In this example, we see that the user session package COMMON-LISP-USER doesn’t export any symbols. We also see that the cmucl 20c package PROFILE exports a reasonably long list of symbols. Try calling (package-all-symbols "PROFILE") instead of (package-external-symbols "PROFILE") yourself to see the huge difference.

What packages are available, by the way?

If you paid attention while reading the previous paragraph, you’ll have asked yourself: “Wait a minute! How did you know about this PROFILE package?” And while you’re at it, you may be wondering what packages are available in general in your specific ANSI Common Lisp implementation.

Wouldn’t it be nice to have a list of all available packages, just as we can create a list of all available symbols? Fortunately, ANSI Common Lisp defines a function LIST-ALL-PACKAGES that does just that. We’ll wrap it slightly into the following function to get a list of package names (i.e. a list of strings, instead of a list of package objects, using the standard function PACKAGE-NAME), and while we’re at it, well sort it alphabetically too:

[lisp];;; NAME-ALL-PACKAGES returns a list of ALL available package (names).

(defun name-all-packages ()
"Return a list of all package names."
(let ((reslist (mapcar #’package-name
(list-all-packages))))
(sort reslist #’string-lessp)))[/lisp]

Let’s look at the list of all package names provided by default by the current open source ANSI Common Lisp implementations.

  • Armed Bear Common Lisp (abcl):
    CL-USER> (name-all-packages)
    ("COMMON-LISP" "COMMON-LISP-USER" "EXTENSIONS" "FORMAT" "JAVA" "JVM" "KEYWORD"
     "LISP" "LOOP" "MOP" "PRECOMPILER" "PROFILER" "SEQUENCE" "SWANK"
     "SWANK-BACKEND" "SWANK-IO-PACKAGE" "SWANK-LOADER" "SWANK-MATCH" "SWANK-MOP"
     "SWANK-RPC" "SYSTEM" "THREADS" "TOP-LEVEL" "XP")
     
    CL-USER>
  • CMU Common Lisp 20c (cmucl):
    CL-USER> (name-all-packages)
    ("ALIEN" "ALIEN-INTERNALS" "ANSI-LOOP" "BIGNUM" "C" "C-CALL" "CLOS-MOP"
     "COMMON-LISP" "COMMON-LISP-USER" "CONDITIONS" "DEBUG" "DEBUG-INTERNALS"
     "DFIXNUM" "DISASSEM" "EVAL" "EXTENSIONS" "FORMAT" "FWRAPPERS" "HEMLOCK"
     "HEMLOCK-INTERNALS" "INSPECT" "INTL" "KERNEL" "KEYWORD" "LISP" "LOOP"
     "MULTIPROCESSING" "NEW-ASSEM" "PCL" "PRETTY-PRINT" "PROFILE" "STREAM" "SWANK"
     "SWANK-BACKEND" "SWANK-IO-PACKAGE" "SWANK-LOADER" "SWANK-MATCH" "SWANK-MOP"
     "SWANK-RPC" "SYSTEM" "UNIX" "WALKER" "WIRE" "X86" "XLIB" "XREF")
     
    CL-USER>
  • Steel Bank Common Lisp (sbcl):
    CL-USER> (name-all-packages)
    ("ASDF" "COMMON-LISP" "COMMON-LISP-USER" "KEYWORD" "SB-ALIEN"
     "SB-ALIEN-INTERNALS" "SB-ASSEM" "SB-BIGNUM" "SB-BSD-SOCKETS"
     "SB-BSD-SOCKETS-INTERNAL" "SB-BSD-SOCKETS-SYSTEM" "SB-C" "SB-CLTL2"
     "SB-CLTL2-SYSTEM" "SB-DEBUG" "SB-DI" "SB-DISASSEM" "SB-EVAL" "SB-EXT"
     "SB-FASL" "SB-FORMAT" "SB-GRAY" "SB-GROVEL" "SB-GROVEL-SYSTEM" "SB-IMPL"
     "SB-INT" "SB-INTROSPECT" "SB-INTROSPECT-SYSTEM" "SB-KERNEL" "SB-LOOP" "SB-MOP"
     "SB-PCL" "SB-POSIX" "SB-POSIX-SYSTEM" "SB-PRETTY" "SB-PROFILE" "SB-SEQUENCE"
     "SB-SYS" "SB-THREAD" "SB-UNIX" "SB-VM" "SB-WALKER" "SWANK" "SWANK-BACKEND"
     "SWANK-IO-PACKAGE" "SWANK-LOADER" "SWANK-MATCH" "SWANK-MOP" "SWANK-RPC")
     
    CL-USER>
  • Clozure Common Lisp (ccl):
    CL-USER> (name-all-packages)
    ("ANSI-LOOP" "ARCH" "CCL" "COMMON-LISP" "COMMON-LISP-USER" "CROSS-REFERENCE"
     "GRAY" "INSPECTOR" "KEYWORD" "MONITOR" "OPENMCL-MOP" "OPENMCL-SOCKET" "SETF"
     "SWANK" "SWANK-BACKEND" "SWANK-IO-PACKAGE" "SWANK-LOADER" "SWANK-MATCH"
     "SWANK-MOP" "SWANK-RPC" "X86" "X86-FREEBSD64" "X8632" "X8664")
     
    CL-USER>
  • Embedded Common Lisp (ecl):
    CL-USER> (name-all-packages)
    ("C" "CLOS" "COMMON-LISP" "COMMON-LISP-USER" "EXT" "FFI" "GRAY" "KEYWORD"
     "MP" "PROFILE" "SB-BSD-SOCKETS" "SERVE-EVENT" "SI" "SWANK" "SWANK-BACKEND"
     "SWANK-IO-PACKAGE" "SWANK-LOADER" "SWANK-MATCH" "SWANK-MOP" "SWANK-RPC")
     
    CL-USER>
  • CLISP (clisp):
    CL-USER> (name-all-packages)
    ("CHARSET" "CLOS" "COMMON-LISP" "COMMON-LISP-USER" "CS-COMMON-LISP"
     "CS-COMMON-LISP-USER" "CUSTOM" "EXPORTING" "EXT" "FFI" "GRAY" "GSTREAM"
     "I18N" "KEYWORD" "MONITOR" "POSIX" "PXREF" "READLINE" "REGEXP" "SCREEN"
     "SOCKET" "SWANK" "SWANK-BACKEND" "SWANK-IO-PACKAGE" "SWANK-LOADER"
     "SWANK-MATCH" "SWANK-MOP" "SWANK-RPC" "SYSTEM")
     
    CL-USER>

As you can see, there’s a huge difference between the various Lisp implementations. And we haven’t even started talking about user-installed packages (e.g. installed via asdf)!

So, now we have a list of all packages. How about delving deeper? Let’s for example examine the external interface of CLISP’s REGEXP package:

CL-USER> (package-external-symbols "REGEXP")
(REGEXP:MATCH REGEXP:MATCH-END REGEXP:MATCH-START REGEXP:MATCH-STRING
 REGEXP:REGEXP-COMPILE REGEXP:REGEXP-EXEC REGEXP:REGEXP-MATCHER
 REGEXP:REGEXP-QUOTE REGEXP:REGEXP-SPLIT REGEXP:WITH-LOOP-SPLIT)
 
CL-USER>

Try to DESCRIBE one of those symbols, e.g. the function REGEXP:MATCH (output elided for better readability):

CL-USER> (describe 'regexp:match)
 
(...)
 
CLISP Documentation is at
For more information, evaluate (SYMBOL-PLIST 'REGEXP:MATCH).
 
(...)
 
CL-USER>

Learning more about a single package

Sometimes, you are not interested in the list of all symbols of a package as returned by PACKAGE-ALL-SYMBOLS. Neither are you interested in its external interface as shown by PACKAGE-EXTERNAL-SYMBOLS. All you want, is a succint description of a package, so you can get a quick idea about what’s in it.

Letting packages DESCRIBE themselves

The easiest way to get a simple description for a package, is to let the package describe itself, using the standard DESCRIBE function.

Since DESCRIBE requires a package object instead of a package name, and since it sends its descriptions to *standard-output* by default instead of returning a string, we wrap DESCRIBE into the following function PACKAGE-DESCRIBE-STRING:

[lisp];;; PACKAGE-DESCRIBE-STRING returns a non-portable description of a package

(defun package-describe-string (&optional (package "COMMON-LISP-USER"))
"Return a non-portable description for PACKAGE name, via DESCRIBE."
(with-output-to-string (sstream)
(describe (find-package package) sstream)))[/lisp]

Note how we transformed the package name into a package object with the standard function FIND-PACKAGE, before passing it to DESCRIBE.

Because DESCRIBE sends output to a stream, we need to create an output string stream that will collect that output into a string. Instead of creating such a stream manually with MAKE-STRING-OUTPUT-STREAM, extract the string with GET-OUTPUT-STREAM-STRING, close that stream with CLOSE… we use the standard macro WITH_OUTPUT-TO-STRING to do it all in one fell swoop.

So how does the output look like? It’s implementation-dependent, of course. On Clozure Common Lisp (ccl), we get:

CL-USER> (package-describe-string "COMMON-LISP")
"#<Package \"COMMON-LISP\">
Type: PACKAGE
Class: #<BUILT-IN-CLASS PACKAGE>
Internal Symbols: 0
External Symbols: 978
PKG.ITAB: (#(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ...) 0 . 36)
PKG.ETAB: (#(DEFSETF NUMBER 0 &REST ABORT SUBSTITUTE *PRINT-LENGTH* DEFVAR
             ECHO-STREAM-INPUT-STREAM PROGV *READ-DEFAULT-FLOAT-FORMAT* GETHASH
             STRUCTURE-CLASS UNBOUND-SLOT COMPILER-MACRO SUBSTITUTE-IF COERCE 
             STRING-TRIM BIT-ORC2 0 ...)
           978 . 1009)
PKG.USED: NIL
PKG.USED-BY: (#<Package \"SWANK\"> #<Package \"SWANK-RPC\">
              #<Package \"SWANK-MATCH\"> #<Package \"CROSS-REFERENCE\">
              #<Package \"MONITOR\"> #<Package \"SWANK-BACKEND\">
              #<Package \"SWANK-LOADER\"> #<Package \"COMMON-LISP-USER\">
              #<Package \"ANSI-LOOP\"> #<Package \"INSPECTOR\"> #<Package \"X8664\">
              #<Package \"ARCH\"> #<Package \"X86\"> #<Package \"X8632\">
              #<Package \"OPENMCL-SOCKET\"> #<Package \"X86-FREEBSD64\">
              #<Package \"CCL\">)
PKG.NAMES: (\"COMMON-LISP\" \"CL\")
PKG.SHADOWED: NIL
PKG.LOCK: #<READ-WRITE-LOCK [ptr @ #x8014D1180] #x30200000DC0D>
PKG.INTERN-HOOK: NIL
"
 
CL-USER>

Try it with other ANSI Common Lisp implementations.

Getting package docstrings isn’t portable

It should be noted that getting the documentation string (docstring) of a package is not portable, because ANSI Common Lisp doesn’t define a docstring for a package at all! However, some implementations are free to provide one, in a totally non-portable way.

Here is an example of a function that tries to extract the docstring of a package in an implementation dependent way. Feel free to experiment and to extend these functions:

[lisp];;; Because ANSI Common Lisp doesn’t define the docstring of a package,
;;; the following functions are implementation dependent.

(defun package-doc-string (package)
"Return the documentation string of PACKAGE name, when possible."
#+sbcl (sb-kernel:package-doc-string (find-package package))
#+cmucl (lisp::package-doc-string (find-package package))
#+clisp (system::package-documentation (find-package package))
#-(or sbcl cmucl clisp)
(progn
(warn "No docstring for ~A on this version of Lisp."
package)
nil)
)

(defun doc-all-packages ()
"Return a list of (packagename . docstring) for all packages."
(mapcar #'(lambda (name) (cons name (package-doc-string name)))
(name-all-packages)))[/lisp]

On ccl, this function is not implemented. We thus get:

CL-USER> (package-doc-string "COMMON-LISP-USER")
; Warning: No docstring for COMMON-LISP-USER on this version of Lisp.
; While executing: PACKAGE-DOC-STRING, in process repl-thread(9).
NIL
 
CL-USER> (doc-all-packages)
; Warning: No docstring for ANSI-LOOP on this version of Lisp.
; While executing: PACKAGE-DOC-STRING, in process repl-thread(9).
 
(... a lot more warnings elided ...)
 
 (("ANSI-LOOP") ("ARCH") ("CCL") ("COMMON-LISP") ("COMMON-LISP-USER")
  ("CROSS-REFERENCE") ("GRAY") ("INSPECTOR") ("KEYWORD") ("MONITOR")
  ("OPENMCL-MOP") ("OPENMCL-SOCKET") ("SETF") ("SWANK") ("SWANK-BACKEND")
  ("SWANK-IO-PACKAGE") ("SWANK-LOADER") ("SWANK-MATCH") ("SWANK-MOP")
  ("SWANK-RPC") ("X86") ("X86-FREEBSD64") ("X8632") ("X8664"))
 
CL-USER>

But on sbcl, we get more sensible output:

CL-USER> (package-doc-string "COMMON-LISP-USER")
"public: the default package for user code and data"
 
CL-USER> (doc-all-packages)
(("ASDF")
 ("COMMON-LISP"
  . "public: home of symbols defined by the ANSI language specification")
 ("COMMON-LISP-USER" . "public: the default package for user code and data")
 ("KEYWORD" . "public: home of keywords")
 ("SB-ALIEN" . "public: the ALIEN foreign function interface (If you're
porting CMU CL code, note that this package corresponds roughly to a union
of the packages ALIEN and C-CALL at the time of the SBCL fork. SB-C-CALL
is a deprecated nickname to help ease the transition from older versions
of SBCL which maintained the CMU-CL-style split into two packages.)")
 ("SB-ALIEN-INTERNALS" . "private: stuff for implementing ALIENs and friends")
 ("SB-ASSEM" . "private: the assembler, used by the compiler")
 ("SB-BIGNUM" . "private: bignum implementation")
 ("SB-BSD-SOCKETS" . "
 
A thinly-disguised BSD socket API for SBCL.  Ideas stolen from the BSD
socket API for C and Graham Barr's IO::Socket classes for Perl.
 
We represent sockets as CLOS objects, and rename a lot of methods and
arguments to fit Lisp style more closely.
 
")
 ("SB-BSD-SOCKETS-INTERNAL") ("SB-BSD-SOCKETS-SYSTEM")
 ("SB-C" . "private: implementation of the compiler") ("SB-CLTL2")
 ("SB-CLTL2-SYSTEM")
 ("SB-DEBUG"
  . "sorta public: Eventually this should become the debugger interface, with
basic stuff like BACKTRACE and ARG. For now, the actual supported interface
is still mixed indiscriminately with low-level internal implementation stuff
like *STACK-TOP-HINT* and unsupported stuff like *TRACED-FUN-LIST*.")
 ("SB-DI" . "private: primitives used to write debuggers")
 ("SB-DISASSEM"
  . "private: stuff related to the implementation of the disassembler")
 ("SB-EVAL"
  . "internal: the evaluator implementation used to execute code without compiling it.")
 ("SB-EXT"
  . "public: miscellaneous supported extensions to the ANSI Lisp spec")
 ("SB-FASL" . "private: stuff related to FASL load/dump logic (and GENESIS)")
 ("SB-FORMAT" . "private: implementation of FORMAT and friends")
 ("SB-GRAY" . "public: an implementation of the stream-definition-by-user
Lisp extension proposal by David N. Gray")
 ("SB-GROVEL") ("SB-GROVEL-SYSTEM")
 ("SB-IMPL" . "private: a grab bag of implementation details")
 ("SB-INT"
  . "private: miscellaneous unsupported extensions to the ANSI spec. Much of
the stuff in here originated in CMU CL's EXTENSIONS package and is retained,
possibly temporariliy, because it might be used internally.")
 ("SB-INTROSPECT") ("SB-INTROSPECT-SYSTEM")
 ("SB-KERNEL"
  . "private: Theoretically this 'hides state and types used for package
integration' (said CMU CL architecture.tex) and that probably was and
is a good idea, but see SB-SYS re. blurring of boundaries.")
 ("SB-LOOP" . "private: implementation details of LOOP")
 ("SB-MOP" . "public: the MetaObject Protocol interface, as defined by
The Art of the Metaobject Protocol, by Kiczales, des Rivieres and Bobrow:
ISBN 0-262-61074-4, with exceptions as noted in the User Manual.")
 ("SB-PCL" . "semi-public: This package includes useful meta-object protocol
extensions, but even they are not guaranteed to be present in later
versions of SBCL, and the other stuff in here is definitely not
guaranteed to be present in later versions of SBCL.  Use of this
package is deprecated in favour of SB-MOP.")
 ("SB-POSIX") ("SB-POSIX-SYSTEM")
 ("SB-PRETTY" . "private: implementation of pretty-printing")
 ("SB-PROFILE" . "public: the interface to the profiler")
 ("SB-SEQUENCE" . "semi-public: implements something which might eventually
be submitted as a CDR")
 ("SB-SYS" . "private: In theory, this \"contains functions and information
necessary for system interfacing\" (said cmu-user.tex at the time
of the SBCL code fork). That probably was and is a good idea, but in
practice, the distinctions between this package and SB-KERNEL
and even SB-VM seem to have become somewhat blurred over the years.
Some anomalies (e.g. FIND-IF-IN-CLOSURE being in SB-SYS instead of
SB-KERNEL) have been undone, but probably more remain.")
 ("SB-THREAD" . "public (but low-level): native thread support")
 ("SB-UNIX" . "private: a wrapper layer for SBCL itself to use when talking
with an underlying Unix-y operating system.
This was a public package in CMU CL, but that was different.
CMU CL's UNIX package tried to provide a comprehensive,
stable Unix interface suitable for the end user.
This package only tries to implement what happens to be
needed by the current implementation of SBCL, and makes
no guarantees of interface stability.")
 ("SB-VM"
  . "internal: the default place to hide information about the hardware and data
structure representations")
 ("SB-WALKER" . "internal: a code walker used by PCL") ("SWANK")
 ("SWANK-BACKEND") ("SWANK-IO-PACKAGE") ("SWANK-LOADER") ("SWANK-MATCH")
 ("SWANK-MOP") ("SWANK-RPC"))
 
CL-USER>

As you can see, this is totally implementation dependent.

The file package.lisp

All functions above are defined in the file package.lisp below. As usual, you can simply (LOAD "package.lisp") in your own programs or at the read-eval-print loop to use them. Enjoy.

[lisp];;;; package.lisp — print all symbols of a package

;;;; License: 2-clauses BSD
;;
;; Copyright (c) 2012 Farid Hajji. All rights reserved.
;;
;; Redistribution and use in source and binary forms, with or without
;; modification, are permitted provided that the following conditions
;; are met:
;; 1. Redistributions of source code must retain the above copyright
;; notice, this list of conditions and the following disclaimer.
;; 2. Redistributions in binary form must reproduce the above copyright
;; notice, this list of conditions and the following disclaimer in the
;; documentation and/or other materials provided with the distribution.
;;
;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS “AS IS” AND
;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
;; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
;; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
;; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
;; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
;; SUCH DAMAGE.
;;;;

;;; Poor man’s OBLIST using APROPOS-LIST

(defun poor-man-oblist (&optional (substring ""))
"Return a list of all symbols containing SUBSTRING as a substring."
(sort (apropos-list substring) #’string-lessp))

;;; This is a helper macro for the followig PACKAGE-*-SYMBOLS functions.

(defmacro loop-as-package (package symbol-type)
`(loop
for symname being each ,symbol-type of ,package
collect symname into reslist
finally
(return (setf reslist
(sort reslist #’string-lessp)))))

;;; PACKAGE-ALL-SYMBOLS returns a list of ALL symbols of a given package
;;; PACKAGE-PRESENT-SYMBOLS returns only a list of present symbols
;;; PACKAGE-EXTERNAL-SYMBOLS returns a list of exported symbols

(defun package-all-symbols (&optional (package "COMMON-LISP-USER"))
"Return a list of symbols in PACKAGE name."
(loop-as-package package symbol))

(defun package-present-symbols (&optional (package "COMMON-LISP-USER"))
"Return a list of present symbols in PACKAGE name."
(loop-as-package package present-symbol))

(defun package-external-symbols (&optional (package "COMMON-LISP-USER"))
"Return a list of external symbols in PACKAGE name."
(loop-as-package package external-symbol))

;;; NAME-ALL-PACKAGES returns a list of ALL available package (names).

(defun name-all-packages ()
"Return a list of all package names."
(let ((reslist (mapcar #’package-name
(list-all-packages))))
(sort reslist #’string-lessp)))

;;; PACKAGE-DESCRIBE-STRING returns a non-portable description of a package

(defun package-describe-string (&optional (package "COMMON-LISP-USER"))
"Return a non-portable description for PACKAGE name, via DESCRIBE."
(with-output-to-string (sstream)
(describe (find-package package) sstream)))

;;; Because ANSI Common Lisp doesn’t define the docstring of a package,
;;; the following functions are implementation dependent.

(defun package-doc-string (package)
"Return the documentation string of PACKAGE name, when possible."
#+sbcl (sb-kernel:package-doc-string (find-package package))
#+cmucl (lisp::package-doc-string (find-package package))
#+clisp (system::package-documentation (find-package package))
#-(or sbcl cmucl clisp)
(progn
(warn "No docstring for ~A on this version of Lisp."
package)
nil)
)

(defun doc-all-packages ()
"Return a list of (packagename . docstring) for all packages."
(mapcar #'(lambda (name) (cons name (package-doc-string name)))
(name-all-packages)))

;;; That’s all, folks![/lisp]

4 Comments

  1. foo

    All these ‘reslist’ variables and setting them is unnecessary. SORT returns the sorted list. If you don’t need or don’t use RESLIST, you can just omit the thing. (let ((reslist (mapcar #’package-name (list-all-packages)))) (sort reslist #’string-lessp))) is just (sort (mapcar #’package-name (list-all-packages)) #’string-lessp). Similar, the LOOP does not need a variable RESLIST.

     
  2. You’re absolutely right. I’ve kept RESLIST for readability reasons, but there’s nothing wrong with streamlining the code even more.

     
  3. option in a subsequent call to defpackage (for some other package ) expects to find these symbols accessible but not necessarily external.

     
  4. Allow the value of a pathname’s directory component to be a list. The car of the list is one of the symbols :ABSOLUTE or :RELATIVE. Each remaining element of the list is a string or a symbol (see below). Each string names a single level of directory structure. The strings should contain only the directory names themselves — no punctuation characters.