Skip to content

LISP - Symbols

In Common Lisp, a symbol is a unique, named data object. It’s not just a name for a variable; the symbol itself is an object that can be manipulated. Think of it as a named container that can hold a value, a function, and a list of properties all at once.

One of the classic features of symbols is the property list, or plist. A plist is a simple key-value store attached directly to a symbol, allowing you to associate arbitrary metadata with it.

A property list is structured as a list of alternating keys (called indicators) and values. For example, if we want to describe a person, we could use a symbol 'person and attach properties like age, job, and city.

When a symbol is first created, its property list is empty. You can add or modify properties using the get function inside a setf form. setf is Lisp’s universal assignment operator, capable of modifying many different kinds of places.

Let’s see how to assign properties to a symbol named book.

Example: Creating and Accessing Properties

Section titled “Example: Creating and Accessing Properties”

Create a new source file named main.lisp and enter the following code. This example demonstrates setting and then retrieving properties from the book symbol.

;;; Use SETF with GET to assign properties to the 'book symbol.
;;; Note that both the symbol ('book) and the indicators ('title, etc.) are quoted.
(setf (get 'book 'title) "Gone with the Wind")
(setf (get 'book 'author) "Margaret Mitchell")
(setf (get 'book 'publisher) "Warner Books")
;;; Use GET to retrieve the value for a specific property.
(format t "Title: ~s~%" (get 'book 'title))
(format t "Author: ~s~%" (get 'book 'author))
;;; GET returns NIL if the property does not exist.
(format t "ISBN: ~s~%" (get 'book 'isbn))

When you load and execute the code (e.g., in an SBCL REPL with (load "main.lisp")), it produces the following result:

Title: "Gone with the Wind"
Author: "Margaret Mitchell"
ISBN: NIL

Common Lisp provides a few key functions for managing plists.

  • get symbol indicator &optional default: Retrieves the value for the given indicator. If not found, it returns NIL or the provided default value.
  • symbol-plist symbol: Returns the entire property list of the symbol.
  • remprop symbol indicator: Removes a property (both key and value) from the symbol’s plist.

Example: Inspecting and Removing Properties

Section titled “Example: Inspecting and Removing Properties”

Let’s expand on our previous example to see these functions in action.

;; Define a function to set up and demonstrate properties for a symbol.
(defun demonstrate-plists (person-symbol)
(setf (get person-symbol 'age) 43)
(setf (get person-symbol 'job) "Accountant")
(setf (get person-symbol 'sex) 'female)
(format t "Initial plist for '~s: ~s~%"
person-symbol
(symbol-plist person-symbol))
;; Remove the 'age' property
(remprop person-symbol 'age)
(format t "After removing 'age: ~s~%" (symbol-plist person-symbol)))
;; Run the demonstration with the symbol 'annie
(demonstrate-plists 'annie)
Initial plist for 'ANNIE: (SEX FEMALE JOB "Accountant" AGE 43)
After removing 'age: (SEX FEMALE JOB "Accountant")

Modern Best Practices: Plists vs. Other Structures

Section titled “Modern Best Practices: Plists vs. Other Structures”

While property lists are a classic Lisp feature, for modeling structured application data, modern Common Lisp offers better alternatives:

  • Hash Tables: Ideal for general-purpose key-value mapping where keys are not necessarily symbols. They are highly efficient. (make-hash-table)
  • Structs: A lightweight way to define simple object-like structures with named slots. They offer better performance and type-checking than plists. (defstruct)
  • CLOS Classes: The full Common Lisp Object System provides powerful features like inheritance, multi-methods, and introspection. (defclass)

When should you use plists? Property lists are best suited for attaching metadata to symbols themselves, especially in metaprogramming or compiler-related tasks. For representing entities like books or users in your application, prefer defstruct for simplicity or defclass for more complex requirements.

;; Defining a book structure
(defstruct book
title
author
publisher
isbn)
;; Creating an instance of the book struct
(let ((my-book (make-book :title "Structure and Interpretation of Computer Programs"
:author "Abelson & Sussman"
:publisher "MIT Press")))
;; Accessing slots is clean and efficient
(format t "Struct Title: ~s~%" (book-title my-book))
(format t "Struct Author: ~s~%" (book-author my-book)))
Struct Title: "Structure and Interpretation of Computer Programs"
Struct Author: "Abelson & Sussman"