{Begin SubSec Creating and Using Objects} {Title Creating and Using Objects} {Text {index objects} {note add general info on object-oriented programming} In the LOOPS implementation of object-oriented programming, there are three types of objects: Instances,{index instances} Classes,{index classes} and Metaclasses.{index metaclasses} Instances are used like data objects in Lisp; they are commonly created, passed around, and modified by procedures (although all objects can be). Classes and metaclasses are objects which "define" a group of objects that are "instances of" that class or metaclass. The difference between classes and metaclasses is that the instances of a class are instances, and the instances of a metaclass are classes---all comments about classes apply to metaclasses, except where otherwise stated. {note should flush terminalogical distinction between class and metaclass --- just have classes and 'classes whose instances are classes' --- mjsann} Note that the word "instance" is used in two separate ways: the phrase "instance of" refers to the relation between any object and the class (or metaclass) that "defines" it. The noun "instance" is only used to refer to those objects which are instances of classes. {note need to change either "instance" or "instance of". This is too confusing --- mjsann} A class contains information about instance variables,{index instance variables} class variables,{index class variables} and methods.{index methods} Instance variables are local variables stored within each instance of the class. Class variables are variables stored within the class object, accessable from each instance of the class. Methods are procedures which are used to perform operations on instances of the class. Each Class also contains a list of other classes called "super classes"{index super classes} or "supers".{index supers} The super class list provides a mechanism for inheriting instance variables, class variables, and methods from other classes (see {PageRef Tag SuperClasses}). This section first describes how to create and use objects. Next, "sending a message" (the standard way to invoke a method). Next, creating and using new instances. Next, defining and editing new classes. Finally, defining a new method for a class. {note I hate writing this sort of summary ---mjsann} {Begin SubSec Sending a Message to an Object} {Title Sending a Message to an Object} {Text Operations in LOOPS are invoked by sending messages. Sending a message to an object invokes a method (from the class that the object is an instance of) to execute the operation. Messages are sent using the function {fn _} as follows: {FnDef {Name _} {Args object Selector arg{sub 1} {ellipsis} arg{sub N}} {Type NLAMBDA NOSPREAD} {Text Sends the message {arg Selector} to the object {arg object} with the arguments {arg arg{sub 1}} {ellipsis} {arg arg{sub N}}. {arg Selector} is always implicitly quoted (i.e., not evaluated); the remaining arguments are evaluated. {arg object} must be an "internal pointer" to the object. The internal pointer to the object with the LOOPS name {lisp FOO} can be extracted by the form {lisp ($ FOO)}.{index $ Fn} Note: {fn SEND}{index SEND Fn} can be used instead of {fn _}. The arrow notation, although less mnemonic, is usually used to make expressions shorter and hence easier to type and read. If it is necessary to {it compute} the selector, one can use the function {fn _!},{index _!} which is just like {fn _} except that it also evaluates its {arg Selector} argument. }} Example: {lispcode (_ ($ PayRoll) PrintOut file1)} This sends a {lisp PrintOut} message to the class {lisp PayRoll} (with a single argument; the value of the Intrerlisp variable {lisp file1}). {Begin Note} {lisp $} is a read macro that converts a class name into an expression that evaluates to an internal pointer to the class. ---- $FOO changed to ($ FOO) --- mjsann Notation. In these examples, boldface is used to indicate things that are typed exactly as they are shown. Italics is used to indicate meta-language. ---- this notation punted to conform with IM format --- mjsann {End Note} }{End SubSec Sending a Message to an Object} {Begin SubSec Creating a New Instance} {Title Creating a New Instance} {Text To create an instance of a particular class, one sends the message {lisp New}{index New (Message)} to the class: {Def {Type (Message)} {Name New} {Args} {PrintName {lisp (_ {arg class} New)}} {Text Returns a new instance of the class {arg class}. In the usual case, initial values for instance variables are taken from the instance variable descriptions associated with the class. LOOPS provides some other ways to exercise control over the initialization of values in instances (see {PageRef Tag InitInstance}). }} }{End SubSec Creating a New Instance} {Begin SubSec Naming and Pointing to Objects} {Title Naming and Pointing to Objects} {Text In order to manipulate a LOOPS object, it is necessary to have a pointer to it. One way to do this is to save a pointer to the object in an Interlisp variable, for example: {lispcode (SETQ myVariable (_ ($ Transistor) New))} This creates a new instance of the {lisp Transistor} class, and stores a pointer to this instance in the Interlisp variable {lisp myVariable}. Pointers to instances can also be saved in instance variables. LOOPS objects may be passed around and examined by Lisp functions. The following function is useful: {FnDef {Name Object?} {Args X} {Text Returns {arg X} if it is a LOOPS objects, otherwise {lisp NIL}. }} Another way to manipulate an object is by giving it a unique "LOOPS name". An object can be given a LOOPS name by sending it the message {lisp SetName} {Def {Type (Message)} {Name SetName} {args name} {PrintName {lisp (_ {arg object} SetName {arg name})}} {Text Sets the LOOPS name {arg name} to refer to {arg object}. LOOPS names are unique in a LOOPS environment; the name is assigned in the environment specified by the global variable {var CurrentEnvironment}{index CurrentEnvironment Var} (see {PageRef Tag LOOPSenvironments} for a complete description of environments). If an attempt is made to assign a name already in use in the environment, and the global flag {var ErrorOnNameConflict}={lisp T}, an error is generated.{index ErrorOnNameConflict Var} If {var ErrorOnNameConflict}={lisp NIL}, and there is already an object {arg oldObject} with that name, the name is unset for {arg oldObject} and set for {arg object} without generating an error. }} For example, if {lisp I1} is an Interlisp variable whose value is a pointer to some instance, the object can be given the LOOPS name {lisp Foo} as follows: {lispcode (_ I1 SetName 'Foo)} After naming {lisp I1} this way, the user can refer to this object as {lisp ($ Foo)},{index $ Fn} which returns the object whose name is {lisp Foo}. The user can refer to an object with a {it computed} LOOPS name using the form {lisp ($! {arg EXPR})}.{index $! Fn} For example, if the value of the lisp variable {lisp X} is the atom {lisp Apple}, then {lisp ($! X)} = {lisp ($ Apple)}. Classes having {lisp NamedObject}{index NamedObject (Class)} (see {PageRef (Class) NamedObject}) as a super class inherit an instance variable, {lisp name}, that contains the name of the objects. Instances of these classes can be named, as before, with a {lisp SetName} message, or alternatively as a side effect of setting the {lisp name} instance variable. Class objects are automatically given a LOOPS name when they are created, as described below. {Begin Note} The notation {lisp $Foo} translates at read time into the expression {lisp ($ Foo)} which when later evaluated returns the object whose name is {lisp Foo}. The related notation {lisp #$Foo} translates at read time into a pointer to the object named {lisp Foo}, rather than an expression to be evaluated. This is often useful for inserting a direct pointer to an object in another object (e.g., using an editor). ----- #$Foo notation still used? is there a non-prefix notation? ---mjsann ----- need to have better description of #$FOO form and how it is used. Where? not here. --- mjsann {End Note} }{End SubSec Naming and Pointing to Objects} {Begin SubSec Defining a New Class} {Title Defining a New Class} {Text The way one creates a new class is to send the message {lisp New}{index New (Message)} to a metaclass. Usually, the metaclass named {lisp Class}{index Class (Class)} is used. {Def {Type (Message)} {Name New} {args className supersList} {PrintName {lisp (_ {arg metaClass} New {arg className} {arg supersList})}} {Text Returns a new instance of the metaclass {arg metaClass}. {arg className} is the new class name and {arg supersList} is a list of the names of the super classes for this new class. If the list of super class names is omitted, {arg supersList} defaults to {lisp (Object)}. }} Example: {lispcode (_ ($ Class) New 'StudentEmployee '(Student Employee))} This defines a new class, {lisp StudentEmployee} as a subclass of the known classes named {lisp Student} and {lisp Employee}. An abbreviated way of defining a class is to use the function {fn DC}: {FnDef {Name DC} {Args className supersList} {Text ("define class") Sends the class {lisp Class} an appropriate {lisp New} message: {lisp (_ ($ Class) {arg className} {arg supersList})} }} Example: {lispcode (DC 'StudentEmployee '(Student Employee))} This specifies that the class {lisp Student} is to be used recursively, inheriting both from {lisp Student} and all its supers, and from {lisp Employee} and all its supers. After defining the class, one can modify its structure by editing the textual source for the class with {fn EC}: {FnDef {Name EC} {Args className {anonarg}} {Text ("edit class") {fn EC} envokes the Interlisp editor on the textual source for the class named {arg className}. The editor can also be envoked by sending the {lisp Edit} message:{index Edit (Message)} {lisp (_ ($ {arg className}) Edit)}. }} For example, {lisp (EC 'StudentEmployee)} might start the editor editing the expression: {lispcode [DEFCLASS StudentEmployee (MetaClass Class Edited: (* lc: "18-Oct-82 14:26")) (Supers Student Employee) (InstanceVariables) (Methods]} One can then change this to: {lispcode [DEFCLASS StudentEmployee (MetaClass Class Edited: (* lc: "18-Oct-82 14:26")) (Supers Student Employee) (InstanceVariables (sponsor NIL doc (* Name of sponsor)) (stay 3 doc (* number of months here))) (Methods]} Leaving the editor successfully at this point would install the two instance vararible descriptions in the class {lisp StudentEmployee}. Then, in addition to those instance variables {lisp StudentEmployee} inherited from {lisp Student} and {lisp Employee}, each instance would also have two new ones, {lisp sponsor} and {lisp stay} with default values of {lisp NIL} and {lisp 3} respectively{note describe 'doc' props}. A more extensive description of editing and changing classes is found in {SectionRef Tag LOOPSediting}. }{End SubSec Defining A New Class} {Begin SubSec Defining a Method} {Title Defining a Method} {Text In order to define a method for a class, one can use the Interlisp function {fn DM}: {FnDef {Name DM} {args className selector argsOrFnName form} {Text Defines a method for the class named {arg className} that can be called using the selector {arg selector}. If {arg form} is non-{lisp NIL}, then {arg argsOrFnName} is interpreted as the list of arguments for a function, and {arg form} as the body of that function. If the first element of the list {arg argsOrFnName} is not {lisp self}, then {lisp self} is added on the front. {fn DM} defines a function whose name is the concatenation of {arg className}, a period, and {arg selector}. For example, {lisp Class.List} is the function name created for the {lisp List} selector in the class {lisp Class}. The function definition is created by substituting into {lisp (LAMBDA {arg argsOrFnName} . {arg form})}. If {arg argsOrFnName} and {arg form} are {lisp NIL}, {fn DM} creates a skeleton definition for the function and puts the user into the Interlisp editor, editing the skeleton. If only {arg form} is {lisp NIL}, {arg argsOrFnName} is interpreted as the name of a function to be used for implementing the method. Note: a method can also be defined by sending the {lisp DefMethod}{index DefMethod (Message)} message to the class: {lisp (_ {arg class} DefMethod {arg selector} {arg argsOrFnName} {arg form})}. }} Example: {lispcode (DM 'Number 'Increment '(self) '((* incr my IV) (_@ :myValue (ADD1 (@ :myValue)))))} This defines a method with selector {lisp Increment} for the class {lisp Number} which adds 1 to the instance variable {lisp myValue} (the {lisp @}-notation for accessing variables is described on {PageRef Tag ObjectVariables}). This form results in the definition of a function named {lisp Number.Increment} as follows: {lispcode (DEFINEQ (Number.Increment (LAMBDA (self) (* incr my IV) (_@ :myValue(ADD1 (@ :myValue)))]} {FnDef {Name EM} {args className selector {anonarg}} {Text Calls the Interlisp editor to edit the method for the class named {arg className} associated with the selector {arg selector}. Often it is more conveniently to use the LOOPS browser to edit the code for a method (see {PageRef Tag LOOPSbrowser}). }} Example: To edit the method from the example above, one could type: {lispcode (EM 'Number 'Increment)} This will edit the method of class {lisp Number} which responds to the selector {lisp Increment}, whether or not it has a name of the standard form. }{End SubSec Defining a Method} }{End SubSec Creating and Using Objects}