Create and Destroy Methods

Objects are created with the create/2 predicate. When you define a class, you must specify all the ways that instances of the class can be created. The simplest creation method is defined as

     Self <- create.

If this method were defined for Class, then the command

     | ?- create(Class, Object).

would create an instance of Class and bind the variable Object to that instance. All slots would receive their (possibly default) initial values.

More generally, if the definition for Class contains a create method

     Self <- create(Arguments) :-

then the command

     | ?- create(Class(Arguments), Object).

will create an instance of Class and execute the Body of the create method, using the specified Arguments. The variable Object is bound to the new instance.

If a simple class definition has no create methods, then it is impossible create instances of the class. While the absence of create methods may be a programmer error, that is not always the case. Abstract classes, which are classes that cannot have instances, are often quite useful in defining a class hierarchy.

Create methods can be used to initialize slots in situations when specifying initial slot values will not suffice. (Remember that initial values must be specified as constants at compile time). The simplest case uses the arguments of the create message as initial slot values. For example, the definition of the point class might contain the following create method.

     Self <- create(X,Y) :-
             Self << x(X),
             Self << y(Y).

If used as follows

     | ?- create(point(3.14159, 2.71828), PointObj),
          PointObj >> x(X),
          PointObj >> y(Y).

it would give X and Y the values of 3.14159 and 2.71828, respectively.

In some cases, the create method might compute the initial values. The following (partial) class definition uses the date/1 predicate from library(date) to initialize its year, month and day slots.

     :- class date_stamp =
     Self <- create :-
             date(date(Year, Month, Day)),
             store_slot(year, Year),
             store_slot(month, Month),
             store_slot(day, Day).

All three slots are private, so it will be necessary to define get methods in order to retrieve the time information. If no put methods are defined, however, the date cannot be modified after the date_stamp object is created (unless some other method for this class invokes store_slot/2 itself).

Create methods can do more than initialize slot values. Consider the named_point class, whose definition begins as follows:

     :- class named_point =
             [public name:atom,
              public x:float=1,
              public y:float=0].
     Self <- create(Name, X, Y) :-
             Self << name(Name),
             Self << x(X),
             Self << y(Y),
             assert(name_point(Name, Self)).

Not only does the create/3 message initialize the slots of a new named_point object, but it also adds a name_point/2 fact to the Prolog database, allowing each new object to be found by its name. (This create method does not require the named_point object to have a unique name. Defining a uniq_named_point class is left as an exercise.)

An object is destroyed with the destroy/1 command. Unlike create/2, destroy/1 does not require that you define a destroy method for a class. However, destroy/1 will send a destroy message (with no arguments) to an object before it is destroyed, if a destroy method is defined for the object's class.

If a named_point object is ever destroyed, the address of the object stored in this name point/2 fact is no longer valid. Hence, there should be a corresponding destroy method that retracts it.

     Self <- destroy :-
             Self >> name(Name),
             retract(name_point(Name, Self)).

Similar create and destroy methods can be defined for objects that allocate their own separate memory or that announce their existence to foreign code.