19.1 Introduction

The objects module provides simple support for object-oriented programming in PSL. It is based on the ”flavors” facility of the LISP machine, which is the source of its terminology. The LISP Machine Manual contains a much longer introduction to the idea of object oriented programming, generic operations, and the flavors facility in particular. This discussion briefly covers the basics of using objects to give you an idea of what is involved; then it explains the details.

19.1.1 Terminology

An object datatype is known as a flavor. It can be thought of in two parts:

  1. A template that defines the characteristics of the flavor.
  2. A set of operations that can be performed on the flavor.

The template that defines the characteristics of a flavor is known as the flavor definition. It is created by the macro defflavor. Each operation that can be performed on an object is known as a method definition. They are created by the macro defmethod. Invoking an operation on an object is known as sending an object a message. A created object of a given flavor is known as an instance of that flavor.

Example of a flavor definition is:

(defflavor complex-number  
            (imaginary-part 0.0)  

A flavor definition specifies the fields, or in our terminology, the instance variables, that each object of that flavor is to have. In the example above, the instance variables are REAL-PART and IMAGINARY-PART. The mention of the instance variable IMAGINARY-PART indicated that by default the imaginary part of a complex number will be initialized to 0.0. There is no default initialization for REAL-PART.

Instance variables may be strictly part of the implementation of a flavor, totally invisible to users. Typically though, some of the instance variables are directly visible in some way to the user of the object. The flavor definition may specify ”initable-instance-variables”, ”gettable-instance-variables”, and ”settable-instance-variables”. These options mean that the instance variables specified are able to be initialized (initable), able to be accessed by name (gettable), and able to be assigned and accessed (settable). None, some of, or all of the instance variables may be specified in each option. In the example above, both REAL-PART and IMAGINARY- PART may be initialized and may be accessed by name.

With the objects package the programmer completely controls what operations are to be done on objects of each flavor, so this is a true object-oriented programming facility. Also, operations on flavored objects are generic. This means that the operations can be done on an object of any flavor, as long as the operations are defined for that flavor of object. The same operation can be defined for many flavors; and whenever the operation is invoked, what is actually done will depend on the flavor of the object it is being done to.

To see the power of generic operations, consider the following example:

Say we wish to write a scanner that reads a sequence of characters out of some object and processes them. It does not need to assume that the characters are coming from a file, or even from an I/O channel.

Suppose the scanner gets a character by invoking the GET-CHARACTER operation. In this case any object of a flavor with a GET-CHARACTER operation can be passed to the scanner, and the GET-CHARACTER operation defined for that object’s flavor will performed to fetch the character. This means that the scanner can get characters from a string, or from a text editor’s buffer, or from any object at all that provides a GET-CHARACTER operation. The scanner is automatically general.