cl: Structures

 
 11 Structures
 *************
 
 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 type.
 
    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.
 
  -- Macro: cl-defstruct name slots...
      The ‘cl-defstruct’ form 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,
 
           (cl-defstruct person name age sex)
 
      defines a struct type called ‘person’ that contains three slots.
      Given a ‘person’ object P, you can access those slots by calling
      ‘(person-name P)’, ‘(person-age P)’, and ‘(person-sex P)’.  You can
      also change these slots by using ‘setf’ on any of these place
      forms, for example:
 
           (cl-incf (person-age birthday-boy))
 
      You can create a new ‘person’ by calling ‘make-person’, which takes
      keyword arguments ‘:name’, ‘:age’, and ‘: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 ‘nil’.)
 
      Given a ‘person’, ‘(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’, and 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 that starts with
      the correct tag symbol.)
 
      Accessors like ‘person-name’ normally check their arguments
      (effectively using ‘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 ‘person’”).  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.  SeeDeclarations.
 
           (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 that 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-‘nil’ value for
      this option means the slot should not be ‘setf’-able; the slot’s
      value is determined when the object is created and does not change
      afterward.
 
           (cl-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.)
 
           (cl-defstruct (person (:constructor create-person)
                                 (:type list)
                                 :named)
                name age sex)
 
      The following structure options are recognized.
 
      ‘:conc-name’
           The argument is a symbol whose print name is used as the
           prefix for the names of slot accessor functions.  The default
           is the name of the struct type followed by a hyphen.  The
           option ‘(:conc-name p-)’ would change this prefix to ‘p-’.
           Specifying ‘nil’ as an argument means no prefix, so that the
           slot names themselves are used to name the accessor functions.
 
      ‘:constructor’
           In the simple case, this option takes one argument which is an
           alternate name to use for the constructor function.  The
           default is ‘make-NAME’, e.g., ‘make-person’.  The above
           example changes this to ‘create-person’.  Specifying ‘nil’ as
           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.  SeeProgram
           Structure, for a description of the format of Common Lisp
           argument lists.  All options, such as ‘&rest’ and ‘&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, ‘&optional’ and ‘&key’ arguments that don’t
           specify defaults take their defaults from the slot descriptor.
           It is valid to include arguments that don’t correspond to slot
           names; these are useful if they are referred to in the
           defaults for optional, keyword, or ‘&aux’ arguments that _do_
           correspond to slots.
 
           You can specify any number of full-format ‘:constructor’
           options on a structure.  The default constructor is still
           generated as well unless you disable it with a simple-format
           ‘:constructor’ option.
 
                (cl-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 are ‘"Jane"’, 0, and ‘female’,
           respectively.
 
           The second constructor takes two keyword arguments, ‘:name’,
           which initializes the ‘name’ slot and defaults to ‘"Rover"’,
           and ‘:dog-years’, which does not itself correspond to a slot
           but which is used to initialize the ‘age’ slot.  The ‘sex’
           slot is forced to the symbol ‘canine’ with no syntax for
           overriding it.
 
      ‘:copier’
           The argument is an alternate name for the copier function for
           this type.  The default is ‘copy-NAME’.  ‘nil’ means not to
           generate a copier function.  (In this implementation, all
           copier functions are simply synonyms for ‘copy-sequence’.)
 
      ‘:predicate’
           The argument is an alternate name for the predicate that
           recognizes objects of this type.  The default is ‘NAME-p’.
           ‘nil’ means not to generate a predicate function.  (If the
           ‘:type’ option is used without the ‘:named’ option, 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
           package, ‘cl-typep’ simply looks for a function called
           ‘TYPENAME-p’, so it will work for structure types only if they
           used the default predicate name.
 
      ‘:include’
           This option implements a very limited form of C++-style
           inheritance.  The argument is the name of another structure
           type previously created with ‘cl-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:
 
                (cl-defstruct person name (age 0) sex)
                        ⇒ person
                (cl-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"
 
           Thus, if ‘astronaut’ is a specialization of ‘person’, then
           every ‘astronaut’ is also a ‘person’ (but not the other way
           around).  Every ‘astronaut’ includes all the slots of a
           ‘person’, plus extra slots that are specific to astronauts.
           Operations that work on people (like ‘person-name’) work on
           astronauts just like other people.
 
      ‘:print-function’
           In full Common Lisp, this option allows you to specify a
           function that is called to print an instance of the structure
           type.  The Emacs Lisp system offers no hooks into the Lisp
           printer which would allow for such a feature, so this package
           simply ignores ‘:print-function’.
 
      ‘:type’
           The argument should be one of the symbols ‘vector’ or ‘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.
 
      ‘:named’
           This option, which takes no arguments, causes a characteristic
           “tag” symbol to be stored at the front of the structure
           object.  Using ‘:type’ without also using ‘:named’ will 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
           conjunction with ‘:type’.
 
                (cl-defstruct (person1) name age sex)
                (cl-defstruct (person2 (:type list) :named) name age sex)
                (cl-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, ‘cl-defstruct’ is
           not able to make a useful predicate for recognizing them.
           Also, accessors like ‘person3-name’ will be generated but they
           will not be able to do any type checking.  The ‘person3-name’
           function, for example, will simply be a synonym for ‘car’ in
           this case.  By contrast, ‘person2-name’ is able to verify that
           its argument is indeed a ‘person2’ object before proceeding.
 
      ‘:initial-offset’
           The argument must be a nonnegative integer.  It specifies a
           number of slots to be left “empty” at the front of the
           structure.  If the structure is named, the tag appears at the
           specified position in the list or vector; otherwise, the first
           slot appears at that position.  Earlier positions are filled
           with ‘nil’ by the constructors and ignored otherwise.  If the
           type ‘:include’s another type, then ‘:initial-offset’
           specifies a number of slots to be skipped between the last
           slot of the included type and the first new slot.
 
    Except as noted, the ‘cl-defstruct’ facility of this package is
 entirely compatible with that of Common Lisp.
 
    The ‘cl-defstruct’ package also provides a few structure
 introspection functions.
 
  -- Function: cl-struct-sequence-type struct-type
      This function returns the underlying data structure for
      ‘struct-type’, which is a symbol.  It returns ‘vector’ or ‘list’,
      or ‘nil’ if ‘struct-type’ is not actually a structure.
 
  -- Function: cl-struct-slot-info struct-type
      This function returns a list of slot descriptors for structure
      ‘struct-type’.  Each entry in the list is ‘(name . opts)’, where
      ‘name’ is the name of the slot and ‘opts’ is the list of slot
      options given to ‘defstruct’.  Dummy entries represent the slots
      used for the struct name and that are skipped to implement
      ‘:initial-offset’.
 
  -- Function: cl-struct-slot-offset struct-type slot-name
      Return the offset of slot ‘slot-name’ in ‘struct-type’.  The
      returned zero-based slot index is relative to the start of the
      structure data type and is adjusted for any structure name and
      :initial-offset slots.  Signal error if struct ‘struct-type’ does
      not contain ‘slot-name’.
 
  -- Function: cl-struct-slot-value struct-type slot-name inst
      Return the value of slot ‘slot-name’ in ‘inst’ of ‘struct-type’.
      ‘struct’ and ‘slot-name’ are symbols.  ‘inst’ is a structure
      instance.  This routine is also a ‘setf’ place.  Can signal the
      same errors as ‘cl-struct-slot-offset’.