|[ < ]||[ > ]||[ << ]||[ Up ]||[ >> ]||[Top]||[Contents]||[Index]||[ ? ]|
The Common Lisp structure mechanism provides a general way
to define data types similar to C's
struct types. A
structure is a Lisp object containing some number of slots,
each of which can hold any Lisp data object. Functions are
provided for accessing and setting the slots, creating or copying
structure objects, and recognizing objects of a particular structure
In true Common Lisp, each structure type is a new type distinct from all existing Lisp types. Since the underlying Emacs Lisp system provides no way to create new distinct types, this package implements structures as vectors (or lists upon request) with a special "tag" symbol to identify them.
defstructform defines a new structure type called name, with the specified slots. (The slots may begin with a string which documents the structure type.) In the simplest case, name and each of the slots are symbols. For example,
(defstruct person name age sex)
defines a struct type called
person which contains three
slots. Given a
person object p, you can access those
slots by calling
(person-sex p). You can also change these slots by
setf on any of these place forms:
(incf (person-age birthday-boy))
You can create a new
person by calling
which takes keyword arguments
:sex to specify the initial values of these slots in the
new object. (Omitting any of these arguments leaves the corresponding
slot "undefined," according to the Common Lisp standard; in Emacs
Lisp, such uninitialized slots are filled with
(copy-person p) makes a new
object of the same type whose slots are
eq to those of p.
Given any Lisp object x,
(person-p x) returns
true if x looks like a
person, false otherwise. (Again,
in Common Lisp this predicate would be exact; in Emacs Lisp the
best it can do is verify that x is a vector of the correct
length which starts with the correct tag symbol.)
person-name normally check their arguments
person-p) and signal an error if the
argument is the wrong type. This check is affected by
(optimize (safety ...)) declarations. Safety level 1,
the default, uses a somewhat optimized check that will detect all
incorrect arguments, but may use an uninformative error message
(e.g., "expected a vector" instead of "expected a
Safety level 0 omits all checks except as provided by the underlying
aref call; safety levels 2 and 3 do rigorous checking that will
always print a descriptive error message for incorrect inputs.
See section 7. Declarations.
(setq dave (make-person :name "Dave" :sex 'male)) => [cl-struct-person "Dave" nil male] (setq other (copy-person dave)) => [cl-struct-person "Dave" nil male] (eq dave other) => nil (eq (person-name dave) (person-name other)) => t (person-p dave) => t (person-p [1 2 3 4]) => nil (person-p "Bogus") => nil (person-p '[cl-struct-person counterfeit person object]) => t
In general, name is either a name symbol or a list of a name symbol followed by any number of struct options; each slot is either a slot symbol or a list of the form `(slot-name default-value slot-options...)'. The default-value is a Lisp form which is evaluated any time an instance of the structure type is created without specifying that slot's value.
Common Lisp defines several slot options, but the only one
implemented in this package is
:read-only. A non-
value for this option means the slot should not be
the slot's value is determined when the object is created and does
not change afterward.
(defstruct person (name nil :read-only t) age (sex 'unknown))
Any slot options other than
:read-only are ignored.
For obscure historical reasons, structure options take a different form than slot options. A structure option is either a keyword symbol, or a list beginning with a keyword symbol possibly followed by arguments. (By contrast, slot options are key-value pairs not enclosed in lists.)
(defstruct (person (:constructor create-person) (:type list) :named) name age sex)
The following structure options are recognized.
(:conc-name p-)would change this prefix to
nilas an argument means no prefix, so that the slot names themselves are used to name the accessor functions.
make-person. The above example changes this to
nilas an argument means that no standard constructor should be generated at all.
In the full form of this option, the constructor name is followed
by an arbitrary argument list. See section 3. Program Structure, for a
description of the format of Common Lisp argument lists. All
options, such as
&key, are supported.
The argument names should match the slot names; each slot is
initialized from the corresponding argument. Slots whose names
do not appear in the argument list are initialized based on the
default-value in their slot descriptor. Also,
&key arguments which don't specify defaults take their
defaults from the slot descriptor. It is legal to include arguments
which don't correspond to slot names; these are useful if they are
referred to in the defaults for optional, keyword, or
arguments which do correspond to slots.
You can specify any number of full-format
options on a structure. The default constructor is still generated
as well unless you disable it with a simple-format
(defstruct (person (:constructor nil) ; no default constructor (:constructor new-person (name sex &optional (age 0))) (:constructor new-hound (&key (name "Rover") (dog-years 0) &aux (age (* 7 dog-years)) (sex 'canine)))) name age sex)
The first constructor here takes its arguments positionally rather
than by keyword. (In official Common Lisp terminology, constructors
that work By Order of Arguments instead of by keyword are called
"BOA constructors." No, I'm not making this up.) For example,
(new-person "Jane" 'female) generates a person whose slots
"Jane", 0, and
The second constructor takes two keyword arguments,
which initializes the
name slot and defaults to
:dog-years, which does not itself correspond to a slot
but which is used to initialize the
age slot. The
slot is forced to the symbol
canine with no syntax for
nilmeans not to generate a copier function. (In this implementation, all copier functions are simply synonyms for
nilmeans not to generate a predicate function. (If the
:typeoption is used without the
:namedoption, no predicate is ever generated.)
In true Common Lisp,
typep is always able to recognize a
structure object even if
:predicate was used. In this
typep simply looks for a function called
typename-p, so it will work for structure types
only if they used the default predicate name.
defstruct. The effect is to cause the new structure type to inherit all of the included structure's slots (plus, of course, any new slots described by this struct's slot descriptors). The new structure is considered a "specialization" of the included one. In fact, the predicate and slot accessors for the included type will also accept objects of the new type.
If there are extra arguments to the
:include option after
the included-structure name, these options are treated as replacement
slot descriptors for slots in the included structure, possibly with
modified default values. Borrowing an example from Steele:
(defstruct person name (age 0) sex) => person (defstruct (astronaut (:include person (age 45))) helmet-size (favorite-beverage 'tang)) => astronaut (setq joe (make-person :name "Joe")) => [cl-struct-person "Joe" 0 nil] (setq buzz (make-astronaut :name "Buzz")) => [cl-struct-astronaut "Buzz" 45 nil nil tang] (list (person-p joe) (person-p buzz)) => (t t) (list (astronaut-p joe) (astronaut-p buzz)) => (nil t) (person-name buzz) => "Buzz" (astronaut-name joe) => error: "astronaut-name accessing a non-astronaut"
astronaut is a specialization of
astronaut is also a
person (but not the
other way around). Every
astronaut includes all the slots
person, plus extra slots that are specific to
astronauts. Operations that work on people (like
work on astronauts just like other people.
list. This tells which underlying Lisp data type should be used to implement the new structure type. Vectors are used by default, but
(:type list)will cause structure objects to be stored as lists instead.
The vector representation for structure objects has the advantage that all structure slots can be accessed quickly, although creating vectors is a bit slower in Emacs Lisp. Lists are easier to create, but take a relatively long time accessing the later slots.
:typewithout also using
:namedwill result in a structure type stored as plain vectors or lists with no identifying features.
The default, if you don't specify
:type explicitly, is to
use named vectors. Therefore,
:named is only useful in
(defstruct (person1) name age sex) (defstruct (person2 (:type list) :named) name age sex) (defstruct (person3 (:type list)) name age sex) (setq p1 (make-person1)) => [cl-struct-person1 nil nil nil] (setq p2 (make-person2)) => (person2 nil nil nil) (setq p3 (make-person3)) => (nil nil nil) (person1-p p1) => t (person2-p p2) => t (person3-p p3) => error: function person3-p undefined
Since unnamed structures don't have tags,
defstruct is not
able to make a useful predicate for recognizing them. Also,
person3-name will be generated but they
will not be able to do any type checking. The
function, for example, will simply be a synonym for
this case. By contrast,
person2-name is able to verify
that its argument is indeed a
person2 object before
nilby the constructors and ignored otherwise. If the type
:includes another type, then
:initial-offsetspecifies a number of slots to be skipped between the last slot of the included type and the first new slot.
Except as noted, the
defstruct facility of this package is
entirely compatible with that of Common Lisp.
|[ << ]||[ >> ]||[Top]||[Contents]||[Index]||[ ? ]|