Overview

Kixforms is an object-oriented extension to the Kixtart scripting language and is based on objects, rather than being class-based. Because of this difference, it can be less apparent how one creates hierarchies of objects, and to have inheritance of properties and their values. This aritcle attempts to clarify the situation.

This article assumes that you are already familiar with Kixforms and the Kixtart scripting language, and that you have used both to create scripts.

Class-Based vs. Prototype-Based Languages

Class-based object-oriented languages, such as Java and C++, are founded on the concept of two distinct entities: classes and instances. A class defines all of the properties that characterize a certain set of objects. A class is an abstract thing, rather than any particular member of the set of objects it describes. For example, the Employee class could represent the set of all employees. An instance, on the other hand, is the instantiation of a class; that is, one of its members. For example, Victoria could be an instance of the Employee class, representing a particular individual as an employee. An instance has exactly the properties of its parent class (no more, no less).

A prototype-based language does not make this distinction: it simply has objects. A prototype-based language has the notion of a prototypical object, an object used as a template from which to get the initial properties for a new object. An object can specify its own properties when you create it at run time. In addition, an object can be associated as the prototype for another object, allowing the second object to share the first object's properties.

It is important to note that Kixtart is not an object-oriented scripting language. There is no built-in support for classes or objects or even prototype-based objects. However Kixtart does support the Component Object Model (COM) and the ability to create and use external objects via COM Automation. Kixforms extends the paradigm by allowing you to create a prototypical (template) object that can be customized (extended) from within Kixtart.

Defining a Class

In class-based languages, you define a class in a separate class definition. In that definition you can specify special methods, called constructors, to create instances of the class. A constructor method can specify initial values for the instance's properties and perform other processing appropriate at creation time. You use the new operator in association with the constructor method to create class instances.

Kixtart/Kixforms follows a similar model, but does not have a class definition separate from the constructor. Instead, you define a constructor function to create objects with a particular initial set of properties and values. Any function can be used as a constructor.

Subclasses and Inheritance

In a class-based language, you create a hierarchy of classes through the class definitions. In a class definition, you can specify that the new class is a subclass of an already existing class. The subclass inherits all the properties of the superclass and additionally can add new properties or modify the inherited ones. For example, assume the Employee class includes only the name and department properties, and Manager is a subclass of Employee that adds the reports property. In this case, an instance of the Manager class would have all three properties: name, department, and reports.

Kixtart/Kixforms implements inheritance by allowing you to associate a prototypical object with any constructor function. So, you can create exactly the Employee- Manager example, but you use slightly different terminology. First you define the Employee constructor function, specifying the name and department properties. Next, you define the Manager constructor function and assign a new Employee object as the prototype for the Manager constructor function. Finally, you specifying the reports property. Then, when you create a new Manager, it inherits the name and department properties from the Employee object.

Dynamic (Expando) Properties

In class-based languages, you typically create a class at compile time and then you instantiate instances of the class either at compile time or at run time. You cannot change the number or the type of properties of a class after you define the class. With Kixforms however, at run time you can add properties to an object. If you add a property to an object that is used as the prototype for a set of objects, the objects for which it is the prototype also get the new property.

The Employee Example

The remainder of this section uses a simple employee hierarchy described using the following objects:

Creating the Hierarchy

There are several ways to define appropriate constructor functions to implement the Employee hierarchy. How you choose to define them depends largely on what you want to be able to do in your application. This section shows how to use very simple (and comparatively inflexible) definitions to demonstrate how to get the inheritance to work. In these definitions, you cannot specify any property values when you create an object. The newly-created object simply gets the default values, which you can change at a later time. The following code examples demonstrate how the inheritance occurs.

function Employee() $Employee = $System.Object() $Employee.name = "" $Employee.department = "general" endfunction

The Manager and WorkerBee definitions show the difference in how to specify the next object higher in the inheritance chain. You add a prototypical instance as the value of the constructor function. Then initialize the properties that are unique to the object:

function Manager() $Manager = Employee() $Manager.reports = "" endfunction function WorkerBee() $WorkerBee = Employee() $WorkerBee.projects = "" endfunction

The Engineer and SalesPerson definitions create objects that descend from WorkerBee and hence from Employee. An object of these types has properties of all the objects above it in the chain. In addition, these definitions override the inherited value of the department property with new values specific to the objects.

Function SalesPerson() $SalesPerson = WorkerBee() $SalesPerson.department = "Sales" $SalesPerson.Quota = 100 EndFunction Function Engineer() $Engineer = WorkerBee() $Engineer.department = "Engineering" $Engineer.machine = "" EndFunction

Using these definitions, you can create instances of these objects that get the default values for their properties. The following example illustrates using these definitions to create new objects and shows the property values for the new objects:

$jim = Employee() $jim.name is "" $jim.department is "general" $sally = Manager() $sally.name is "" $sally.department is "general" $sally.reports is "" $mark = WorkerBee() $mark.name is "" $mark.department is "general" $mark.projects is "" $fred = SalesPerson() $fred.name is "" $fred.department is "sales" $fred.projects is "" $fred.quota is 100 $jane = Engineer() $jane.name is "" $jane.department is "engineering" $jane.projects is "" $jane.machine is ""

The term instance has a specific technical meaning in class-based languages. In these languages, an instance is an individual member of a class and is fundamentally different from a class.

In Kixforms, "instance" does not have this technical meaning because Kixforms does not have this difference between classes and instances. However, in talking about Kixforms, "instance" can be used informally to mean an object created using a particular constructor function. So, in the previous example, you could informally say that Jane is an instance of Engineer. Similarly, although the terms parent, child, ancestor, and descendant do not have formal meanings in Kixforms; you can use them informally to refer to objects higher or lower in the prototype chain.

Inheriting Properties

This section discusses how objects inherit properties from other objects in the prototype chain and what happens when you add a property at run time. Suppose you create the mark object as a WorkerBee as shown in the following statement:

$mark = WorkerBee()

When Kixtart processes the WorkerBee constructor function, it first creates a new generic Employee object and assigns this object as the return value of the WorkerBee contructor. The constructor function then explicitly sets the value of the projects property. Once these properties are set, Kixtart returns the new object and the assignment statement sets the variable $mark to that object.

function WorkerBee() $WorkerBee = Employee() $WorkerBee.projects = "" endfunction

This process does not explicitly put values in the mark object (local values) for the properties mark inherits from the prototype chain. The mark object is simply an amalgamation of all the properties it inherits through it's prototype chain. When you ask for the value of a property, Kixforms checks to see if the property exists in that object. If it does, the value is returned. If the property does not exist, an empty reference is returned. In this way, the mark object has the following properties and values:

$mark.name is "" $mark.department is "general" $mark.projects is ""

Because these constructors do not let you supply instance-specific values, this information is generic. The property values are the default ones shared by all new objects created from WorkerBee. You can, of course, change the values of any of these properties. So, you could give specific information for mark as follows:

$mark.name = "Mark Smith" $mark.department = "admin" $mark.projects = "navigator","explorer"

Adding Properties

You can add properties to an object at run time. You are not constrained to use only the properties provided by the constructor function. To add a property that is specific to a single object, you assign a value to the object, as follows:

$mark.bonus = 3000

Now, the mark object has a bonus property, but no other WorkerBee has this property.

If you add a new property to an object that is being used as the prototype for a constructor function, you add that property to all objects that subsequently inherit from that prototype. For example, you can add a specialty property to all employees with the following statement:

function Employee() $Employee = $System.Object() $Employee.name = "" $Employee.department = "general" $Employee.specialty = "none" endfunction

In the future, any object that derives from the Employee object also has the specialty property with the value of "none". The following example shows the effect of adding this property to the Employee prototype and then overriding it in the Engineer prototype.

function Engineer() $Engineer = WorkerBee() $Engineer.department = "engineering" $Engineer.machine = "" $Engineer.specialty = "code" endfunction

More Flexible Constructors

The constructor functions shown so far do not let you specify property values when you create an instance. As with other object-oriented langauges, you can provide arguments to constructors to initialize property values for instances. The following example shows one way to do this:

function Engineer($machine) $Engineer = WorkerBee() $Engineer.department = "engineering" $Engineer.machine = $machine endfunction

An even more flexible strategy allows for optional parameters to be passed as initial values to the constructor function, for example:

function Engineer(optional $machine) $Engineer = WorkerBee() $Engineer.department = "engineering" $Engineer.machine = iif($machine, $machine, "") endfunction

This example uses a special idiom for setting default values:

$Engineer.machine = iif($machine, $machine, "")

The IIF function evaluates its first argument. If that argument evaluates true, the function returns the value of its second argument. Otherwise, the function returns the value of its third argument. Therefore, this line of code tests to see if $machine has a useful value for the machine property. If it does, it sets machine property to that value. Otherwise, it sets the machine property to the empty string. This topic uses this idiom for brevity; however, it can be puzzling at first glance.

With these definitions, when you create an instance of an object, you can specify values for the locally defined properties. For example, you can now use either of the following statements to create a new Engineer:

$jane = Engineer() $jane = Engineer( "lathe" )

Jane's properties are now:

$jane.name is "" $jane.department is "general" $jane.projects is "" $jane.machine is "lathe"

Notice that with these definitions, you cannot specify an initial value for an inherited property such as name. If you want to specify an initial value for inherited properties you need to add more code to the constructor function:

function Engineer( optional $name, optional $machine ) $Engineer = WorkerBee() $Engineer.name = iif( $name, $name, "" ) $Engineer.department = "engineering" $Engineer.machine = iif( $machine, $machine, "" ) endfunction

However, a more modular and object-oriented approach is to allow for optional constructor properties throughout the prototype chain, then pass any base properties directly on to the base prototype in the constructor. This will allow for assigning defaults and any special processing that must occur at the lower levels. The following example shows these new definitions:

function Employee( optional $name, optional $department ) $Employee = $System.Object() $Employee.name = iif( $name, $name, "" ) $Employee.department = iif( $department, $department, "" ) endfunction function WorkerBee( optional $name, optional $department, optional $projects ) $WorkerBee = Employee($name, $department) $WorkerBee.projects = iif($projects, $projects, "") endfunction function Engineer(optional $name, optional $projects, optional $machine) $Engineer = WorkerBee($name, "engineering", $projects) $Engineer.machine = iif($machine, $machine, "") endfunction

Let's look at one of these definitions in detail. Suppose you create a new Engineer object as follows:

$jane = Engineer("Doe, Jane", "kixtart | javascript", "belau")

Refering back to the code, the Engineer constructor calls its WorkerBee base constructor, passing as its arguments two of the arguments passed to the constructor ("Doe, Jane" and "kixtart | javascript") and also the string "engineering". Explicitly using "engineering" in the constructor indicates that all Engineer objects have the same value for the inherited department property, and this value overrides the value inherited from Employee.

The WorkerBee constructor in turn passes the "Doe, Jane" and "engineering" arguments to the Employee constructor function. Upon return from the Employee constructor function, the WorkerBee function uses the remaining argument to set the projects property.

Upon return from the base constructor, the Engineer constructor initializes the object's machine property to "belau".

Upon return from the constructor, Kixtart assigns the new object to the $jane variable.


The CHM file was converted to HTML by chm2web software.