LISP - Slots and Accessors
Lisp - Classes, Slots, and Accessors
Section titled “Lisp - Classes, Slots, and Accessors”Welcome to the Common Lisp Object System (CLOS), one of the most powerful and flexible object systems available. In this chapter, we’ll learn the fundamentals: defining classes, the slots that hold their data, and the accessors used to interact with that data.
Defining a Class with defclass
Section titled “Defining a Class with defclass”A class is a blueprint for creating objects. We define a class using the defclass macro. Inside, we define slots, which are the data members of the class. Let’s model a product for an e-commerce application.
(defclass product () ((id :initarg :id :reader product-id) (name :initarg :name :accessor product-name) (price :initarg :price :accessor product-price :initform 0.0 :type 'float) (inventory-count :initarg :inventory :accessor product-inventory :initform 0 :type 'integer)))Understanding Slot Options
Section titled “Understanding Slot Options”Each slot is defined with a name followed by options that control its behavior:
:initarg: Creates a keyword (e.g.,:id) that can be used to initialize this slot when creating an object withmake-instance.:accessor: Creates a generic function (e.g.,product-name) that can both read the slot’s value and write to it using(setf (product-name an-instance) new-value).:reader: Creates a function (e.g.,product-id) that can only read the slot’s value. This is useful for immutable fields like an ID.:writer: (Not used above) Creates a function that can only write to the slot. It’s less common than:accessoror:reader.:initform: Provides a default value for the slot if it’s not supplied during initialization (e.g.,pricedefaults to0.0).:type: Specifies the expected type of data for the slot. This helps with error checking and potential compiler optimizations.
Creating and Using Objects
Section titled “Creating and Using Objects”We create an instance of a class with make-instance. We then use the accessor and reader functions we defined to interact with its data.
Complete Example (main.lisp)
Section titled “Complete Example (main.lisp)”;; Define the 'product' class as shown above
;; --- Create an instance of the class ---(defvar *keyboard* (make-instance 'product :id "KBD-42" :name "Mechanical Keyboard" :price 99.99 :inventory 50))
;; --- Read data using accessors and readers ---(format t "--- Initial Product State ---~%")(format t "ID: ~a (read-only)~%" (product-id *keyboard*))(format t "Name: ~a~%" (product-name *keyboard*))(format t "Price: $~,2f~%" (product-price *keyboard*))(format t "In Stock: ~d~%~%" (product-inventory *keyboard*))
;; --- Update data using an accessor ---(format t "--- Updating Product State ---~%")(format t "Putting the keyboard on sale...~%")(setf (product-price *keyboard*) 79.99)
;; A customer buys one(decf (product-inventory *keyboard*))
;; --- Display the updated state ---(format t "New Price: $~,2f~%" (product-price *keyboard*))(format t "New Stock: ~d~%" (product-inventory *keyboard*))
;; --- Trying to write to a read-only slot will cause an error ---; (setf (product-id *keyboard*) "NEW-ID") ; This would signal an errorOutput
Section titled “Output”When you execute the code, it returns the following result:
--- Initial Product State ---ID: KBD-42 (read-only)Name: Mechanical KeyboardPrice: $99.99In Stock: 50
--- Updating Product State ---Putting the keyboard on sale...New Price: $79.99New Stock: 49Key Takeaways and Next Steps
Section titled “Key Takeaways and Next Steps”- Encapsulation: Using
:readerfor immutable data and:accessorfor mutable data is a key part of encapsulation, protecting your objects from invalid state changes. make-instance: This is the universal constructor for all CLOS objects. The use of keyword arguments makes initialization code readable and self-documenting.- The Power of CLOS: This is just the beginning. The real power of CLOS comes from generic functions and
defmethod, which allow you to define behavior that is specialized for different classes, enabling true polymorphism. For example, you could define a genericship-itemfunction that behaves differently for aphysical-productversus adigital-download.