This tutorial is adapted from a '
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
There are many programming models, some of
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.
Object-Oriented
The
One aspect of an object is that you do not have to know what is inside or
how it works to be able to
Another aspect of objects is that of inheritance.
There are many different languages with
The object data type is a
An 'object instance' is a composite structure that contains internal
state information, methods [the code which respond to messages],
A class object is, essentially,
the template for defining the derived object instances.
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
method
The 'method' is the actual code that gets executed when the object receives the message.
The mechanism for sending messages to XLISP objects is via the
send function.
The way that a user creates a new object is to
send a
:new message to a previously
defined class.
> (setq my-object (send object :new)) #<Object: #2e100>
The object created above is of limited value. Most often, you create a
'class' object and then you create instances of that 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>
In the examples above, a :new
message was used to create an object.
> (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:
This pointer shows to what class the object [instance or class]
belongs. For a class, this always points to the
This pointer shows what the next class up the class
messages
This component shows what messages are allowed for the class, and the
description of the method that will be used.
#<Subr-: #18b98>
Remember that the class hierarchy [through the superclass pointer] is searched if the requested message is not found in the class.
The IVARS component lists what instance variables will be created
when an object instance is created.
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.
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 '
First the class '
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:
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.
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.
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
:new
The :new method allows you
to create an instance when the
:new message is sent to a
:show
The :show method displays the instance or class.
:isa
The :isa method tests if an object inherits from a class.
In addition to the send function,
there is another function called
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'.
The following is an example, using the idea of tools again.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; 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 '
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]>