Skip to content

Lisp - Objects

Common Lisp features a powerful and unified object system where nearly everything is a first-class object, from simple numbers to complex data structures and even functions. This design philosophy is rooted in Lisp’s dynamic nature, where the type of an object is determined at runtime, not compile time. This chapter explores the fundamental concepts of Lisp objects, their types, and the Common Lisp Object System (CLOS).

Lisp is dynamically typed, meaning variables themselves don’t have a fixed type; instead, the values they hold have types. This allows for great flexibility. At the same time, Lisp is strongly typed, meaning it will not automatically coerce incompatible types (e.g., you cannot automatically add a number and a string without an explicit conversion). The type of any object can be checked at runtime.

;; A variable can hold different types of objects over its lifetime.
(let ((my-variable 42))
(print (type-of my-variable)) ;=> COMMON-LISP:INTEGER
(setf my-variable "Hello, Lisp!")
(print (type-of my-variable)) ;=> COMMON-LISP:SIMPLE-STRING
(setf my-variable '(1 2 3))
(print (type-of my-variable))) ;=> COMMON-LISP:CONS

Lisp provides a rich hierarchy of built-in data types. All types inherit from the root type, T. You can inspect an object’s type using the type-of function and check its type using predicate functions, which by convention end in -p or p.

  • Scalars: number (e.g., integer, float, ratio), character, symbol.
  • Collections: list (linked lists), vector (one-dimensional arrays), array (multi-dimensional), hash-table, string.
  • Composite Types: structure (created with defstruct), class-object (instances of CLOS classes).
  • Special Types: function, pathname, stream.
(print (numberp 101)) ;=> T
(print (stringp "text")) ;=> T
(print (listp '(a b c))) ;=> T
(print (symbolp 'my-sym)) ;=> T
(print (stringp 42)) ;=> NIL

CLOS is the object-oriented programming (OOP) paradigm integrated into Common Lisp. It is incredibly powerful and flexible, going beyond the OOP systems of many other languages. Objects in CLOS are instances of classes defined with defclass.

  • Classes: A blueprint defining the structure (slots) and behavior of objects. Defined with defclass.
  • Instances: A concrete object created from a class definition using make-instance.
  • Slots: The attributes or data members of an object, analogous to fields or properties in other languages.
  • Generic Functions: Functions whose behavior is determined by the classes or types of their arguments. Defined with defgeneric.
  • Methods: An implementation of a generic function specialized for certain argument classes. Defined with defmethod. This allows for polymorphism.
  • Multiple Inheritance: A CLOS class can inherit slots and methods from multiple parent classes, enabling powerful and reusable code patterns.
  • Multi-methods: Unlike most OOP languages where methods belong to a class and dispatch on only the host object’s type, Lisp’s generic functions can dispatch on the types of multiple arguments.
  • Unified System: In Lisp, almost everything is an object, providing a consistent and predictable environment.
  • Flexibility through Dynamic Typing: The ability to determine and check types at runtime allows for highly adaptable and expressive code.
  • Power of CLOS: For complex applications, CLOS provides a state-of-the-art OOP system with features like multiple inheritance and multi-methods that are unparalleled in many mainstream languages.

The Lisp object system is a cornerstone of the language’s power. It seamlessly blends simple, built-in data types with a sophisticated, world-class object-oriented system (CLOS). Understanding that all data are typed objects and that you can interact with them dynamically is the first step to mastering Lisp’s expressive capabilities.