Nyquist / XLISP 2.0  -  Contents | Tutorials | Examples | Reference

XLISP Objects Primer


  1. Programming Styles
  2. Object Oriented Programming
  3. Xlisp Object Terminology
  4. Sending Messages
  5. Classes
  6. A Better Class Example
  7. Instances
  8. Methods
  9. Sending Messages To A Superclass
  10. Object And Class
  11. Objects Example

This tutorial is adapted from a 'XLISPOOP.DOC' document with the following copyright:

XLisp 2.0 Objects Primer by Tim I Mikkelsen - February 3, 1990

Copyright (c) 1990 by Tim I. Mikkelsen. All Rights Reserved. No part of this document may be copied, reproduced or translated for commercial use without prior written consent of the author. Permission is granted for non-commercial use as long as this notice is left intact.

One of the features in the design of XLISP is object-oriented programming. This primer is intended to serve as a very brief introduction to the object facilities of the XLISP 2.0 dialect of LISP. Note that the object features of XLISP are not based on other existing object definitions in other LISP dialects. If you find problems in the primer, I'd appreciate hearing.

Tim Mikkelsen, (tim@hpfcbig.SDE.HP.COM), 4316 Picadilly Drive, Fort Collins, Colorado 80526


1  Programming Styles


There are many programming models, some of them are:

A language can have aspects of one or many of these programming models.

Procedure-Oriented

The programming paradigm most people are familiar with is the procedural style. The primitives in procedural programming are subroutines and data structures. Through these primitives, programmers have some limited abilities to share programs and program fragments. C and Pascal are examples of procedural languages. Some procedural languages [such as Modula and ADA] have extensions that provide for better sharing of code.

Object-Oriented

Object-oriented programming is based on the primitives of objects, classes and messages. Objects are defined in terms of classes. Actions occur by sending a message to an object. An object's definition can be inherited from more general classes. Objective-C and C++ both are object-oriented dialects of the C language. Many dialects of Lisp have some object-oriented extension [Flavors, Common LOOPS, CLOS and others]. There currently is standards work proceeding to add object-oriented programming to Common Lisp.

  Back to top


2  Object Oriented Programming


The object-oriented programming model is based around the concepts of objects, classes and messages. An object is essentially a black box that contains internal state information. You send an object a message which causes the object to perform some operation. Objects are defined and described through classes.

One aspect of an object is that you do not have to know what is inside or how it works to be able to use it. From a programming point of view, this is very handy. You can develop a series of objects for someone to use. If you need to change what goes on inside, the users of the objects should be unaware.

Another aspect of objects is that of inheritance. You can build up new classes from existing classes by inheriting the existing class's functionality and then extending the new definition. For example, you can define a 'tool' class with various attributes and then go about creating object instances like 'tool-1', 'tool-2', and so on. You can also create new sub-classes of the 'tool' class like 'power-tool'. This is also very handy because you don't have to re-implement something if you can build it up from existing code.

  Back to top


3  Xlisp Object Terminology


There are many different languages with object-oriented extensions and facilities. The terminology, operations and styles of these are very different. Some of the main definitions for XLISP's object-oriented extensions are:

object data type

The object data type is a built-in data type of XLISP. Members of the object data type are object instances and classes.

object instances

An 'object instance' is a composite structure that contains internal state information, methods [the code which respond to messages], a pointer to the object instance's defining class and a pointer to the object's super-class. XLISP contains no built-in object instances.

class objects

A class object is, essentially, the template for defining the derived object instances. A class object, although used differently from a simple object instance, is structurally a member of the object data type. It also contains the linking mechanism that allows you to build class hierarchies [sub-classes and super-classes]. XLISP contains two built-in class objects, 'object' and 'class'.

message selector

The 'message selector' is the symbol that is used to select a particular action [method] from the object.

message

The 'message' is the combination of the message selector and the data [if any] to be sent to the object.

method

The 'method' is the actual code that gets executed when the object receives the message.

  Back to top


4  Sending Messages


The mechanism for sending messages to XLISP objects is via the send function. It takes an object, a message selector and various optional arguments [depending on the message selector].

The way that a user creates a new object is to send a :new message to a previously defined class. The result of this send will return an object, so this is normally preceded by a setq.

> (setq my-object (send object :new))
#<Object: #2e100>

The examples are similar to what you should see on your computer screen. The '>' is the normal XLISP prompt, the characters that follow the prompt is what you type in to try the examples. Note that XLISP prints objects together with their internal pointer, like #2e100 in the example above. These #ID numbers may not match with the numbers you see on the screen if you try the examples, but this is not an error.

The object created above is of limited value. Most often, you create a 'class' object and then you create instances of that class. So in the following example, a class called 'my-class' is created that inherits its definition from the a built-in class definition. Then two instances are created of the new class:

> (setq my-class (send class :new '()))
#<Object: #27756>

> (setq my-instance (send my-class :new))
#<Object: #27652>

> (setq another-instance (send my-class :new))
#<Object: #275da>

  Back to top


5  Classes


In the examples above, a :new message was used to create an object. The message used to see what is in an object is the :show message:

> (send my-class :show)
Object is #<Object: #27756>, Class is #<Object: #23fe2>
  MESSAGES = NIL
  IVARS = NIL
  CVARS = NIL
  CVALS = NIL
  SUPERCLASS = #<Object: #23fd8>
  IVARCNT = 0
  IVARTOTAL = 0
#<Object: #27756>

From the display of the 'my-class' object you can see there are a variety of components. The components of a class are:

class pointer

This pointer shows to what class the object [instance or class] belongs. For a class, this always points to the built-in object class. This is also true of the class object, its class pointer points to itself.

superclass pointer

This pointer shows what the next class up the class hierarchy is. If the user does not specify what class is the superclass, it will point to the built-in class object.

messages

This component shows what messages are allowed for the class, and the description of the method that will be used. If the method is system-defined, it will show up in the form of:

#<Subr-: #18b98>

Remember that the class hierarchy [through the superclass pointer] is searched if the requested message is not found in the class.

instance variables

The IVARS component lists what instance variables will be created when an object instance is created. If no instances of the class exist, there are no instance variables. If there are 5 instances of a class, there are 5 complete and different groups of the instance variables.

class variables and values

The CVARS component lists what class variables exist within the class. The CVALS component shows what the current values of the variables are. Class variables are used to hold state information about a class. There will be one of each of the class variables, independent of the number of instances of the class created.

  Back to top


6  A Better Class Example


The example shown in the previous section does work, but the class and instances created don't really do anything of interest. The following example sets up a tool class and creates some tool instances:

> (setq my-tools (send class :new '(power moveable operation)))
#<Object: #277a6>

> (send my-tools :answer :isnew '(pow mov op) 
    '((setq power pow moveable mov operation op))
#<Object: #277a6>

> (setq drill (send my-tools :new 'AC t 'holes))
#<Object: #2ddbc>

> (setq hand-saw (send my-tools :new 'none t 'cuts))
#<Object: #2dc40>

> (setq table-saw (send my-tools :new 'AC nil 'cuts))
#<Object: #2db00>

A class of objects called 'my-tools' and three instances were created:

  [Figure 1]

First the class 'my-tools' was created by sending the :new message to the built-in class object. Then, within the 'my-tool' class, three instances called 'drill', 'hand-saw' and 'table-saw' were created by sending the :new message to the 'my-tools' class. Notice the three parameters following the message selector. The instance variables are initialized from these parameters by the :isnew method, inherited from the 'my-tools' class at the time when the instances were created.

  Back to top


7  Instances


The following is a display of the contents of some of the instances created above, where the XLISP object #ID numbers had been replaced by the respective class and instance names:

> (send drill :show)
Object is #<Object: #[drill]>, Class is #<Object: #[my-tools]>
  POWER = AC
  MOVEABLE = T
  OPERATION = HOLES
#<Object: #[drill]>

> (send hand-saw :show)
Object is #<Object: #[hand-saw]>, Class is #<Object: #[my-tools]>
  POWER = NONE
  MOVEABLE = T
  OPERATION = CUTS
#<Object: #[hand-saw]>

From the display of these instances you can see there are some components and values. The components of an instance are:

class pointer

This pointer shows to which class the current object instance belongs. It is through this link that the system finds the methods to execute for the received messages.

instance variables and values

The variables existing within the instance are shown together with their values. Instance Variables are used to hold state information for each instance. There will be a group of instance variables for each instance.

  Back to top


8  Methods


There have been a few of the messages and methods in XLISP shown to this point like :new and :show. The following are the methods built into XLISP:

:answer

The :answer method allows you to define or change methods within a class.

:class

The :class method returns the class of an object.

:isnew

The :isnew method causes an instance to run its initialization code. When the :isnew method is run on a class, it resets the class state. This allows you to re-define instance variables, class variables, etc.

:new

The :new method allows you to create an instance when the :new message is sent to a user-defined class. The :new method allows you to create a new class when the :new message is sent to the built-in class.

:show

The :show method displays the instance or class.

:isa

The :isa method tests if an object inherits from a class.

  Back to top


9  Sending Messages To A Superclass


In addition to the send function, there is another function called send-super. The send-super function causes the specified message to be performed by the superclass method. This is a mechanism to allow chaining of methods in a class hierarchy. This chaining behavior can be achieved by creating a method for a class with the :answer message. Within the body of the method, you include a send-super form. This function is allowed only inside the execution of a method of an object.

  Back to top


10  Object And Class


The definition of the built-in class 'object' is:

> (send object :show)
Object is #<Object: #[built-in-object]>, Class is #<Object: #[built-in-class]>
  MESSAGES = ((:ISA   . #<Subr-: #[built-in-isa-method]>)
              (:SHOW  . #<Subr-: #[built-in-show-method]>)
              (:CLASS . #<Subr-: #[built-in-class-method]>)
              (:ISNEW . #<Subr-: #[built-in-isnew-method]>))
  IVARS = NIL
  CVARS = NIL
  CVALS = NIL
  SUPERCLASS = NIL  ; no superclass
  IVARCNT = 0
  IVARTOTAL = 0
#<Object: #[built-in-object]>

Note that 'object' is a class, as opposed to an 'instance-style' object. 'object' has no superclass, it is the top or root of the class hierarchy. 'object's class is 'class'.

> (send class :show)
Object is #<Object: #[built-in-class]>, Class is #<Object: #[built-in-class]>
  MESSAGES = ((:ANSWER . #<Subr-: #[built-in-answer-method]>) 
              (:ISNEW  . #<Subr-: #[built-in-isnew-method]>) 
              (:NEW    . #<Subr-: #[built-in-new-method]>))
  IVARS = (MESSAGES IVARS CVARS CVALS SUPERCLASS IVARCNT IVARTOTAL)
  CVARS = NIL
  CVALS = NIL
  SUPERCLASS = #<Object: #[built-in-object]>
  IVARCNT = 7
  IVARTOTAL = 7
#<Object: #[built-in-class]>

'class' has a superclass of 'object'. It's class is itself, 'class'.

  Back to top


11  Objects Example


The following is an example, using the idea of tools again. It contains a hierarchy of tool classes. The top of the class hierarchy is 'tools'. 'hand-tools' and 'shop-tools' are sub-classes of 'tools'. The example creates instances of these sub-classes. It is possible to extend this example in various ways. One obvious extension would be to create a third tier of classes under 'hand-tools' that could contain classes like drills, screwdrivers, pliers and so on.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;       Define the superclasses and classes
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; make TOOLS superclass
;;
;;   with a different :ISNEW method
;;   added methods are :BORROW and :RETURN
;;
;;   class variables      NUMBER        contains # of tool instances
;;                        ACTIVE-LIST   contains list of current objects
;;
;;   instance variables   POWER         list - (AC BATTERY HAND)
;;                        MOVEABLE      CAN-CARRY or CAN-ROLL or FIXED
;;                        OPERATIONS    list
;;                        MATERIAL      list - (WOOD METAL PLASTIC ...)
;;                        PIECES        list
;;                        LOCATION      HOME or person's name
;;

(setq tools (send class :new '(power moveable operations
                               material pieces location) 
                             '(number active-list)))

(send tools :answer :isnew '() 
  '((setq number      (if (null number) 1 (1+ number))
          active-list (cons self active-list)
          location    'home)))

(send tools :answer :borrow '(by-who)
  '((if (eq location 'home)
        (setq location by-who)
        (print "you can't"))))

(send tools :answer :return '()
  '((if (eq location 'home)
        (print "got it already")
        (setq location 'home))))

;; make HAND-TOOLS class
;; - with a different :ISNEW method
;; - new instance variable WEIGHT = <number> of pounds
;; - the rest is inherited from TOOLS
 
(setq hand-tools (send class :new '(weight) '() tools))

(send hand-tools :answer :isnew '(pow op mat parts w-in)
  '((setq power       pow
          moveable    'can-carry
          operations  op
          material    mat
          pieces      parts
          weight      w-in)
    (send-super :isnew)))

;; make SHOP-TOOLS class
;; - with a different :ISNEW method
;; - no new instance variables
;; - the rest is inherited from TOOLS

(setq shop-tools (send class :new '() '() tools))

(send shop-tools :answer :isnew '(pow mov op mat parts)
  '((setq power       pow
          moveable    mov
          operations  op
          material    mat
          pieces      parts)
    (send-super :isnew)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;       Create instances of various tool classes
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(setq hand-drill (send hand-tools :new       ; make an instance - HAND-DRILL
                                  '(ac) 
                                  '(drill polish grind screw)
                                  '(wood metal plastic)
                                  '(drill drill-bits screw-bits buffer)
                                  '2.5))

(setq table-saw  (send shop-tools :new       ; make an instance - TABLE-SAW
                                  '(ac)
                                  'fixed
                                  '(rip cross-cut)
                                  '(wood plastic)
                                  '(saw blades fence)))

(setq radial-arm (send shop-tools :new       ; make an instance = RADIAL-ARM
                                  '(ac)
                                  'can-roll
                                  '(rip cross-cut)
                                  '(wood plastic)
                                  '(saw blades dust-bag)))

The following session shows how to use the tool definitions from the code above:

> (send hand-drill :borrow 'fred)
FRED

> (send table-saw :return)
"got it already"
"got it already"

> (send hand-drill :borrow 'joe)
"you can't"
"you can't"

> (send hand-drill :return)
HOME

Fred was able to borrow the 'hand-drill'. When an attempt was made to return the 'table-saw', it was already at home. A second attempt to borrow the 'hand-drill' indicated that "you can't" because it was already lent out. Lastly, the 'hand-drill' was returned successfully. [Note that the "got it already" and "you can't" strings show up twice in the display because the methods both print and return the string.)

The following example shows the structure of the 'tools' object with the XLISP #ID numbers replaced by the related class and method names:

> (send tools :show)
Object is #<Object: #[tools]>, Class is #<Object: #[class]>
  MESSAGES = ((:RETURN . #<Closure-:RETURN: #[tools-return-method]>) 
              (:BORROW . #<Closure-:BORROW: #[tools-borrow-method]>) 
              (:ISNEW  . #<Closure-:ISNEW:  #[tools-isnew-method]>))
  IVARS = (POWER MOVEABLE OPERATIONS MATERIAL PIECES LOCATION)
  CVARS = (NUMBER ACTIVE-LIST)
  CVALS = #(3 (#<Object: #[radial-arm]> 
               #<Object: #[table-saw]> 
               #<Object: #[hand-drill]>))
  SUPERCLASS = #<Object: #[object]>
  IVARCNT = 6
  IVARTOTAL = 6
#<Object: #[tools]>

The two 'tools' sub-classes 'hand-tools' and 'shop-tools' structure looks like:

> (send hand-tools :show)
Object is #<Object: #[hand-tools]>, Class is #<Object: #[class]>
  MESSAGES = ((:ISNEW . #<Closure-:ISNEW: #[hand-tools-isnew-method]>))
  IVARS = (WEIGHT)
  CVARS = NIL
  CVALS = NIL
  SUPERCLASS = #<Object: #[tools]>
  IVARCNT = 1
  IVARTOTAL = 7
#<Object: #[hand-tools]>

> (send shop-tools :show)
Object is #<Object: #[shop-tools]>, Class is #<Object: #[class]>
  MESSAGES = ((:ISNEW . #<Closure-:ISNEW: #[shop-tools-isnew-method]>))
  IVARS = NIL
  CVARS = NIL
  CVALS = NIL
  SUPERCLASS = #<Object: #[tools]>
  IVARCNT = 0
  IVARTOTAL = 6
#<Object: #[shop-tools]>

The class 'hand-tools' has an instance 'hand-drill' which looks like:

> (send hand-drill :show)
Object is #<Object: #[hand-drill]>, Class is #<Object: #[hand-tools]>
  WEIGHT = 2.5
  POWER = (AC)
  MOVEABLE = CAN-CARRY
  OPERATIONS = (DRILL POLISH GRIND SCREW)
  MATERIAL = (WOOD METAL PLASTIC)
  PIECES = (DRILL DRILL-BITS SCREW-BITS BUFFER)
  LOCATION = HOME
#<Object: #[hand-drill]>

  Back to top


Nyquist / XLISP 2.0  -  Contents | Tutorials | Examples | Reference