Skip to content

LISP - Structure Types

In Common Lisp, defstruct is a macro used to define simple, efficient, record-like data structures. It provides a convenient way to group related data into a single object with named slots. defstruct automatically generates a constructor, accessors, and other utility functions for your new data type.

The simplest defstruct defines a new type and its slots. Lisp then creates a suite of functions to work with it.

;; Define a structure named 'book' with four slots.
(defstruct book
title
author
subject
id)
;; Create an instance of the 'book' structure.
;; The constructor 'make-book' is automatically created for us.
;; Using keyword arguments is the standard, readable practice.
(let ((book1 (make-book :title "The Pragmatic Programmer"
:author "Andy Hunt & Dave Thomas"
:subject "Software Engineering"
:id 9780201616224)))
;; Access the slots using the automatically generated accessors.
;; The accessor format is 'structure-name'-'slot-name'.
(print (book-title book1))
(print (book-author book1))
(print (book-id book1))
;; The 'book-p' type predicate was also created.
(print (book-p book1)))
"The Pragmatic Programmer"
"Andy Hunt & Dave Thomas"
9780201616224
T

defstruct is highly customizable. You can define default values for slots and control the generation of helper functions.

(defstruct user
(name "Anonymous") ; Default value for the 'name' slot
(level 1) ; Default value for 'level'
email)
;; Create a user, providing only the required 'email' slot.
(let ((new-user (make-user :email "guest@example.com")))
(print (user-name new-user)) ; Prints the default value
(print (user-level new-user)))
;; Create another user, overriding the defaults.
(let ((admin-user (make-user :name "Admin" :level 99 :email "admin@example.com")))
(print (user-name admin-user)))
"Anonymous"
1
"Admin"

Structures support single inheritance using the :include option. The new structure inherits all the slots from the parent structure.

(defstruct person name age)
;; The 'student' structure includes 'person', inheriting its slots.
(defstruct (student (:include person))
major
gpa)
(let ((s1 (make-student :name "Jane Doe" :age 21 :major "Computer Science" :gpa 3.8)))
;; We can use accessors from the parent structure.
(print (student-name s1))
(print (student-major s1)))
"Jane Doe"
"Computer Science"

Choosing between defstruct and defclass (from the Common Lisp Object System) is a common design decision:

  • Use defstruct when: You need a simple, lightweight data container. Structures are often implemented more efficiently by Lisp systems and are perfect for performance-sensitive code or when you just need to bundle a few pieces of data together.
  • Use defclass when: You need the full power of object-oriented programming. This includes multiple inheritance, method specialization (polymorphism), and dynamic redefinition of classes, which are features not available to structures.

Think of defstruct as being for “data objects” and defclass for “behavioral objects”.

defstruct is a practical and essential tool in the Lisp programmer’s toolkit. It provides a simple, efficient, and clean way to define custom data types for aggregating information, striking a perfect balance between raw lists and the full-featured CLOS.