{Begin SubSec Instance Creation} {Title Instance Creation} {Text {Tag InitInstance} The standard process of creating an instance of a class is to send a {lisp New} message to the class. In the simplest case, this causes the information in the {it instance variable descriptions} of the class to be used to establish default values for variables in the newly created instance. When that process is finished, the instance can be altered in various ways by sending it messages. LOOPS provides a variety of facilities for controlling this by using active values, standard access functions, and metaclasses. This section summarizes some of the common cases. See {PageRef Tag CompositeObjects} for an illustratation of the use of these facilities to support the important example of composite objects. {Begin SubSec Specifying Values at Instance Creation} {Title Specifying Values at Instance Creation} {Text The {lisp NewWithValues} message simplifies the case where it is desired to specify values and properties in an instance when it is created. The form of this message is: {Def {Type (Message)} {Name NewWithValues} {Args valDescriptionList} {PrintName {lisp (← {arg class} NewWithValues {arg valDescriptionList})}} {Text {arg valDescriptionlist} must evaluate to a list of value descriptions, each of which is a list of a variable name, variable value, and properties; e.g. {lispcode (({arg varName{sub 1}} {arg value{sub 1}} {arg prop{sub 1}} {arg propVal{sub 1}} {ellipsis}) ({arg varName{sub 2}} {arg value{sub 2}} {ellipsis}) {ellipsis})} The method for {lisp NewWithValues} first creates the object with {it no} other initialization (e.g. without computing values specified in the class, as described in sections below). It then directly installs the values and property lists specified in {arg valDescriptionList} and returns the created object. Variables which have no description in {arg valDescriptionList} will be given no value in the instance, and thus will inherit the default value from the class. }} }{End SubSec Specifying Values at Instance Creation} {Begin SubSec Sending a Message at Instance Creation} {Title Sending a Message at Instance Creation} {Text A simplification in form is available when one wants to send a message to an instance immediately after its creation. For example, consider: {lispcode (← (← ($ Transistor) New) Display windowCenter)} which creates an instance of the {lisp Transistor} class, and then displays it at a point {lisp windowCenter}. A more compact notation for doing this is provided: {lispcode (←New ($ Transistor) Display windowCenter)} where {lisp ←New}{index ←New (Macro)} ("send New") means to create a new instance and send it a message. The value returned by {lisp ←New} is the new instance. Any value returned by the method is discarded. In order to name an object, one can send the message {lisp SetName}{index SetName (Message)} to that object. As a simplification, if one provides an argument to the {lisp New}{index New (Message)} message, the default interpretation of that argument is to use it as a name, sending the newly created object the {lisp SetName} message. }{End SubSec Sending a Message at Instance Creation} {Begin SubSec Computing a Value at First Fetch} {Title Computing a Value at First Fetch} {Text As described earlier, one can use an active value to activate arbitrary procedures when values are fetched. The built-in function {fn FirstFetch}{index FirstFetch Fn} can be used as a {arg getFn} in an active value as the default value in the class. If no value has been assigned to the variable or property before the value is fetched for the first time, the {lisp FirstFetch}{index FirstFetch (Active Value)} active value is invoked. The local state of this active value can be a list which is a form to be evaluated. During the evaluation, the variables {var self},{index self Var} {var varName},{index varName Var} and {var propName}{index propName Var} are are appropriately bound. The local state of the {lisp FirstFetch} active value can also be an atom; if so, it is treated as the name of a function to be applied to the object, {lisp varName} and {lisp propName}. The value of the form or function application is made the value in the instance as well as being returned as the value of the fetch. For example, the random number example could have been done as follows: {lispcode (DEFCLASS TestDatum (MetaClass Class) (... (InstanceVariables (sampleX #((RAND 0. 100.) FirstFetch))) ...)} In this example {fn FirstFetch} evaluates the form {lisp (RAND 0. 100.)} and replaces the value of the {lisp sampleX} variable of the instance by the random number. In many cases the form may be a {fn ←} expression. }{End SubSec Computing a Value at First Fetch} {Begin SubSec Computing a Value at Instance Creation} {Title Computing a Value at Instance Creation} {Text In the previous example, {fn FirstFetch} initializes the value of an instance variables at first access. Sometimes it is important to initialize an instance variable when the instance is created. For such cases LOOPS provides a distinguished {arg getFn}, {fn AtCreation}.{index AtCreation Fn} If a default value of an instance variable or property contains an active value with {fn AtCreation} as its {arg getFn}, then at creation time, the {arg localState} of this active value will be used to determine a value to be inserted in the new instance. As with {fn FirstFetch}, if the {arg localState} is an atom, then it will be treated as the name of a function to be applied to the object, variable name, and property name. If it is a list, then that list will be evaluated in a context in which {var self},{index self Var} {var varName},{index varName Var} and {var propName}{index propName Var} are appropriately bound. Functions run at initialization time are run in the order in which they appear in the class. Default values of variables are available to these functions. If an object is created by {lisp NewWithValues}{index NewWithValues (Message)} without a value being supplied for a variable which contains an {fn AtCreation} default value, then at the first fetch of that variable, the function or form will be evaluated. Example: Suppose we want to have an instance variable called {lisp creationDate} which tells the date that an instance was created. This can be implemented in LOOPS as follows: {lispcode (DEFCLASS DatedObject (MetaClass Class) (... (InstanceVariables (creationDate #((DATE) AtCreation))) ...)} The function {fn DATE} in Interlisp computes a string which is the current date and time. The value of this string at instance creation time is made the intitial value of {lisp creationDate}. Another use of an {fn AtCreation} active value might be to make an index entry to a newly created object. }{End SubSec Computing a Value at Instance Creation} {Begin SubSec Special Actions at Instance Creation} {Title Special Actions at Instance Creation} {Text For some special cases, the user may want to have more control over the creation of instances. For example, LOOPS itself uses different LISP data types to represent classes and instances. The {lisp New}{index New (Message)} message for classes is fielded by their metaclass, usually the object {lisp MetaClass}. This section shows how to create a new metaclass. Any metaclass should have {lisp Class} as one of its super classes and {lisp MetaClass}{index MetaClass (Metaclass)} as its metaclass. The easiest way to create a new metaclass is to send a {lisp New} message to {lisp MetaClass} as follows: {lispcode (← ($ MetaClass) New {arg metaClassName} {arg supers})} This creates a new metaclass with the name {arg metaClassName} and with the super classes named in the list {arg supers}. The default supers for metaclasses is the list containing {lisp Class}. The metaclass for the the new class is {lisp MetaClass}. One then installs the specialized method for {lisp New} in the new metaclass. This method provides the mechanism for creations of instances of the class which have this as a metaclass. Sending this metaclass the message {lisp New} will cause the creation of a class with the appropriate property. As a simple example we will define a new metaclass {lisp ListMetaClass} which will augment the instance creation process by keeping a list of all instances which have been created. This list will be kept on the class property {lisp allInstances}. To create this class we go through the scenario in {FigureRef ListMetaClass}. {Begin Figure} {Tag ListMetaClass} {Text {lispcode ← (← ($ MetaClass) New 'ListMetaClass '(Class)) #$ListMetaClass {it -- We have now defined a new metaclass} {it -- This defines the New method for that metaclass} ← (DM 'ListMetaClass 'New '(self name) '((* Create an instance and add it to list in class) (PROG ((newObj (←Super self New name))) (* newObj created by super method from class) (PutClass self (CONS newObj (LISTP (GetClassHere self 'AllInstances))) 'AllInstances) (* LISTP returns previous list or NIL if none) (RETURN newObj] ListMetaClass.New ← (← ($ ListMetaClass) New 'Book) #$Book {it -- This creates a new class ($ Book)} {it whose metaclass is ($ ListMetaClass)} ← (← ($ Book) New 'B1) #$B1 {it -- Creating #$B1 using ListMetaClass.New} ← (← ($ Book) New 'B2) #$B2 ← (GetClass ($ Book) 'AllInstances) (#$B1 #$B2) {it -- The list of instances created so far.}} } {Caption In this scenario, a new metaclass {lisp ListMetaClass} is defined by the {lisp New} method of {lisp ($ MetaClass)}. It has metaclass {lisp ($ MetaClass)}. We then define the specialized {lisp New} method for {lisp ListMetaClass}. This includes a call to its super ({lisp Class}) to actually create the object; it puts the newly created object on its list of objects. We then create {lisp ($ Book)} which has {lisp ListMetaClass} as its metaclass. When two instances of book are created, each is placed on the list {lisp AllInstances} which is a class property. } {End Figure} }{End SubSec Special Actions at Instance Creation} }{End SubSec Instance Creation}