The Revised Maclisp ManualThe PitmanualPage A-10
Published by HyperMeta Inc.
Prev | Index | Next
[Blue Marble]
Climate Change
Can hydrogen-powered vehicles really help us in time?

Symbol Manipulation


A symbol is a primitive Lisp data structure which has a pname (print name), value cell, property list, args information.

Symbols also serve the role of identifiers in Lisp code.

Symbols are created automatically by READ when their names are typed. Those symbols are “interned” on an obarray so that later references to the same name will produce the same object. There are other ways to create and intern symbols as well, and it is also possible to create symbols which are not interned on any obarray and hence cannot be confused with any symbol returned by READ.

Symbols' names can be made up of any alphabetic characters, though special characters must be quoted with a slash or matched vertical bars when typing them to READ. For example, X and H2O are normal symbol names but to have Lisp interpreted 17 as a symbol instead of a number, you would have to type /17 or |17|. For more information about slash and vertical bar, see the Syntax chapter.

Normal users should try not to be concerned with memory usage because Lisp will usually take care of managing that fine on its own. For those users with very large systems that tend to run out of storage, however, here is some information about the costs of creating symbols: A symbol whose name is 5 characters or less takes up 5 words of memory if it is not interned and not bound. Add two words of memory for each 5 characters of additional print name (or fraction thereof), one word of memory for each obarray the symbol is on (usually exactly one), and one word of memory for a value cell if the symbol is or ever has been bound. So gensyms take 5 words and special variables take 7 or more words.

A symbol may sometimes be seen (elsewhere in this manual and in some Lisp error messages) to be referred to as an “atomic symbol.” That term is actually redundant; all symbols are atomic.


Predicate returns T if q is a symbol, and NIL otherwise.


(symbolp 'howdy)	=> T
(symbolp 'another-sym)	=> T
(symbolp nil)		=> T   ;NIL is defined to be a symbol
(symbolp 3)		=> NIL ;numbers aren't symbols
(symbolp '(a b))	=> NIL ;lists aren't symbols
(symbolp tyo)		=> NIL ;files aren't symbols


The symbol NIL is permanently bound to itself. It is an error to use SET or SETQ to try to change its value. This symbol represents the empty list, (), whose car and cdr are both NIL. Both "NIL" and "()" are fully interchangeable input representations for the same object. NIL is also the only object treated by Lisp to mean false in conditionals (e.g., COND, OR, AND). Although it is really a symbol, the functions ATOM, SYMBOLP, and LISTP all return T when given NIL as an argument.

See also: T, NOT, COND, AND, OR


(ATOM 'NIL)	=>	T
(EQ 'NIL '())	=>	T
(NULL 'NIL)	=>	T
(NOT 'NIL)	=>	T


The symbol T is permanently bound to itself. Although anything in Lisp which is not NIL is considered true by conditionals (e.g., IF, COND, OR, AND), T is guaranteed to always evaluate to a true, and is the preferred true value to be returned by predicates which have nothing better to return. (Some predicates, like MEMBER and ASSOC, return more useful truth values.) T is also the canonical expression for introducing the `else clause' of a COND clause.

See also: NIL, NOT, COND, AND, OR

Symbol Bindings

The functions on this page are intended for the manipulate global symbols as data structures. They are not intended to manipulate symbols which are being used as local program variables.

SETFunction(SET sym val)

SET is like SETQ except that the first argument is evaluated; also SET only takes one pair of arguments. The first argument must evaluate to a symbol whose value is changed to the value of the second argument. SET returns the value of its second argument.


Like EVAL but works only on symbols -- considerably more efficient than just calling EVAL. Note that the optional second argument of an “a-list” pointer is not allowed.

Note: Like EVAL, care must be used in calling SYMEVAL from compiled code in order to achieve the correct effects. Appropriate SPECIAL declarations to the compiler may be necessary in some cases. Also, as with EVAL, if you find writing a call to SYMEVAL inside a FEXPR definition, you probably want a MACRO definition instead. However, since SYMEVAL is a highly constrained use of EVAL, it is not discouraged from use where it seems appropriate.

BOUNDPFunction(BOUNDP sym)

The argument to boundp must be a symbol. If it has a value, T is returned. Otherwise NIL is returned.


Causes a symbol to become unbound. Its value is removed and it becomes undefined, which is the initial state for symbols. Returns its argument.

MAKUNBOUNDStyle NoteVariables vs Symbols

Symbols play a dual role in Lisp. A symbol can be a data object with a value cell (manipulable by SET, SYMEVAL, BOUNDP, and MAKUNBOUND) or a program object (read by using its name or assigned by SETQ). While novices may find great amusement in the ability of programs to modify themselves as data, such programs do not compile well. As such, SET, SYMEVAL, BOUNDP, and MAKUNBOUND should never be thought of as something to be done to the local variables of a program; they are operations to be done on toplevel symbols. Expressions such as

(LET ((X ...)) ... (MAKUNBOUND 'X) ...) ;bad style

are strongly discouraged since they may compile into code that works nothing like the way the interpreter treats them.

;; Assign some variables...
(setq a 3 b 'a c 'b)
=> B

;; Compute various interesting expressions...
(list 'a a
      'b b (symeval 'b) (syemval b)
      'c c (symeval 'c) (symeval c))
=> (A 3 B A A 3 C B B A)

(set (symeval 'c) 4)
=> 4

(list a b c)
=> (3 4 B)

(set (symeval 'b) 5)

;; A different example; this time we'll use a gensym...
(setq var (gensym))
=> G0259

;; Gensyms, like other newly-created symbols, are initially unbound.
(cond ((boundp var) (list var (symeval var)))
      (t (list var)))
=> (G0259)

(set var 4)
=> 4

(cond ((boundp var) (list var (symeval var)))
      (t (list var)))
=> (G0259 4)

;; Now kill that gensym's value and show it's unbound again...
(progn (makunbound var) (boundp var))
=> NIL

Property Lists

Property ListConceptLookup Table

Symbols have an associated entity called a property list. This is a table of indicators and values. For example, if the value associated with the BAR indicator on the plist of some symbol FOO is BAZ, then FOO is said to have a BAR property of BAZ.

Symbols' property lists can be a convenient storage mechanism for many kinds of global data, but as with any kind of global data, it is always possible that programs can accidentally alter data they don't realize is being used by other programs, so some care is advised in using them.

Most programs should not have to know the format of a property list since PUTPROP, REMPROP, and GET make it relatively invisible. Some system programs which call PLIST or SETPLIST, however, may need to know that a property list is a list with the form:

(ind1 val1 ind2 val2 ...)

There are also local property lists, called “disembodied property lists.” These are lists whose car is ignored and whose cdr is the plist. The typical way to create a disembodied property list is to say (NCONS NIL). Never use a quoted list '(NIL) since you will be side-effecting its structure and it is best to have a cons you can be sure will not be shared with anyone else; it is the job of NCONS (and CONS, ...) to get ahold of such a fresh cons. Disembodied property lists are objects which can be passed around conveniently as data. Gensyms also make good disembodied property lists, but they take up more room than disembodied property lists. (About 5 words for a gensym vs 1 word for a disembodied property list).

Here's a sample program using disembodied property lists:

;;; (CLASSIFY list-of-entries)
;;;  Given a list of the form ((type data) (type data) ...)
;;;  Returns a list for the form:
;;;    ((type1 data1-1 data1-2 ...) (type2 data2-1 ...) ...)

  (LET ((PL (NCONS NIL))) ;disembodied property list
        ((NULL L))
      (PUSH (CADR (CAR L)) (GET PL (CAR (CAR L)))))
    (DO ((L (PLIST PL) (CDDR L))
         (RESULT NIL (CONS (CONS (CAR L) (NREVERSE (CADR L))) ;destroys PL
        ((NULL L) RESULT))))
(classify nil)
=> NIL

(classify '((a b) (b foo) (a 3) (a 7) (c 4) (b bar)))
=> ((A B 3 7) (B FOO BAR) (C 4))

The property list functions GET, PUTPROP, REMPROP, GETL, PLIST and SETPLIST work both on symbols and on disembodied property lists.

PUTPROPFunction(PUTPROP sym val indicator)

Gives sym a property named indicator of val, returning val. The sym argument may be a symbol or a disembodied property list. After doing (PUTPROP x y z), (GET x z) will return y.

GETFunction(GET sym indicator)

The sym argument is expected to be a symbol or a disembodied property list. The indicator is the name of a property to be looked up. This is usually a symbol, but is not restricted to be -- an EQ test is done to find the indicator. The value returned is the filler of the slot named indicator.

A return value of NIL means that either the filler of that slot was NIL or that there was no indicator slot. Usually this ambiguity is desirable. In the few cases where it is not, use GETL.

To see a list of pre-defined property list indicators, see the Property Index.

GETLFunction(GETL sym indlist)

Like GET except that indlist is a list of indicators rather than just a single indicator. GETL searches sym's property list until a property in indlist is found. The portion of sym's property list beginning with this property is returned. The car of this is the property name and the cadr is what get would have returned. GETL returns NIL if none of the properties in indlist appear on the property list of sym. The sym may be a symbol or a disembodied property list.

To see a list of pre-defined property list indicators, see the Property Index.


  (DO ((Q (PLIST X) (CDDR Q))) ; scan down PLIST of sym
      ((OR (NULL Q) (MEMQ (CAR Q) INDS)) Q)))

REMPROPFunction(REMPROP sym indicator)

Removes sym's indicator property, by splicing it out of sym's property list. The value is NIL if the indicated property did not exist; otherwise, the value is not NIL. The sym may be a symbol or a disembodied property list.

PLISTFunction(PLIST sym)

Returns the property-list of a symbol or a disembodied property list.

To see a list of pre-defined property list indicators, see the Property Index.


Sets the property list of sym to be l. Unless the user is sure he knows what he is doing, l should be a list.

This function is not really recommended because it blindly clobbers all things on the property list. Probably you want PUTPROP. SETPLIST is provided basically as a debugging tool and has only a few other “legitimate” applications.

(progn (putprop 'red t 'adjective)
       (putprop 'red "a primary color" 'meaning))
=> "a primary color"	

(plist 'red)
=> (MEANING "a primary color" ADJECTIVE T)

(progn 	(putprop 'red "an angry color" 'meaning)
        (putprop 'red nil 'adjective))
=> NIL

(get 'red 'adjective)
=> NIL

(getl 'red '(adjective))

(plist 'red)
=> (MEANING "an angry color" ADJECTIVE NIL)

(remprop 'red 'adjective)
=> (NIL)

(plist 'red)
=> (MEANING "an angry color")

(get 'red 'adjective)
=> NIL

(getl 'red '(adjective))
=> NIL

(setplist 'red '(position-in-traffic-light top))

(get 'red 'position-in-traffic-light)
=> TOP

(get 'red 'meaning)
=> NIL

Note: When looking for functional definitions, Lisp will always prefer the first definition (the one nearest the head of the propety list) if more than one is found. Hence, when new functions are defined, they should be placed on the property list ahead of any old definitions. DEFUN and DEFPROP will handle this correctly by doing a REMPROP of the old property before calling PUTPROP to assure that the new property is at the head of the plist. Users of PUTPROP, however, will probably want to do an explicit REMPROP to achieve the correct behavior.

Print Names

PNGETFunction(PNGET symbol n)

[PDP-10 Only] Gets pname (print name) of symbol. n specifies the form of the pname. If n is 7, then 7bit (ASCII) form is used (no consing is done; the actual pname is returned). If n is 6, then the printname is translated to 6bit and returned as a list. Pnames are lists of fixnums, each representing 5 (if ASCII format) or 6 (if 6bit format) characters. In ASCII format, the extra bit is the low bit, which is always 0.

See also: PNPUT

PNPUTFunction(PNPUT l internflag)

[PDP-10 Only] Returns a symbol whose pname is l, which must be in 7bit (ASCII) format. The second argument, internflag, says whether the resulting symbol is to be interned.

See also: PNGET

;; This demo sequence will be in octal
(setq ibase 8. base 8.)
=> 10

(pnget '|.LISP.| 6)
=> (165451636016)		

;; Note that the "GHI" part does not fill the word
;; so is left justified in the word with 0's padding.
(pnget 'ABCDEFGHI 6)
=> (-363534333232 -302727_22)

(pnget 'SOME-SYMBOL 7)
=> (-261406235246 -261146236542 -320_33)

(pnput * t)

=> T

;; In the 7bit case, the actual internal pname structure is
;; returned for efficiency. In the 6bit case, a fresh result is
;; consed each time.
(eq (pnget 'foo 7) (pnget 'foo 7))
=> T

(eq (pnget 'foo 6) (pnget 'foo 6))
=> NIL

Symbols and Strings

Note: The Lisp READ function (except on Multics) does uppercasing of characters in symbols except when they are quoted with slash, doublequotes, or vertical bars. Don't let that confuse you as you try the examples given for the functions on this page. Actually, because of the lack of case translation effect, some of these examples will work differently on Multics.


The arguments to SAMEPNAMEP must be symbols or strings (Multics). It returns true if they have the same pname, or false otherwise. The pname of a character string is considered to be the string itself.


(samepnamep 'xyz (maknam '(x y z)))	=> T
(samepnamep 'xyz (maknam '(w x y)))	=> NIL
(samepnamep 'xyz 'xyz)			=> T
(samepnamep 'x "X")			=> T
(samepnamep 'X "x")			=> NIL


Where q1 and q2 are symbols (or strings) returns T if the pname of q1 occurs earlier in alphabetical order than the pname of q2. The pname of a character string is considered to be the string itself.


(ALPHALESSP 'X  '/x)	=> T ;uppercase before lowercase
(ALPHALESSP "x" 'Y)	=> NIL ;ditto
(ALPHALESSP "x" 'y)	=> NIL ;ditto, since READ upcased Y

The Obarray and Interning


The value of OBARRAY is an array which is a table of known symbols. When a symbol is read in it is interned on this obarray (i.e., made EQ to any atomic symbols with the same pname that were previously read in). If a symbol, such as one created by (GENSYM), is not in this table an atom read in with the same pname will not be EQ. There will be two separate copies. The obarray may be manipulated by the functions REMOB and INTERN. A new obarray can be created using the function *ARRAY (preferred) or ARRAY. The symbol OBARRAY may be SETQ'd or lambda-bound to different obarrays at different times, which allows multiple sets of symbols to be kept separate -- you can have different symbols with the same pname interned on different obarrays at the same time. Note that the value of OBARRAY is not a symbol which names an array, but an array object itself.

Elements of an obarray are lists of symbols which are the fillers of that “bucket” in the obarray. The hash function for determining which symbol belongs in which bucket is (\ (SXHASH sym) 511.).

The array property of the symbol OBARRAY is the same array as its initial value. It is recommended that the value of this property not be changed since it is frequently invaluable to have this backup pointer available while debugging.

;; User-defined INTERNEDP predicate...

MAPATOMSFunction(MAPATOMS fn [obarray])

Like MAPC except maps across an obarray. Default is the value of the special variable OBARRAY. Returns NIL.

INTERNFunction(INTERN sym)

Searches for a symbol, x, on the current obarray (see OBARRAY) which has the same pname as sym (i.e., for which (SAMEPNAMEP sym x) is true). If such a symbol is found, it is returned. If no such symbol is found, then x is added to the obarray and returned. Hence, INTERN may side-effect OBARRAY, but does not have to.

As a special case, a symbol with a +INTERNAL-STRING-MARKER property of T will never be placed on an obarray by intern. If a `fake-string' (a symbol with such a property) is given to INTERN and a symbol with its name is not on the obarray already, a copy of the symbol given to INTERN is placed on the obarray and that copied symbol is returned. This keeps Maclisp's `fake-strings' from being accidentally placed on the obarray by INTERN.

REMOBFunction(REMOB sym)

The argument to remob must be a symbol. It is removed from the current obarray if it is interned on that obarray. This makes the symbol inaccessible to any S-expressions that may be read in or loaded in the future. REMOB returns no useful value.

OBARRAYArrayInitial Obarray

When Lisp loads up, the value of the symbol OBARRAY and its ARRAY property contain the same value. INTERN and friends look only at the value of the symbol; if you ever find your Lisp has an OBARRAY selected which you don't like, you can usually at least revert to the initial one by typing:


Creating Symbols

SYMBOLCONCFunction(SYMBOLCONC x1 x2 x3 ...)

Returns an interned symbol whose print name is the concatenation of the objects in x1, x2, x3, etc., If an x is a symbol, its pname is used in the concatenation; if an x is a fixnum, its base 10 representation (without a decimal point) is used; and if x is a list, it is assumed to be a list of characters.

Multics users must (%include runtime) to get SYMBOLCONC.


(symbolconc 'a 3)			=> A3
(symbolconc 'foo 'bar 'baz)		=> FOOBARBAZ
(symbolconc (exploden '(a b)) 'c)	=> |(A B)C|


Maknam takes as its argument a list of characters, like readlist, and returns an uninterned atom whose pname is determined by the list of characters.

To get an interned symbol, you probably want IMPLODE.


(maknam '(a b #o60 d #/E))
=> AB0DE

(eq * 'AB0DE)
=> NIL

COPYSYMBOLFunction(COPYSYMBOL symbol flag)

Creates a new symbol with the same pname as the symbol given. The new symbol is not interned. flag says whether the new symbol is to also inherit a copy of the old symbol's property list (actually copies each of the toplevel cons's in the property list so that PUTPROP'ing to one or the other symbol will not cause side-effecting between them). T means to give the new symbol this copied property list, NIL means the new symbol should have a null property list initially.


GENSYMFunction(GENSYM data)

Creates and returns a new symbol, which is not interned on the obarray (is not recognized by read.) The symbol's pname is of the form <prefix><number>; e.g., G0001. The number is incremented each time. The number is in decimal and always four digits (after “9999” becomes “0000”) and the prefix is always one character.

The data argument may be either a symbol, the first character of which will be used as the resulting symbol's prefix, or a positive integer, which will be used as the number part of the resulting symbol's pname. The information given by the data argument is sticky. If given a character, subsequent calls to GENSYM will continue to use that character. If given a number, subsequent calls to GENSYM will continue counting from that number.


(setq a (gensym))
=> G0123

;; Every call to GENSYM produces a fresh symbol.
=> G0124

;; Even though the symbol looks like an interned symbol,
;; it's not the same one.
(eq a 'g0123)
=> NIL

(equal a 'g0123)
=> NIL

;; Usually, you call GENSYM just to get a symbol which won't be
;; confused with symbols the user can type in. In the rare case
;; where you might need to compare printed representations, though,
;; you can use SAMEPNAMEP.
(samepnamep a 'g0123)
=> T

(gensym 17.)
=> G0017

(eq (gensym 17.) (gensym 17.))
=> NIL

(list (gensym 'f) (gensym))
=> (F0018 F0019)

[Blue Marble]
Climate Change
By what date must we act?

The Revised Maclisp Manual (Sunday Morning Edition)
Published Sunday, December 16, 2007 06:17am EST, and updated Sunday, July 6, 2008.
Prev | Index | Next