Here is a couple of hopefully portable ANSI Common Lisp functions that will help you explore available objects (symbols). In this post you’ll find:
OBLIST, a function that returns a list of all objects??,?f,?v,?t, and?s, functions that will fetch the whole description or documentation strings of a symbolSYMBOL->CELLS, a function that returns the cells of a symbol (name, value, function, plist, package, docstrings) as an alistOBLIST-WITH-CELLS, likeOBLIST, but returns a list of cells for each symbol- introspection.lisp, the source file with all those functions.
OBLIST
The problem: you’ve just started your favorite ANSI Common Lisp implementation and you’re at the REPL top level. Now you may be wondering what functions and variables, or more generally what symbols, are available to you. You may want to explore your Lisp, and you either don’t use an IDE, or your IDE doesn’t feature a symbol browser.
So, what do you need? Some old Lisp implementations provided a function OBLIST (short for OBject LIST) that returned a list of all objects, but there’s no OBLIST in ANSI Common Lisp.
Fortunately, all is not lost: we can roll our own OBLIST portably, using nothing more than pure ANSI Common 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))
OBLIST relies on the ANSI Common Lisp function DO-SYMBOLS to do its job.
At the REPL of your favorite Lisp, just call (oblist) to get a list of all symbols in the default “COMMON-LISP-USER” package. Alternatively, you can also ask for all symbols of a specific package by providing its name (as a string) to OBLIST, e.g. by calling (oblist "COMMON-LISP"). Remember to LOAD the file containing the definition of OBLIST beforehand:
CL-USER> (load "introspection.lisp") T CL-USER> (oblist "COMMON-LISP") (&ALLOW-OTHER-KEYS &AUX &BODY &ENVIRONMENT &KEY &OPTIONAL &REST &WHOLE * ** *** *BREAK-ON-SIGNALS* *COMPILE-FILE-PATHNAME* *COMPILE-FILE-TRUENAME* *COMPILE-PRINT* *COMPILE-VERBOSE* *DEBUG-IO* *DEBUGGER-HOOK* *DEFAULT-PATHNAME-DEFAULTS* *ERROR-OUTPUT* *FEATURES* *GENSYM-COUNTER* *LOAD-PATHNAME* *LOAD-PRINT* *LOAD-TRUENAME* *LOAD-VERBOSE* *MACROEXPAND-HOOK* *MODULES* *PACKAGE* *PRINT-ARRAY* *PRINT-BASE* *PRINT-CASE* *PRINT-CIRCLE* *PRINT-ESCAPE* *PRINT-GENSYM* *PRINT-LENGTH* *PRINT-LEVEL* *PRINT-LINES* *PRINT-MISER-WIDTH* *PRINT-PPRINT-DISPATCH* *PRINT-PRETTY* *PRINT-RADIX* *PRINT-READABLY* *PRINT-RIGHT-MARGIN* *QUERY-IO* *RANDOM-STATE* *READ-BASE* *READ-DEFAULT-FLOAT-FORMAT* *READ-EVAL* *READ-SUPPRESS* *READTABLE* *STANDARD-INPUT* *STANDARD-OUTPUT* *TERMINAL-IO* *TRACE-OUTPUT* + ++ +++ - / // /// /= 1+ 1- < <= = > >= ABORT ABS ACONS ACOS ACOSH ADD-METHOD ADJOIN ADJUST-ARRAY ADJUSTABLE-ARRAY-P ALLOCATE-INSTANCE ALPHA-CHAR-P ALPHANUMERICP AND APPEND APPLY APROPOS APROPOS-LIST AREF ARITHMETIC-ERROR ARITHMETIC-ERROR-OPERANDS ARITHMETIC-ERROR-OPERATION ARRAY ARRAY-DIMENSION ARRAY-DIMENSION-LIMIT ARRAY-DIMENSIONS ARRAY-DISPLACEMENT ARRAY-ELEMENT-TYPE ARRAY-HAS-FILL-POINTER-P ARRAY-IN-BOUNDS-P ARRAY-RANK ARRAY-RANK-LIMIT ARRAY-ROW-MAJOR-INDEX ARRAY-TOTAL-SIZE ARRAY-TOTAL-SIZE-LIMIT ARRAYP ASH ASIN ASINH ASSERT ASSOC ASSOC-IF ASSOC-IF-NOT ATAN ATANH ATOM BASE-CHAR BASE-STRING BIGNUM BIT BIT-AND BIT-ANDC1 BIT-ANDC2 BIT-EQV BIT-IOR BIT-NAND BIT-NOR BIT-NOT BIT-ORC1 BIT-ORC2 BIT-VECTOR BIT-VECTOR-P BIT-XOR BLOCK BOOLE BOOLE-1 BOOLE-2 BOOLE-AND BOOLE-ANDC1 BOOLE-ANDC2 BOOLE-C1 BOOLE-C2 BOOLE-CLR BOOLE-EQV BOOLE-IOR BOOLE-NAND BOOLE-NOR BOOLE-ORC1 BOOLE-ORC2 BOOLE-SET BOOLE-XOR BOOLEAN BOTH-CASE-P BOUNDP BREAK BROADCAST-STREAM BROADCAST-STREAM-STREAMS BUILT-IN-CLASS BUTLAST BYTE BYTE-POSITION BYTE-SIZE CAAAAR CAAADR CAAAR CAADAR CAADDR CAADR CAAR CADAAR CADADR CADAR CADDAR CADDDR CADDR CADR CALL-ARGUMENTS-LIMIT CALL-METHOD CALL-NEXT-METHOD CAR CASE CATCH CCASE CDAAAR CDAADR CDAAR CDADAR CDADDR CDADR CDAR CDDAAR CDDADR CDDAR CDDDAR CDDDDR CDDDR CDDR CDR CEILING CELL-ERROR CELL-ERROR-NAME CERROR CHANGE-CLASS CHAR CHAR-CODE CHAR-CODE-LIMIT CHAR-DOWNCASE CHAR-EQUAL CHAR-GREATERP CHAR-INT CHAR-LESSP CHAR-NAME CHAR-NOT-EQUAL CHAR-NOT-GREATERP CHAR-NOT-LESSP CHAR-UPCASE CHAR/= CHAR< CHAR<= CHAR= CHAR> CHAR>= CHARACTER CHARACTERP CHECK-TYPE CIS CLASS CLASS-NAME CLASS-OF CLEAR-INPUT CLEAR-OUTPUT CLOSE CLRHASH CODE-CHAR COERCE COMPILATION-SPEED COMPILE COMPILE-FILE COMPILE-FILE-PATHNAME COMPILED-FUNCTION COMPILED-FUNCTION-P COMPILER-MACRO COMPILER-MACRO-FUNCTION COMPLEMENT COMPLEX COMPLEXP COMPUTE-APPLICABLE-METHODS COMPUTE-RESTARTS CONCATENATE CONCATENATED-STREAM CONCATENATED-STREAM-STREAMS COND CONDITION CONJUGATE CONS CONSP CONSTANTLY CONSTANTP CONTINUE CONTROL-ERROR COPY-ALIST COPY-LIST COPY-PPRINT-DISPATCH COPY-READTABLE COPY-SEQ COPY-STRUCTURE COPY-SYMBOL COPY-TREE COS COSH COUNT COUNT-IF COUNT-IF-NOT CTYPECASE DEBUG DECF DECLAIM DECLARATION DECLARE DECODE-FLOAT DECODE-UNIVERSAL-TIME DEFCLASS DEFCONSTANT DEFGENERIC DEFINE-COMPILER-MACRO DEFINE-CONDITION DEFINE-METHOD-COMBINATION DEFINE-MODIFY-MACRO DEFINE-SETF-EXPANDER DEFINE-SYMBOL-MACRO DEFMACRO DEFMETHOD DEFPACKAGE DEFPARAMETER DEFSETF DEFSTRUCT DEFTYPE DEFUN DEFVAR DELETE DELETE-DUPLICATES DELETE-FILE DELETE-IF DELETE-IF-NOT DELETE-PACKAGE DENOMINATOR DEPOSIT-FIELD DESCRIBE DESCRIBE-OBJECT DESTRUCTURING-BIND DIGIT-CHAR DIGIT-CHAR-P DIRECTORY DIRECTORY-NAMESTRING DISASSEMBLE DIVISION-BY-ZERO DO DO* DO-ALL-SYMBOLS DO-EXTERNAL-SYMBOLS DO-SYMBOLS DOCUMENTATION DOLIST DOTIMES DOUBLE-FLOAT DOUBLE-FLOAT-EPSILON DOUBLE-FLOAT-NEGATIVE-EPSILON DPB DRIBBLE DYNAMIC-EXTENT ECASE ECHO-STREAM ECHO-STREAM-INPUT-STREAM ECHO-STREAM-OUTPUT-STREAM ED EIGHTH ELT ENCODE-UNIVERSAL-TIME END-OF-FILE ENDP ENOUGH-NAMESTRING ENSURE-DIRECTORIES-EXIST ENSURE-GENERIC-FUNCTION EQ EQL EQUAL EQUALP ERROR ETYPECASE EVAL EVAL-WHEN EVENP EVERY EXP EXPORT EXPT EXTENDED-CHAR FBOUNDP FCEILING FDEFINITION FFLOOR FIFTH FILE-AUTHOR FILE-ERROR FILE-ERROR-PATHNAME FILE-LENGTH FILE-NAMESTRING FILE-POSITION FILE-STREAM FILE-STRING-LENGTH FILE-WRITE-DATE FILL FILL-POINTER FIND FIND-ALL-SYMBOLS FIND-CLASS FIND-IF FIND-IF-NOT FIND-METHOD FIND-PACKAGE FIND-RESTART FIND-SYMBOL FINISH-OUTPUT FIRST FIXNUM FLET FLOAT FLOAT-DIGITS FLOAT-PRECISION FLOAT-RADIX FLOAT-SIGN FLOATING-POINT-INEXACT FLOATING-POINT-INVALID-OPERATION FLOATING-POINT-OVERFLOW FLOATING-POINT-UNDERFLOW FLOATP FLOOR FMAKUNBOUND FORCE-OUTPUT FORMAT FORMATTER FOURTH FRESH-LINE FROUND FTRUNCATE FTYPE FUNCALL FUNCTION FUNCTION-KEYWORDS FUNCTION-LAMBDA-EXPRESSION FUNCTIONP GCD GENERIC-FUNCTION GENSYM GENTEMP GET GET-DECODED-TIME GET-DISPATCH-MACRO-CHARACTER GET-INTERNAL-REAL-TIME GET-INTERNAL-RUN-TIME GET-MACRO-CHARACTER GET-OUTPUT-STREAM-STRING GET-PROPERTIES GET-SETF-EXPANSION GET-UNIVERSAL-TIME GETF GETHASH GO GRAPHIC-CHAR-P HANDLER-BIND HANDLER-CASE HASH-TABLE HASH-TABLE-COUNT HASH-TABLE-P HASH-TABLE-REHASH-SIZE HASH-TABLE-REHASH-THRESHOLD HASH-TABLE-SIZE HASH-TABLE-TEST HOST-NAMESTRING IDENTITY IF IGNORABLE IGNORE IGNORE-ERRORS IMAGPART IMPORT IN-PACKAGE INCF INITIALIZE-INSTANCE INLINE INPUT-STREAM-P INSPECT INTEGER INTEGER-DECODE-FLOAT INTEGER-LENGTH INTEGERP INTERACTIVE-STREAM-P INTERN INTERNAL-TIME-UNITS-PER-SECOND INTERSECTION INVALID-METHOD-ERROR INVOKE-DEBUGGER INVOKE-RESTART INVOKE-RESTART-INTERACTIVELY ISQRT KEYWORD KEYWORDP LABELS LAMBDA LAMBDA-LIST-KEYWORDS LAMBDA-PARAMETERS-LIMIT LAST LCM LDB LDB-TEST LDIFF LEAST-NEGATIVE-DOUBLE-FLOAT LEAST-NEGATIVE-LONG-FLOAT LEAST-NEGATIVE-NORMALIZED-DOUBLE-FLOAT LEAST-NEGATIVE-NORMALIZED-LONG-FLOAT LEAST-NEGATIVE-NORMALIZED-SHORT-FLOAT LEAST-NEGATIVE-NORMALIZED-SINGLE-FLOAT LEAST-NEGATIVE-SHORT-FLOAT LEAST-NEGATIVE-SINGLE-FLOAT LEAST-POSITIVE-DOUBLE-FLOAT LEAST-POSITIVE-LONG-FLOAT LEAST-POSITIVE-NORMALIZED-DOUBLE-FLOAT LEAST-POSITIVE-NORMALIZED-LONG-FLOAT LEAST-POSITIVE-NORMALIZED-SHORT-FLOAT LEAST-POSITIVE-NORMALIZED-SINGLE-FLOAT LEAST-POSITIVE-SHORT-FLOAT LEAST-POSITIVE-SINGLE-FLOAT LENGTH LET LET* LISP-IMPLEMENTATION-TYPE LISP-IMPLEMENTATION-VERSION LIST LIST* LIST-ALL-PACKAGES LIST-LENGTH LISTEN LISTP LOAD LOAD-LOGICAL-PATHNAME-TRANSLATIONS LOAD-TIME-VALUE LOCALLY LOG LOGAND LOGANDC1 LOGANDC2 LOGBITP LOGCOUNT LOGEQV LOGICAL-PATHNAME LOGICAL-PATHNAME-TRANSLATIONS LOGIOR LOGNAND LOGNOR LOGNOT LOGORC1 LOGORC2 LOGTEST LOGXOR LONG-FLOAT LONG-FLOAT-EPSILON LONG-FLOAT-NEGATIVE-EPSILON LONG-SITE-NAME LOOP LOOP-FINISH LOWER-CASE-P MACHINE-INSTANCE MACHINE-TYPE MACHINE-VERSION MACRO-FUNCTION MACROEXPAND MACROEXPAND-1 MACROLET MAKE-ARRAY MAKE-BROADCAST-STREAM MAKE-CONCATENATED-STREAM MAKE-CONDITION MAKE-DISPATCH-MACRO-CHARACTER MAKE-ECHO-STREAM MAKE-HASH-TABLE MAKE-INSTANCE MAKE-INSTANCES-OBSOLETE MAKE-LIST MAKE-LOAD-FORM MAKE-LOAD-FORM-SAVING-SLOTS MAKE-METHOD MAKE-PACKAGE MAKE-PATHNAME MAKE-RANDOM-STATE MAKE-SEQUENCE MAKE-STRING MAKE-STRING-INPUT-STREAM MAKE-STRING-OUTPUT-STREAM MAKE-SYMBOL MAKE-SYNONYM-STREAM MAKE-TWO-WAY-STREAM MAKUNBOUND MAP MAP-INTO MAPC MAPCAN MAPCAR MAPCON MAPHASH MAPL MAPLIST MASK-FIELD MAX MEMBER MEMBER-IF MEMBER-IF-NOT MERGE MERGE-PATHNAMES METHOD METHOD-COMBINATION METHOD-COMBINATION-ERROR METHOD-QUALIFIERS MIN MINUSP MISMATCH MOD MOST-NEGATIVE-DOUBLE-FLOAT MOST-NEGATIVE-FIXNUM MOST-NEGATIVE-LONG-FLOAT MOST-NEGATIVE-SHORT-FLOAT MOST-NEGATIVE-SINGLE-FLOAT MOST-POSITIVE-DOUBLE-FLOAT MOST-POSITIVE-FIXNUM MOST-POSITIVE-LONG-FLOAT MOST-POSITIVE-SHORT-FLOAT MOST-POSITIVE-SINGLE-FLOAT MUFFLE-WARNING MULTIPLE-VALUE-BIND MULTIPLE-VALUE-CALL MULTIPLE-VALUE-LIST MULTIPLE-VALUE-PROG1 MULTIPLE-VALUE-SETQ MULTIPLE-VALUES-LIMIT NAME-CHAR NAMESTRING NBUTLAST NCONC NEXT-METHOD-P NIL NINTERSECTION NINTH NO-APPLICABLE-METHOD NO-NEXT-METHOD NOT NOTANY NOTEVERY NOTINLINE NRECONC NREVERSE NSET-DIFFERENCE NSET-EXCLUSIVE-OR NSTRING-CAPITALIZE NSTRING-DOWNCASE NSTRING-UPCASE NSUBLIS NSUBST NSUBST-IF NSUBST-IF-NOT NSUBSTITUTE NSUBSTITUTE-IF NSUBSTITUTE-IF-NOT NTH NTH-VALUE NTHCDR NULL NUMBER NUMBERP NUMERATOR NUNION ODDP OPEN OPEN-STREAM-P OPTIMIZE OR OTHERWISE OUTPUT-STREAM-P PACKAGE PACKAGE-ERROR PACKAGE-ERROR-PACKAGE PACKAGE-NAME PACKAGE-NICKNAMES PACKAGE-SHADOWING-SYMBOLS PACKAGE-USE-LIST PACKAGE-USED-BY-LIST PACKAGEP PAIRLIS PARSE-ERROR PARSE-INTEGER PARSE-NAMESTRING PATHNAME PATHNAME-DEVICE PATHNAME-DIRECTORY PATHNAME-HOST PATHNAME-MATCH-P PATHNAME-NAME PATHNAME-TYPE PATHNAME-VERSION PATHNAMEP PEEK-CHAR PHASE PI PLUSP POP POSITION POSITION-IF POSITION-IF-NOT PPRINT PPRINT-DISPATCH PPRINT-EXIT-IF-LIST-EXHAUSTED PPRINT-FILL PPRINT-INDENT PPRINT-LINEAR PPRINT-LOGICAL-BLOCK PPRINT-NEWLINE PPRINT-POP PPRINT-TAB PPRINT-TABULAR PRIN1 PRIN1-TO-STRING PRINC PRINC-TO-STRING PRINT PRINT-NOT-READABLE PRINT-NOT-READABLE-OBJECT PRINT-OBJECT PRINT-UNREADABLE-OBJECT PROBE-FILE PROCLAIM PROG PROG* PROG1 PROG2 PROGN PROGRAM-ERROR PROGV PROVIDE PSETF PSETQ PUSH PUSHNEW QUOTE RANDOM RANDOM-STATE RANDOM-STATE-P RASSOC RASSOC-IF RASSOC-IF-NOT RATIO RATIONAL RATIONALIZE RATIONALP READ READ-BYTE READ-CHAR READ-CHAR-NO-HANG READ-DELIMITED-LIST READ-FROM-STRING READ-LINE READ-PRESERVING-WHITESPACE READ-SEQUENCE READER-ERROR READTABLE READTABLE-CASE READTABLEP REAL REALP REALPART REDUCE REINITIALIZE-INSTANCE REM REMF REMHASH REMOVE REMOVE-DUPLICATES REMOVE-IF REMOVE-IF-NOT REMOVE-METHOD REMPROP RENAME-FILE RENAME-PACKAGE REPLACE REQUIRE REST RESTART RESTART-BIND RESTART-CASE RESTART-NAME RETURN RETURN-FROM REVAPPEND REVERSE ROOM ROTATEF ROUND ROW-MAJOR-AREF RPLACA RPLACD SAFETY SATISFIES SBIT SCALE-FLOAT SCHAR SEARCH SECOND SEQUENCE SERIOUS-CONDITION SET SET-DIFFERENCE SET-DISPATCH-MACRO-CHARACTER SET-EXCLUSIVE-OR SET-MACRO-CHARACTER SET-PPRINT-DISPATCH SET-SYNTAX-FROM-CHAR SETF SETQ SEVENTH SHADOW SHADOWING-IMPORT SHARED-INITIALIZE SHIFTF SHORT-FLOAT SHORT-FLOAT-EPSILON SHORT-FLOAT-NEGATIVE-EPSILON SHORT-SITE-NAME SIGNAL SIGNED-BYTE SIGNUM SIMPLE-ARRAY SIMPLE-BASE-STRING SIMPLE-BIT-VECTOR SIMPLE-BIT-VECTOR-P SIMPLE-CONDITION SIMPLE-CONDITION-FORMAT-ARGUMENTS SIMPLE-CONDITION-FORMAT-CONTROL SIMPLE-ERROR SIMPLE-STRING SIMPLE-STRING-P SIMPLE-TYPE-ERROR SIMPLE-VECTOR SIMPLE-VECTOR-P SIMPLE-WARNING SIN SINGLE-FLOAT SINGLE-FLOAT-EPSILON SINGLE-FLOAT-NEGATIVE-EPSILON SINH SIXTH SLEEP SLOT-BOUNDP SLOT-EXISTS-P SLOT-MAKUNBOUND SLOT-MISSING SLOT-UNBOUND SLOT-VALUE SOFTWARE-TYPE SOFTWARE-VERSION SOME SORT SPACE SPECIAL SPECIAL-OPERATOR-P SPEED SQRT STABLE-SORT STANDARD STANDARD-CHAR STANDARD-CHAR-P STANDARD-CLASS STANDARD-GENERIC-FUNCTION STANDARD-METHOD STANDARD-OBJECT STEP STORAGE-CONDITION STORE-VALUE STREAM STREAM-ELEMENT-TYPE STREAM-ERROR STREAM-ERROR-STREAM STREAM-EXTERNAL-FORMAT STREAMP STRING STRING-CAPITALIZE STRING-DOWNCASE STRING-EQUAL STRING-GREATERP STRING-LEFT-TRIM STRING-LESSP STRING-NOT-EQUAL STRING-NOT-GREATERP STRING-NOT-LESSP STRING-RIGHT-TRIM STRING-STREAM STRING-TRIM STRING-UPCASE STRING/= STRING< STRING<= STRING= STRING> STRING>= STRINGP STRUCTURE STRUCTURE-CLASS STRUCTURE-OBJECT STYLE-WARNING SUBLIS SUBSEQ SUBSETP SUBST SUBST-IF SUBST-IF-NOT SUBSTITUTE SUBSTITUTE-IF SUBSTITUTE-IF-NOT SUBTYPEP SVREF SXHASH SYMBOL SYMBOL-FUNCTION SYMBOL-MACROLET SYMBOL-NAME SYMBOL-PACKAGE SYMBOL-PLIST SYMBOL-VALUE SYMBOLP SYNONYM-STREAM SYNONYM-STREAM-SYMBOL T TAGBODY TAILP TAN TANH TENTH TERPRI THE THIRD THROW TIME TRACE TRANSLATE-LOGICAL-PATHNAME TRANSLATE-PATHNAME TREE-EQUAL TRUENAME TRUNCATE TWO-WAY-STREAM TWO-WAY-STREAM-INPUT-STREAM TWO-WAY-STREAM-OUTPUT-STREAM TYPE TYPE-ERROR TYPE-ERROR-DATUM TYPE-ERROR-EXPECTED-TYPE TYPE-OF TYPECASE TYPEP UNBOUND-SLOT UNBOUND-SLOT-INSTANCE UNBOUND-VARIABLE UNDEFINED-FUNCTION UNEXPORT UNINTERN UNION UNLESS UNREAD-CHAR UNSIGNED-BYTE UNTRACE UNUSE-PACKAGE UNWIND-PROTECT UPDATE-INSTANCE-FOR-DIFFERENT-CLASS UPDATE-INSTANCE-FOR-REDEFINED-CLASS UPGRADED-ARRAY-ELEMENT-TYPE UPGRADED-COMPLEX-PART-TYPE UPPER-CASE-P USE-PACKAGE USE-VALUE USER-HOMEDIR-PATHNAME VALUES VALUES-LIST VARIABLE VECTOR VECTOR-POP VECTOR-PUSH VECTOR-PUSH-EXTEND VECTORP WARN WARNING WHEN WILD-PATHNAME-P WITH-ACCESSORS WITH-COMPILATION-UNIT WITH-CONDITION-RESTARTS WITH-HASH-TABLE-ITERATOR WITH-INPUT-FROM-STRING WITH-OPEN-FILE WITH-OPEN-STREAM WITH-OUTPUT-TO-STRING WITH-PACKAGE-ITERATOR WITH-SIMPLE-RESTART WITH-SLOTS WITH-STANDARD-IO-SYNTAX WRITE WRITE-BYTE WRITE-CHAR WRITE-LINE WRITE-SEQUENCE WRITE-STRING WRITE-TO-STRING Y-OR-N-P YES-OR-NO-P ZEROP) CL-USER>
OBLIST returns a list of all symbols. You can process this list programatically as you would any other list. For example, you could pretty-print it: (pprint (oblist)). We will process this list in the function OBLIST-WITH-CELLS below.
Learning more about symbols with the ?X functions
So now you have a list of all symbols with OBLIST. What’s the next logical step? Right! You pick a symbol, and let Lisp describe it further. To do this, we first write some wrappers around the ANSI Common Lisp functions DESCRIBE and DOCUMENTATION to save us some typing:
;;; The ?X functions retrieve some informations about specific symbols (defun ?? (some-symbol) "Verbosely describe a symbol using #'describe" (describe some-symbol)) (defun ?f (some-function) "Documentation string of a function" (documentation some-function 'function)) (defun ?v (some-variable) "Documentation string of a variable" (documentation some-variable 'variable)) (defun ?t (some-type) (documentation some-type 'type)) (defun ?s (some-structure) (documentation some-structure 'structure))
Okay, here they are. Now let’s try to learn more about an arbitrary symbol, say CAR:
CL-USER> (?? 'car)
COMMON-LISP:CAR
[symbol]
CAR names a compiled function:
Lambda-list: (LIST)
Declared type: (FUNCTION (LIST) (VALUES T &OPTIONAL))
Documentation:
Return the 1st object in a list.
Source file: SYS:SRC;CODE;LIST.LISP
(SETF CAR) names a compiled function:
Lambda-list: (G52694 G52695)
Derived type: (FUNCTION (T LIST) (VALUES T &OPTIONAL))
Inline proclamation: INLINE (inline expansion available)
Source file: SYS:SRC;CODE;SETF-FUNS.LISP
(SETF CAR) has setf-expansion: SB-KERNEL:%RPLACA
; No value
CL-USER>
What you see here is the output of ??, our DESCRIBE wrapper, applied on the symbol CAR. Remember to quote ??‘s arguments: you want to pass the symbol itself, not its value! This output has been written to the standard output stream.
While ?? provides a detailed human-readable description of a symbol, this description isn't easily machine-readable. To make matters worse, the output varies considerably across Common Lisp implementations. For example, the description of CAR above was from Steel Bank Common Lisp (sbcl). On it's ancestor CMU Common Lisp (cmucl), the description is:
CL-USER> (?? 'car)
CAR is an external symbol in the COMMON-LISP package.
Function: #<Function CAR {100B4DC9}>
Function arguments:
(list)
Function documentation:
Returns the 1st object in a list.
Its declared argument types are:
(LIST)
Its result type is:
T
On Thursday, 4/23/09 03:28:55 am [-2] it was compiled from:
target:code/list.lisp
Created: Tuesday, 5/10/05 07:58:06 pm [-2]
Comment: $Header: /project/cmucl/cvsroot/src/code/list.lisp,v 1.35 2005-05-10 17:58:06 rtoy Exp $
CL-USER>
As you can see, it's already different, even though sbcl and cmucl share most of the same code base.
Other ANSI Common Lisp implementations provide DESCRIBE as well, but, you may have expected it by now, format its output in an implementation dependend manner. For example, on Clozure Common Lisp (ccl), it looks differently:
CL-USER> (?? 'car) CAR Type: SYMBOL Class: #<BUILT-IN-CLASS SYMBOL> Function EXTERNAL in package: #<Package "COMMON-LISP"> Print name: "CAR" Value: #<Unbound> Function: #<Compiled-function CAR #x3000000F77CF> Arglist: (CCL::X) Plist: NIL ; No value CL-USER>
You see, it's terser. And under Armed Bear Common Lisp (abcl), an ANSI Common Lisp written in Java and running on a JavaVM, its output is terser still:
CL-USER> (?? 'car)
CAR is an external symbol in the COMMON-LISP package.
Its function binding is #<FUNCTION CAR {1AFDC98D}>.
The function's lambda list is:
(list)
The symbol's property list contains these indicator/value pairs:
JVM::P2-HANDLER JVM::P2-CAR
SYSTEM:FUNCTION-RESULT-TYPE T
SYSTEM:SETF-INVERSE SYSTEM:SET-CAR
; No value
CL-USER>
Under Embedded Common Lisp (ecl), a Lisp that compiles its functions by translating them into C and calling a C compiler behind the scenes, the output of DESCRIBE (and therefore ??) looks different again:
CL-USER> (?? 'car) CAR - external symbol in COMMON-LISP package ----------------------------------------------------------------------------- CAR [Function] Function in COMMON-LISP package: Args: (X) Returns the car of X if X is a cons. Returns NIL if X is NIL. ----------------------------------------------------------------------------- CAR [Setf] Defined as: (DEFSETF CAR . #<compiled-function 0000000000b81ec0>) See the doc of DEFSETF. ----------------------------------------------------------------------------- ; No value CL-USER>
And then there's CLISP (clisp), a popular Lisp that compiles functions to byte-code instead of native code, has yet again a different description for the same symbol:
CL-USER> (?? 'car)
CAR is the symbol CAR, lies in #<PACKAGE COMMON-LISP>, is accessible in 18
packages CLOS, COMMON-LISP, COMMON-LISP-USER, EXPORTING, EXT, FFI, MONITOR,
POSIX, PXREF, READLINE, REGEXP, SCREEN, SWANK, SWANK-BACKEND, SWANK-LOADER,
SWANK-MATCH, SWANK-RPC, SYSTEM, names a function, has 3 properties
SYSTEM::INSTRUCTION, SYSTEM::SETF-EXPANDER, SYSTEM::DOC
;; connecting to "http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/HyperSpec/Data/Map_Sym.txt"...connected...HTTP/1.1 404 Not Found
;; "Date: Fri, 17 Feb 2012 13:22:53 GMT"
;; "Server: Apache/2.2.16 (Debian)"
;; "Content-Length: 333"
;; "Connection: close"
;; "Content-Type: text/html; charset=iso-8859-1"
;; ""
;; "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">"
;; "<html><head>"
;; "<title>404 Not Found</title>"
;; "</head><body>"
;; "<h1>Not Found</h1>"
;; "<p>The requested URL /projects/iiip/doc/CommonLISP/HyperSpec/Data/Map_Sym.txt was not found on this server.</p>"
;; "<hr>"
;; "<address>Apache/2.2.16 (Debian) Server at www.ai.mit.edu Port 80</address>"
;; "</body></html>"
;; connecting to "http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/HyperSpec/Data/Symbol-Table.text"...connected...HTTP/1.1 200 OK...45,322 bytes
;; SYSTEM::GET-CLHS-MAP(#<IO INPUT-BUFFERED SOCKET-STREAM CHARACTER www.ai.mit.edu:80>)...978/978 symbols
.
ANSI-CL Documentation is at
"http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/HyperSpec/Body/acc_carcm_cdr_darcm_cddddr.html"
;; connecting to "http://clisp.cons.org/impnotes/id-href.map"...connected...HTTP/1.1 301 Moved Permanently --> "http://www.clisp.org/impnotes/id-href.map"
;; connecting to "http://www.clisp.org/impnotes/id-href.map"...connected...HTTP/1.1 200 OK...74,297 bytes
;; SYSTEM::GET-STRING-MAP(#<IO INPUT-BUFFERED SOCKET-STREAM CHARACTER www.clisp.org:80>)...2,004 IDs
;; SYSTEM::ENSURE-IMPNOTES-MAP(#P"/usr/local/lib/clisp/data/Symbol-Table.text")...
WARNING: SYSTEM::ENSURE-IMPNOTES-MAP: invalid id "compile-errors" for symbol
"EXT:SOURCE-PROGRAM-ERROR"
WARNING: SYSTEM::ENSURE-IMPNOTES-MAP: invalid id "compile-errors" for symbol
"EXT:SOURCE-PROGRAM-ERROR-DETAIL"
WARNING: SYSTEM::ENSURE-IMPNOTES-MAP: invalid id "compile-errors" for symbol
"EXT:SOURCE-PROGRAM-ERROR-FORM"
644 IDs
For more information, evaluate (SYMBOL-PLIST 'CAR).
#<PACKAGE COMMON-LISP> is the package named COMMON-LISP. It has 2 nicknames
LISP, CL.
It imports the external symbols of 1 package CLOS and exports 978 symbols to
17 packages SWANK, SWANK-RPC, SWANK-MATCH, MONITOR, PXREF, SWANK-BACKEND,
SWANK-LOADER, READLINE, REGEXP, POSIX, EXPORTING, FFI, SCREEN, CLOS,
COMMON-LISP-USER, EXT, SYSTEM.
#<SYSTEM-FUNCTION CAR> is a built-in system function.
Argument list: (#:ARG0)
For more information, evaluate (DISASSEMBLE #'CAR).
Documentation:
CLHS:
"Body/acc_carcm_cdr_darcm_cddddr.html"
SYSTEM::FILE:
((DEFSETF #P"/usr/local/ports/lang/clisp/work/clisp-2.49/src/places.fas" 523
523))
; No value
CL-USER>
I guess, you've got the point by now: every ANSI Common Lisp implementation provides a different output for ??, and this output isn't really machine readable: it can't be used further down in a Lisp program.
Sometimes, you don't need a complete description of a symbol as with ??. Many symbols, including variables and functions can contain a documentation string that can be fetched by the ANSI Common Lisp function DOCUMENTATION, or by our wrappers ?v (docstring of a variable) and ?f (docstring of a function).
Let's query the documentation string of the function CAR (we know by the description returned by ?? that CAR is a function):
CL-USER> (?f 'car) "Return the 1st object in a list." CL-USER>
As you can see, the object returned is a string... and strings can be machine-processed, e.g. stored somewhere. We'll come back to this shortly.
Let's query the docstring of a variable. What variables do we have? (oblist) returned a list of all objects, so we simply pick one object from the list (see above) that follows the naming convention for special variables (with dynamic binding). We choose *FEATURES*:
CL-USER> (?v '*FEATURES*) "a list of symbols that describe features provided by the implementation" CL-USER>
If you're curious, here's the value of *FEATURES* on sbcl:
CL-USER> *FEATURES* (:SWANK :SB-BSD-SOCKETS-ADDRINFO :ASDF2 :ASDF :ANSI-CL :COMMON-LISP :SBCL :SB-DOC :SB-TEST :SB-LDB :SB-PACKAGE-LOCKS :SB-UNICODE :SB-EVAL :SB-SOURCE-LOCATIONS :IEEE-FLOATING-POINT :X86-64 :UNIX :BSD :ELF :FREEBSD :GCC-TLS :GENCGC :STACK-GROWS-DOWNWARD-NOT-UPWARD :C-STACK-IS-CONTROL-STACK :LINKAGE-TABLE :COMPARE-AND-SWAP-VOPS :UNWIND-TO-FRAME-AND-CALL-VOP :RAW-INSTANCE-INIT-VOPS :STACK-ALLOCATABLE-CLOSURES :STACK-ALLOCATABLE-VECTORS :STACK-ALLOCATABLE-LISTS :STACK-ALLOCATABLE-FIXED-OBJECTS :ALIEN-CALLBACKS :CYCLE-COUNTER :COMPLEX-FLOAT-VOPS :FLOAT-EQL-VOPS :INLINE-CONSTANTS :MEMORY-BARRIER-VOPS :MULTIPLY-HIGH-VOPS :OS-PROVIDES-DLOPEN :OS-PROVIDES-DLADDR :OS-PROVIDES-PUTWC :OS-PROVIDES-BLKSIZE-T :OS-PROVIDES-SUSECONDS-T :OS-PROVIDES-GETPROTOBY-R :OS-PROVIDES-POLL) CL-USER>
What's in a symbol?
A symbol doesn't contain only its value, it contains a lot more! According to ANSI Common Lisp, a symbol contains at least the following attributes (cells):
- A name cell which contains the name of the symbol
- A package cell which contains the home package of the symbol
- A value cell which contains the value of a symbol (e.g. if the symbol is a variable, its value cell contains its value)
- A function cell which contains the function object bound to the symbol (in this case, the symbol can be called)
- A property list (plist) cell which contains the property list associated with that symbol (if any).
A symbol may contain many attributes at the same time: for example, FOO may contain a value and evaluating FOO will return that value; and FOO may define a function at the same time, and thus be called like a function. It may also contain a property list.
To explore the cells of a symbol, we define the following wrappers and functions that use only ANSI Common Lisp and the ?X functions defined above:
;;; The SYMBOL->XXX functions return the content of specific symbol cells
;;; (See HyperSpec, CLHS: System Class Symbol for the following functions)
(defconstant U-VALUE 'unbound "Represents an unbound value")
(defconstant U-PLIST 'unbound "Represents an unbound p-list")
(defconstant U-FUNCTION 'unbound "Represents an unbound function")
(defun symbol->name (symbol)
"Return the NAME of SYMBOL as a STRING"
(symbol-name symbol))
(defun symbol->package (symbol)
"Return the PACKAGE of SYMBOL as a STRING"
(package-name (symbol-package symbol)))
(defun symbol->plist (symbol)
"Return the PLIST of SYMBOL as a PLIST, or U-PLIST if unbound"
(let ((plist (symbol-plist symbol)))
(if plist
plist
U-PLIST)))
(defun symbol->value (symbol)
"Return the VALUE of SYMBOL. If unbound, return U-VALUE"
(if (boundp symbol)
(symbol-value symbol)
U-VALUE))
(defun symbol->function (symbol)
"Return the representation of a function of SYMBOL, or U-FUNCTION"
(if (fboundp symbol)
(symbol-function symbol)
U-FUNCTION))
(defun symbol->cells (symbol)
"Create an alist representing SYMBOL's cells"
(let ((alist ()))
;; optional documentation strings
(when (fboundp symbol) (push (cons 'function-doc (?f symbol)) alist))
(when (boundp symbol) (push (cons 'value-doc (?v symbol)) alist))
;; the following are cells of a symbol
(push (cons 'function (symbol->function symbol)) alist)
(push (cons 'plist (symbol->plist symbol)) alist)
(push (cons 'value (symbol->value symbol)) alist)
(push (cons 'package (symbol->package symbol)) alist)
(push (cons 'name (symbol->name symbol)) alist)
;; return value
alist))
The most important function here is, of course, SYMBOL->CELLS, which returns a (machine-readable) association list of a symbol's cells. Let's try it out, first on CAR. Calling SYMBOL->CELLS on sbcl yields:
CL-USER> (symbol->cells 'car) ((NAME . "CAR") (PACKAGE . "COMMON-LISP") (VALUE . UNBOUND) (PLIST . UNBOUND) (FUNCTION . #<FUNCTION CAR>) (FUNCTION-DOC . "Return the 1st object in a list.")) CL-USER>
What do we see here?
- We get a list of conses, i.e. an association list.
- Each cons of the list represents a particular cell of the symbol
CAR:- The
NAMEof the symbolCARis the string"CAR"(you wouldn't have expected that, would've you?) CARis part of the package whose name is the string"COMMON-LISP"(sure, it's indeed aCOMMON-LISPsymbol).CARcontains no value. Try to evaluate it by enteringcarat the REPL, you'll get an unbound exception.CARhas no associated property list (PLIST) in sbcl. Keep this in mind, when we trySYMBOL->CELLSon other Lisps.CARhas aFUNCTIONcell containing the real function object that performs the operation defined byCAR.
- The
- Since the function cell contains a function,
SYMBOL->CELLShas appended aFUNCTION-DOCcons to the association list, containing the documentation string of this function.
As I've just said, different ANSI Common Lisp implementations store different values in the cells of a symbol. Let's examine CAR again, under other Lisps. Under cmucl, we get:
CL-USER> (symbol->cells 'car)
((NAME . "CAR") (PACKAGE . "COMMON-LISP") (VALUE . UNBOUND) (PLIST . UNBOUND)
(FUNCTION . #<Function CAR {100B4DC9}>)
(FUNCTION-DOC . "Returns the 1st object in a list."))
CL-USER>
Since sbcl and cmucl share a big common code base, it should come as no surprise that the content of CAR's cells look very similar. The only difference was the external representation of the function object, but that's nothing worth talking about.
Now for ccl. Clozure Common Lisp returns the following alist:
CL-USER> (symbol->cells 'car) ((NAME . "CAR") (PACKAGE . "COMMON-LISP") (VALUE . UNBOUND) (PLIST . UNBOUND) (FUNCTION . #<Compiled-function CAR #x3000000F77CF>) (FUNCTION-DOC)) CL-USER>
Again, the external representation of the function object is different, but that was to be expected. More interesting is that FUNCTION-DOC doesn't show up as a cons in dotted list notation as previously, but as a list: (FUNCTION-DOC). The reason is obvious, if you think about it: since CAR is FBOUNDP to a function, SYMBOL->CELLS has appended CAR's docstring to the association list. But since ccl doesn't define a docstring for CAR, the associated string was simply NIL:
CL-USER> (?f 'car) NIL CL-USER>
Now on to ecl:
CL-USER> (symbol->cells 'car) ((NAME . "CAR") (PACKAGE . "COMMON-LISP") (VALUE . UNBOUND) (PLIST . UNBOUND) (FUNCTION . #<compiled-function CAR>) (FUNCTION-DOC . "Function in COMMON-LISP package: Args: (X) Returns the car of X if X is a cons. Returns NIL if X is NIL. ")) CL-USER>
Again, nothing new here. Notice that the docstring spans multiple lines here, but it is still one single string.
Armed Bear CL (abcl) yields:
CL-USER> (symbol->cells 'car)
((NAME . "CAR") (PACKAGE . "COMMON-LISP") (VALUE . UNBOUND)
(PLIST JVM::P2-HANDLER JVM::P2-CAR SYSTEM:FUNCTION-RESULT-TYPE T
SYSTEM:SETF-INVERSE SYSTEM:SET-CAR)
(FUNCTION . #<FUNCTION CAR {399F5D89}>) (FUNCTION-DOC))
CL-USER>
This is the first time we've met an ANSI Common Lisp implementation that puts something in the PLIST cell of CAR:
CL-USER> (assoc 'plist (symbol->cells 'car)) (PLIST JVM::P2-HANDLER JVM::P2-CAR SYSTEM:FUNCTION-RESULT-TYPE T SYSTEM:SETF-INVERSE SYSTEM:SET-CAR) CL-USER>
Lisp implementations can, and often do, associate properties to symbols by adding them to a propery list. This propery list is then saved in the PLIST cell of the symbol. A property list is simply a list of key and value pairs, stored one after the other in a list: (key1 value1 key2 value2 ...).
How about clisp? Let's try it!
CL-USER> (symbol->cells 'car)
((NAME . "CAR") (PACKAGE . "COMMON-LISP") (VALUE . UNBOUND)
(PLIST SYSTEM::INSTRUCTION 91 SYSTEM::SETF-EXPANDER SYSTEM::%RPLACA
SYSTEM::DOC
(SYSTEM::FILE
((DEFSETF #P"/usr/local/ports/lang/clisp/work/clisp-2.49/src/places.fas" 523
523))))
(FUNCTION . #<SYSTEM-FUNCTION CAR>) (FUNCTION-DOC))
CL-USER>
Here too, clisp has associated properties to the symbol CAR.
Now that we've examined an object representing a function, let's turn our attention to an object acting as a variable. Remember *FEATURES* from above? Here are its cells as an alist as returned by clisp:
CL-USER> (symbol->cells '*FEATURES*) ((NAME . "*FEATURES*") (PACKAGE . "COMMON-LISP") (VALUE :SWANK :READLINE :REGEXP :SYSCALLS :I18N :LOOP :COMPILER :CLOS :MOP :CLISP :ANSI-CL :COMMON-LISP :LISP=CL :INTERPRETER :SOCKETS :GENERIC-STREAMS :LOGICAL-PATHNAMES :SCREEN :FFI :GETTEXT :UNICODE :BASE-CHAR=CHARACTER :WORD-SIZE=64 :PC386 :UNIX) (PLIST . UNBOUND) (FUNCTION . UNBOUND) (VALUE-DOC)) CL-USER>
The only difference here is that now, the cell VALUE contains the value of *FEATURES*, and the cell FUNCTION shows UNBOUND, i.e. *FEATURES* doesn't contain a function and thus can't be called.
A little real introspection
For the sake of fun, let's introspect the object SYMBOL->CELLS we've just defined in different Lisps. Try to interpret the results yourself:
This is sbcl's cell representation:
CL-USER> (symbol->cells 'symbol->cells) ((NAME . "SYMBOL->CELLS") (PACKAGE . "COMMON-LISP-USER") (VALUE . UNBOUND) (PLIST . UNBOUND) (FUNCTION . #<FUNCTION SYMBOL->CELLS>) (FUNCTION-DOC . "Create an alist representing SYMBOL's cells")) CL-USER>
Have you noticed that PACKAGE is now COMMON-LISP-USER instead of merely COMMON-LISP as in the previous examples? The reason is that SYMBOL->CELLS is a user-defined function here and not part of Common Lisp itself. All user-defined symbols go into the COMMON-LISP-USER package by default (unless we switch packages, of course).
And this is cmucl's take on it:
CL-USER> (symbol->cells 'symbol->cells)
((NAME . "SYMBOL->CELLS") (PACKAGE . "COMMON-LISP-USER") (VALUE . UNBOUND)
(PLIST . UNBOUND)
(FUNCTION . #<Interpreted Function SYMBOL->CELLS {48029179}>)
(FUNCTION-DOC . "Create an alist representing SYMBOL's cells"))
CL-USER>
What about ccl?
CL-USER> (symbol->cells 'symbol->cells) ((NAME . "SYMBOL->CELLS") (PACKAGE . "COMMON-LISP-USER") (VALUE . UNBOUND) (PLIST . UNBOUND) (FUNCTION . #<Compiled-function SYMBOL->CELLS #x3020009322CF>) (FUNCTION-DOC . "Create an alist representing SYMBOL's cells")) CL-USER>
Embedded Common Lisp ecl returns:
CL-USER> (symbol->cells 'symbol->cells) ((NAME . "SYMBOL->CELLS") (PACKAGE . "COMMON-LISP-USER") (VALUE . UNBOUND) (PLIST . UNBOUND) (FUNCTION . #<bytecompiled-closure #<bytecompiled-function SYMBOL->CELLS>>) (FUNCTION-DOC . "Create an alist representing SYMBOL's cells")) CL-USER>
Armed Bear CL abcl:
CL-USER> (symbol->cells 'symbol->cells)
((NAME . "SYMBOL->CELLS") (PACKAGE . "COMMON-LISP-USER") (VALUE . UNBOUND)
(PLIST SYSTEM::%SOURCE
(#P"/users/farid/Devel/CommonLisp/introspection.lisp" . 2041))
(FUNCTION . #<FUNCTION SYMBOL->CELLS {170AEB17}>) (FUNCTION-DOC))
CL-USER>
Notice how abcl stores the path of the file containing SYMBOL->CELLS's source code in the property list under the implementation-dependent property with the key SYSTEM::%SOURCE. This is, of course, an implementation-specific feature that can't be relied upon in pure ANSI Common Lisp.
For clisp, I'll show the whole session, for a reason that will become apparent soon:
CL-USER> (load "introspection.lisp")
;; Loading file introspection.lisp ...
;; Loaded file introspection.lisp
T
CL-USER> (symbol->cells 'symbol->cells)
((NAME . "SYMBOL->CELLS") (PACKAGE . "COMMON-LISP-USER") (VALUE . UNBOUND)
(PLIST SYSTEM::DEFINITION
((DEFUN SYMBOL->CELLS (SYMBOL) "Create an alist representing SYMBOL's cells"
(LET ((ALIST NIL))
(WHEN (FBOUNDP SYMBOL) (PUSH (CONS 'FUNCTION-DOC (?F SYMBOL)) ALIST))
(WHEN (BOUNDP SYMBOL) (PUSH (CONS 'VALUE-DOC (?V SYMBOL)) ALIST))
(PUSH (CONS 'FUNCTION (SYMBOL->FUNCTION SYMBOL)) ALIST)
(PUSH (CONS 'PLIST (SYMBOL->PLIST SYMBOL)) ALIST)
(PUSH (CONS 'VALUE (SYMBOL->VALUE SYMBOL)) ALIST)
(PUSH (CONS 'PACKAGE (SYMBOL->PACKAGE SYMBOL)) ALIST)
(PUSH (CONS 'NAME (SYMBOL->NAME SYMBOL)) ALIST) ALIST))
. #(NIL NIL NIL NIL ((DECLARATION OPTIMIZE DECLARATION))))
SYSTEM::DOC
(SYSTEM::FILE
((SYSTEM::DEFUN/DEFMACRO
#P"/users/farid/Devel/CommonLisp/introspection.lisp" 68 83))))
(FUNCTION .
#<FUNCTION SYMBOL->CELLS (SYMBOL)
"Create an alist representing SYMBOL's cells"
(DECLARE (SYSTEM::IN-DEFUN SYMBOL->CELLS))
(BLOCK SYMBOL->CELLS
(LET ((ALIST NIL))
(WHEN (FBOUNDP SYMBOL) (PUSH (CONS 'FUNCTION-DOC (?F SYMBOL)) ALIST))
(WHEN (BOUNDP SYMBOL) (PUSH (CONS 'VALUE-DOC (?V SYMBOL)) ALIST))
(PUSH (CONS 'FUNCTION (SYMBOL->FUNCTION SYMBOL)) ALIST)
(PUSH (CONS 'PLIST (SYMBOL->PLIST SYMBOL)) ALIST)
(PUSH (CONS 'VALUE (SYMBOL->VALUE SYMBOL)) ALIST)
(PUSH (CONS 'PACKAGE (SYMBOL->PACKAGE SYMBOL)) ALIST)
(PUSH (CONS 'NAME (SYMBOL->NAME SYMBOL)) ALIST) ALIST))>)
(FUNCTION-DOC . "Create an alist representing SYMBOL's cells"))
CL-USER>
Isn't that interesting? CLISP stores not only the source code for the function in the property list under the key SYSTEM::DEFINITION, it also stores the path of the source file as a list under the key SYSTEM::DOC. Furthermore, the FUNCTION cell contains the function object, itself as a source code representation that is nearly identical to our own source code.
The reason for this is that SYMBOL->CELLS is not yet compiled: it is stored internally as source code, i.e. as a regular lisp list. That's why you see its source code in the FUNCTION cell above. Calling this function would mean running it through the interpreter. This can and will be slower than if it were a compiled function.
In other words, CLISP doesn't auto-compile newly defined functions, unlike what other major ANSI Common Lisp implementations do by default.
Of course, nothing prevents us from compiling SYMBOL->CELLS using COMPILE:
CL-USER> (compile 'symbol->cells) SYMBOL->CELLS NIL NIL CL-USER>
Now, the cells of SYMBOL->CELLS look a little differently:
CL-USER> (symbol->cells 'symbol->cells)
((NAME . "SYMBOL->CELLS") (PACKAGE . "COMMON-LISP-USER") (VALUE . UNBOUND)
(PLIST SYSTEM::DEFINITION
((DEFUN SYMBOL->CELLS (SYMBOL) "Create an alist representing SYMBOL's cells"
(LET ((ALIST NIL))
(WHEN (FBOUNDP SYMBOL) (PUSH (CONS 'FUNCTION-DOC (?F SYMBOL)) ALIST))
(WHEN (BOUNDP SYMBOL) (PUSH (CONS 'VALUE-DOC (?V SYMBOL)) ALIST))
(PUSH (CONS 'FUNCTION (SYMBOL->FUNCTION SYMBOL)) ALIST)
(PUSH (CONS 'PLIST (SYMBOL->PLIST SYMBOL)) ALIST)
(PUSH (CONS 'VALUE (SYMBOL->VALUE SYMBOL)) ALIST)
(PUSH (CONS 'PACKAGE (SYMBOL->PACKAGE SYMBOL)) ALIST)
(PUSH (CONS 'NAME (SYMBOL->NAME SYMBOL)) ALIST) ALIST))
. #(NIL NIL NIL NIL ((DECLARATION OPTIMIZE DECLARATION))))
SYSTEM::DOC
(SYSTEM::FILE
((SYSTEM::DEFUN/DEFMACRO
#P"/users/farid/Devel/CommonLisp/introspection.lisp" 68 83))))
(FUNCTION . #<COMPILED-FUNCTION SYMBOL->CELLS>)
(FUNCTION-DOC . "Create an alist representing SYMBOL's cells"))
CL-USER>
If you've paid attention, you may have noticed that the FUNCTION cell now contains a #<COMPILED-FUNCTION SYMBOL->CELLS>, i.e. a compiled function object.
Instead of manually compiling a function with COMPILE, we could also have compiled the whole file introspection.lisp into a FASL file (fast loading lisp file) with COMPILE-FILE-PATHNAME and loaded that FASL file instead:
CL-USER> (compile-file-pathname "introspection.lisp"
:output-file "introspection.fas")
#P"introspection.fas"
CL-USER> (load "introspection.fas")
;; Loading file introspection.fas ...
;; Loaded file introspection.fas
T
CL-USER>
Now, with that file loaded, let's query the cells again:
CL-USER> (symbol->cells 'symbol->cells)
((NAME . "SYMBOL->CELLS") (PACKAGE . "COMMON-LISP-USER") (VALUE . UNBOUND)
(PLIST SYSTEM::DOC
(SYSTEM::FILE
((SYSTEM::DEFUN/DEFMACRO #P"/users/farid/Devel/CommonLisp/introspection.fas"
68 83))))
(FUNCTION . #<COMPILED-FUNCTION SYMBOL->CELLS>)
(FUNCTION-DOC . "Create an alist representing SYMBOL's cells"))
CL-USER>
Again, the FUNCTION cell contains the compiled function, as expected. But now, the source code for the function isn't stored in the property list PLIST anymore. Instead, there's a pointer to the FASL file containing its definition.
OBLIST-WITH-CELLS
The last function of introspection.lisp is OBLIST-WITH-CELLS, which returns a list of all cells corresponding to a list of objects or to all objects:
;;; OBLIST-WITH-CELLS is an OBLIST that returns a list of alists (cells) (defun oblist-with-cells (&optional (symbol-list (oblist))) "Apply SYMBOL->CELLS on all SYMBOLs in SYMBOL-LIST" (mapcar #'symbol->cells symbol-list))
You use this function like this:
CL-USER> (oblist-with-cells (list '?v '?f '*FEATURES*)) (((NAME . "?V") (PACKAGE . "COMMON-LISP-USER") (VALUE . UNBOUND) (PLIST . UNBOUND) (FUNCTION . #) (FUNCTION-DOC . "Documentation string of a variable")) ((NAME . "?F") (PACKAGE . "COMMON-LISP-USER") (VALUE . UNBOUND) (PLIST . UNBOUND) (FUNCTION . #) (FUNCTION-DOC . "Documentation string of a function")) ((NAME . "*FEATURES*") (PACKAGE . "COMMON-LISP") (VALUE :SWANK :SB-BSD-SOCKETS-ADDRINFO :ASDF2 :ASDF :ANSI-CL :COMMON-LISP :SBCL :SB-DOC :SB-TEST :SB-LDB :SB-PACKAGE-LOCKS :SB-UNICODE :SB-EVAL :SB-SOURCE-LOCATIONS :IEEE-FLOATING-POINT :X86-64 :UNIX :BSD :ELF :FREEBSD :GCC-TLS :GENCGC :STACK-GROWS-DOWNWARD-NOT-UPWARD :C-STACK-IS-CONTROL-STACK :LINKAGE-TABLE :COMPARE-AND-SWAP-VOPS :UNWIND-TO-FRAME-AND-CALL-VOP :RAW-INSTANCE-INIT-VOPS :STACK-ALLOCATABLE-CLOSURES :STACK-ALLOCATABLE-VECTORS :STACK-ALLOCATABLE-LISTS :STACK-ALLOCATABLE-FIXED-OBJECTS :ALIEN-CALLBACKS :CYCLE-COUNTER :COMPLEX-FLOAT-VOPS :FLOAT-EQL-VOPS :INLINE-CONSTANTS :MEMORY-BARRIER-VOPS :MULTIPLY-HIGH-VOPS :OS-PROVIDES-DLOPEN :OS-PROVIDES-DLADDR :OS-PROVIDES-PUTWC :OS-PROVIDES-BLKSIZE-T :OS-PROVIDES-SUSECONDS-T :OS-PROVIDES-GETPROTOBY-R :OS-PROVIDES-POLL) (PLIST . UNBOUND) (FUNCTION . UNBOUND) (VALUE-DOC . "a list of symbols that describe features provided by the implementation"))) CL-USER>
Or, if you prefer, you can get a
CL-USER (oblist-with-cells) (... a very long list omitted ...) CL-USER>
If the list doesn't look pretty, you can format it with the pretty printer PPRINT. Here's an example using ccl:
CL-USER> (oblist-with-cells (list '?v '?f)) (((NAME . "?V") (PACKAGE . "COMMON-LISP-USER") (VALUE . UNBOUND) (PLIST . UNBOU\ ND) (FUNCTION . #<Compiled-function ?V #x30200080D8DF>) (FUNCTION-DOC . "Docume\ ntation string of a variable")) ((NAME . "?F") (PACKAGE . "COMMON-LISP-USER") (\ VALUE . UNBOUND) (PLIST . UNBOUND) (FUNCTION . #<Compiled-function ?F #x3020008\ 0DC3F>) (FUNCTION-DOC . "Documentation string of a function"))) CL-USER> (pprint (oblist-with-cells (list '?v '?f))) (((NAME . "?V") (PACKAGE . "COMMON-LISP-USER") (VALUE . UNBOUND) (PLIST . UNBOUND) (FUNCTION . #<Compiled-function ?V #x30200080D8DF>) (FUNCTION-DOC . "Documentation string of a variable")) ((NAME . "?F") (PACKAGE . "COMMON-LISP-USER") (VALUE . UNBOUND) (PLIST . UNBOUND) (FUNCTION . #<Compiled-function ?F #x30200080DC3F>) (FUNCTION-DOC . "Documentation string of a function"))); No value CL-USER>
Further reading
You may want to read about the ANSI Common Lisp function INSPECT, and check out the introspections of the SLIME ide for further ideas. Of course, you can also look at the source code of major ANSI Common Lisp implementations' introspection functions like DESCRIBE and friends to see how they get at the internals of a symbol.
The file introspection.lisp
All functions above are defined in the file introspection.lisp below. You can use these functions by calling (LOAD "introspection.lisp") in your own programs or at the read-eval-print loop (REPL).
;;;; introspection.lisp -- functions to inspect ANSI Common Lisp environment
;;;; 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.
;;;;
;;; 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))
;;; The ?X functions retrieve some informations about specific symbols
(defun ?? (some-symbol)
"Verbosely describe a symbol using #'describe"
(describe some-symbol))
(defun ?f (some-function)
"Documentation string of a function"
(documentation some-function 'function))
(defun ?v (some-variable)
"Documentation string of a variable"
(documentation some-variable 'variable))
(defun ?t (some-type)
(documentation some-type 'type))
(defun ?s (some-structure)
(documentation some-structure 'structure))
;;; The SYMBOL->XXX functions return the content of specific symbol cells
;;; (See HyperSpec, CLHS: System Class Symbol for the following functions)
(defconstant U-VALUE 'unbound "Represents an unbound value")
(defconstant U-PLIST 'unbound "Represents an unbound p-list")
(defconstant U-FUNCTION 'unbound "Represents an unbound function")
(defun symbol->name (symbol)
"Return the NAME of SYMBOL as a STRING"
(symbol-name symbol))
(defun symbol->package (symbol)
"Return the PACKAGE of SYMBOL as a STRING"
(package-name (symbol-package symbol)))
(defun symbol->plist (symbol)
"Return the PLIST of SYMBOL as a PLIST, or U-PLIST if unbound"
(let ((plist (symbol-plist symbol)))
(if plist
plist
U-PLIST)))
(defun symbol->value (symbol)
"Return the VALUE of SYMBOL. If unbound, return U-VALUE"
(if (boundp symbol)
(symbol-value symbol)
U-VALUE))
(defun symbol->function (symbol)
"Return the representation of a function of SYMBOL, or U-FUNCTION"
(if (fboundp symbol)
(symbol-function symbol)
U-FUNCTION))
(defun symbol->cells (symbol)
"Create an alist representing SYMBOL's cells"
(let ((alist ()))
;; optional documentation strings
(when (fboundp symbol) (push (cons 'function-doc (?f symbol)) alist))
(when (boundp symbol) (push (cons 'value-doc (?v symbol)) alist))
;; the following are cells of a symbol
(push (cons 'function (symbol->function symbol)) alist)
(push (cons 'plist (symbol->plist symbol)) alist)
(push (cons 'value (symbol->value symbol)) alist)
(push (cons 'package (symbol->package symbol)) alist)
(push (cons 'name (symbol->name symbol)) alist)
;; return value
alist))
;;; OBLIST-WITH-CELLS is an OBLIST that returns a list of alists (cells)
(defun oblist-with-cells (&optional (symbol-list (oblist)))
"Apply SYMBOL->CELLS on all SYMBOLs in SYMBOL-LIST"
(mapcar #'symbol->cells symbol-list))
;;; That's all, folks!
Hi,
quite OT, are there still plans for a new release of the Perl Book?
Hello,
thank you for asking.
There won’t be any new editions of the Perl Book anytime soon. My motivation to update it (or the Python Book) is at an all times low.
Sorry about that.
Hi Farid,
I understand that writing books can be a quite ungrateful business, regarding both commercial success and personal satisfaction. On the other hand the books you’ve written are really cool, and while the Python book is still available, the Perl book has been out of print for years now. Note that I don’t think you should add a lot of new stuff, just updating everything to current Perl version and libraries for a reprint. Maybe you could find a co-author doing the necessary changes. I’m wondering, am I the only one pleading for a reprint?
Slightly ot, your Python book was registered at novelrank.com (absolutely free service, which tracks amazon sales ranks and calculates sales numbers from differences of sales ranks) and seems to be selling somewhat ok, see http://www.novelrank.com/asin/3827325439 . One probably can’t expect a programming book to become a bestseller.
Though our personal backgrounds may be very different, I’d like to assure you, that this society and the neolib system is dragging down everybody, not just you. A lot of people just try to ignore all the contradictions and discrepancies, some take drugs, some try to raise themselves by despising ‘weaker’ people etc. Personally I’m thinking, that the system will need to go completely bankrupt, before things will start improving again. The whole situation is annoying, because even if you believe in the so-called markets, you’d have to state, that in a real “Free Market(tm)” there would be no such thing as a bailout, ESM, EFSF, etc, at least not without fair compensation for the tax payer. Even the Quantitative Easing of the FED is more responsible than the EU bailouts, no matter what experts say. It’s sad to say, the FED policy of printing money to buy US bonds directly will prevail over the ECB printing money for the banksters in the weak hope that they will buy Euro bonds instead of doing more financial mischief, the whole european austerity strategy will fail. For some reasons the EU elites always want to have one more level of indirection or middle men aka rip-off artists (cf. Ein-Euro-Jobs etc). Another important issue that needs to be mentioned in that context would be closing down all that tax evasion stuff like Double Irish, Dutch Sandwich, Cayman Islands etc, finally establishing some kind of Tobin Tax, but it might need a few more really big crashes until the elites will become reasonable again.
Ok, enough negative stuff for now, in one or another way life will go on.
kind regards, ernst
Hi, it’s me again, sorry for my last post beeing quite offtopic.
I’m currently trying to refresh my Haskell knowledge, using Thompsons “Haskell craft of functional programming”. I had a medium-level understanding of Haskell when studying, and a minimum knowledge of Lisp, but have to admit, that I forgot most of that stuff. My motivation for refreshing Haskell is a Haskell program, that calculates some boolean formulas in a symbolic way, but at some point switches to concrete values, using the same functions, but switching the way, how functions are evaluated by Haskell. I’d like to use that functions for a similar task in my own program, but it is still too sophisticated for me (at the moment).
It’s understood that Haskell and Lisp are quite different in most aspects, though both need “thinking in lists” and similar concepts. I’m planning on refreshing my Lisp knowledge in maybe a year or so, and I’d like to choose a book suited for self-study and work with it, what do you think of a book like “Land of Lisp”, available both in English and German?
kind regards, ernst