# Chapter 25: Object-Oriented Programming

## 25.1 Background and Terminology

In this chapter "OOP" will stand for "object-oriented programming". Here is the barest thumbnail sketch of OOP.

On occasion, a program needs to build, maintain and use a collection of related data, where it is natural to consider the collection to be, in some sense, a whole. For example, a "stack" is a sequence of data items, such that the most-recently added item is the first to be removed. If we intend to make much use of stacks, then it might be a worthwhile investment to write some functions dedicated to building and using stacks.

The combination of some data and some dedicated functions is called an object. Every object belongs to some specific class of similar objects. We will say that a stack is an object of the `Stack` class.

The dedicated functions for objects of a given class are called the "methods" of the class. For example, for objects of the `Stack` class we will need a method for adding a new item, and a method for retrieving the last-added item.

An object needs one or more variables to represent its data. Such variables are called fields. Thus for a stack we may choose to have a single field, a list of items.

In summary, OOP consists of identifying a useful class of objects, and then defining the class by defining methods and fields, and then using the methods. By organizing a program into the definitions of different classes, OOP can be viewed as a way of managing complexity. The simple examples which follow are meant to illustrate the machinery of the OOP approach, but not to provide much by way of motivation for OOP.

We will be using a number of library functions. A brief summary of them is given at the end of this chapter.

## 25.2 Defining a Class

### 25.2.1 Introducing the Class

For a simple example, we look at defining a class of Stack objects. A new class is introduced with the library function `coclass`.

       coclass 'Stack'
    +-----+-+
    |Stack|z|
    +-----+-+

`coclass` is used for its effect, not its result. The effect of `coclass` is to establish and make current a new locale called `Stack`. To verify this, we can inspect the name of the current locale:

       coname ''
    +-----+
    |Stack|
    +-----+

### 25.2.2 Defining the Methods

A new object comes into being in two steps. The first step uses library verb `conew` to create a rudimentary object, devoid of fields, a mere placeholder. The second step gives a new object its structure and initial content by creating and assigning values to the field-variables.

We will deal with the first step below. The second step we look at now. It is done by a method conventionally called `create` (meaning "create fields", not "create object"). This is the first of the methods we must define.

For example, we decide that a `Stack` object is to have a single field called `items`, initially an empty list.

       create =: 3 : 'items =: 0 $ 0'

The connection between this method and the `Stack` class is that `create` has just been defined in the current locale, which is `Stack`.

This `create` method is a verb. In this example, it ignores its argument, and its result is of no interest: it is executed purely for its effect. Its effect will be that the (implicitly specified) object will be set up to have a single field called `items` as an empty list.

Our second method is for pushing a new value on to the front of the `items` in a stack.

       push =: 3 : '# items =: (< y.) , items'

The `push` method is a verb. Its argument `y.` is the new value to be pushed. We made a design-decision here that `y.` is to be boxed and then pushed. The result is of no interest, but there must be some result, so we chose to return `(# items)` rather than just `items`.

Next, methods for, respectively, returning and removing the "top" (most-recently added) item on the stack.

       top =: 3 : '> {. items'
       pop =: 3 : '#  items =: }. items'

Finally, a method to "destroy" a `Stack` object, that is, eliminate it when we are finished with it. For this purpose there is a library function `codestroy`.

       destroy =: codestroy

This completes the definition of the `Stack` class. Since we are still within the scope of the `coclass 'Stack'` statement above, the current locale is `Stack`. To use this class definition we return to our regular working environment, the `base` locale.

       cocurrent 'base'

## 25.3 Making New Objects

Now we are in a position to create and use `Stack` objects. A new `Stack` is created in two steps. The first step uses the library verb `conew`.

       S =: conew 'Stack'

The result of `conew` which we assigned to `S` is not the newly-created object itself. Rather, the value of `S` is in effect a unique reference-number which identifies the newly-created `Stack` object. For brevity we will say "Stack `S`" to mean the object referred to by `S`.

Stack `S` now exists but its state is so far undefined. Therefore the second step in making the object is to use the `create` method to change the state of `S` to be an empty stack. Since `create` ignores its argument, we supply an argument of `0`

       create__S 0

Now we can push values onto the stack `S` and retrieve them in last-in-first-out order. In the following, the expression (`push__S 'hello'` means: the method `push` with argument `'hello'` applied to object `S`.

       push__S 'hello'
    1
       push__S 'how are you?'
    2
       push__S 'goodbye'
    3
       pop__S 0
    2
       top__S 0
    how are you?

### 25.3.1 Dyadic Conew

The two steps involved in creating a new object, `conew` followed by `create`, can be collapsed into one using dyadic `conew`. The scheme is that:

                C =: conew 'Class'
                create__C arg

can be abbreviated as:

                C =: arg conew 'Class'

That is, any left argument of `conew` is passed to `create`, which is automatically invoked. In this simple `Stack` class, `create` ignores its argument, but even so one step is neater than two. For example:

       T =: 0 conew 'Stack'
       push__T 77
    1
       push__T 88
    2
       top__T 0
    88

## 25.4 Classes and Objects are Locales

Recall from Chapter 24 that the expression `conl 0` produces a list of existing locales.

       conl 0
    +-----+----+-+-+
    |Stack|base|j|z|
    +-----+----+-+-+

We see that `Stack` is amongst this list, and so a class-definition is a locale. The methods of the `Stack` class are defined in the locale named `Stack`. We can view this locale (using the `view` utility function from the previous chapter.)

       view 'Stack'
    COCLASSPATH =: 'Stack';'z'
    create      =: 3 : 'items =: 0 $ 0'
    destroy     =: codestroy
    pop         =: 3 : '#  items =: }. items'
    push        =: 3 : '# items =: (< y.) , items'
    top         =: 3 : '> {. items'

### 25.4.1 What objects?

The library verb `costate` produces a report showing what objects exist, and their classes. Currently we have variables `S` and `T` each referring to a `Stack` object.

       costate ''
    +----+--+-------+-------+
    |refs|id|creator|path   |
    +----+--+-------+-------+
    |S   |0 |base   |Stack z|
    +----+--+-------+-------+
    |T   |1 |base   |Stack z|
    +----+--+-------+-------+

Now look at the value of `S`. This value is a boxed character string consisting of numeric digits:

       S
    +-+
    |0|
    +-+

This string is the name of a locale (a name consisting only of numeric digits) and this locale contains the fields of object `S`. Thus objects are locales. We can view them:

`S` | `view >S` | `view >T`

    +-+
    |0|
    +-+

    COCREATOR =: <'base'
    items     =: <;._1 '|how are you?|hello'

    COCREATOR =: <'base'
    items     =: 88;77

The numeric names of object locales are shown in the `costate` report above under the heading `id`.

Let us look at the `costate` report again.

       costate ''
    +----+--+-------+-------+
    |refs|id|creator|path   |
    +----+--+-------+-------+
    |S   |0 |base   |Stack z|
    +----+--+-------+-------+
    |T   |1 |base   |Stack z|
    +----+--+-------+-------+

What makes `S` a `Stack` object is that there is a path from the `S` locale to the `Stack` locale. We can inspect this path (also shown in the `costate` report):

`copath S` | `costate ''`

    +-----+-+
    |Stack|z|
    +-----+-+

    +----+--+-------+-------+
    |refs|id|creator|path   |
    +----+--+-------+-------+
    |S   |0 |base   |Stack z|
    +----+--+-------+-------+
    |T   |1 |base   |Stack z|
    +----+--+-------+-------+

Recall from Chapter 24 that, since `S = <'0'` then the expression `push__S 99` means:

1. change the current locale to `'0'`. Now the fields of object `S`, (that is, the the `items` variable of locale `'0'`) are available.

2. apply the `push` verb to argument `99`. Since `push` is not in locale `'0'`, a search is made along the path from locale `'0'` which takes us to locale `Stack` whence `push` is retrieved before it is applied.
3. Restore the current locale to the status quo.

## 25.5 Inheritance

Here we look at how a new class can build on an existing class. The main idea is that, given some class, we can develop a new class as a specialized version of the old class.

For example, suppose there is a class called `Collection` where the objects are collections of values. We could define a new class where, say, the objects are collections without duplicates, and this class could be called `Set`. Then a `Set` object is a special kind of a `Collection` object.

In such a case we say that the `Set` class is a child of the parent class `Collection`. The child will inherit the methods of the parent, perhaps modifying some and perhaps adding new methods, to realize the special properties of child objects.

For a simple example we begin with a parent-class called `Collection`,

       coclass 'Collection'
    +----------+-+
    |Collection|z|
    +----------+-+
       create  =: 3 : 'items =: 0 $ 0'
       add     =: 3 : '# items =: (< y.) , items'
       remove  =: 3 : '# items =: items -. < y.'
       inspect =: 3 : 'items'
       destroy =: codestroy

Here the `inspect` method yields a boxed list of all the members of the collection.

A quick demonstration:

       cocurrent 'base'
       C1 =: 0 conew 'Collection'
       add__C1 'foo'
    1
       add__C1 37
    2
       remove__C1 'foo'
    1
       inspect__C1 0
    +--+
    |37|
    +--+

Now we define the `Set` class, specifying that `Set` is to be a child of `Collection` with the library verb `coextend`.

       coclass 'Set'
    +---+-+
    |Set|z|
    +---+-+
       coextend 'Collection'
    +---+----------+-+
    |Set|Collection|z|
    +---+----------+-+

To express the property that a `Set` has no duplicates, we need to modify only the `add` method. Here is something that will work:

       add =: 3 : '# items =: ~. (< y.) , items'

All the other methods needed for `Set` are already available, inherited from the parent class `Collection`. We have finished the definition of `Set` and are ready to use it.

       cocurrent 'base'
       s1 =: 0 conew 'Set'  NB. make new Set object.
       add__s1 'a'
    1
       add__s1 'b'
    2
       add__s1 'a'
    2
       remove__s1 'b'
    1
       inspect__s1 0        NB. should have just one 'a'
    +-+
    |a|
    +-+

### 25.5.1 A Matter of Principle

Recall the definition of the `add` method of class `Set`.

       add_Set_
    +-+-+----------------------------+
    |3|:|# items =: ~. (< y.) , items|
    +-+-+----------------------------+

It has an objectionable feature: in writing it we used our knowledge of the internals of a `Collection` object, namely that there is a field called `items` which is a boxed list.

Now the methods of `Collection` are supposed to be adequate for all handling of `Collection` objects. As a matter of principle, if we stick to the methods and avoid rummaging around in the internals, we hope to shield ourselves, to some degree, from possible future changes to the internals of `Collection`. Such changes might be, for example, for improved performance.

Let's try redefining `add` again, this time sticking to the methods of the parent as much as possible. We use our knowledge that the parent `inspect` method yields a boxed list of the membership. If the argument `y.` is not among the membership, then we add it with the parent `add` method.

       add_Set_ =: 3 : 0
    if. (< y.) e. inspect 0
    do.  0
    else. add_Collection_ f. y.   NB. see below !
    end.
    )

Not so nice, but that's the price we pay for having principles. Trying it out on the set `s1`:

       inspect__s1 0
    +-+
    |a|
    +-+
       add__s1     'a'
    0
       add__s1     'z'
    2
       inspect__s1 0
    +-+-+
    |z|a|
    +-+-+

## 25.6 Using Inherited Methods

Let us review the definition of the `add` method of class `Set`.

       add_Set_
    +-+-+---------------------------------------------+
    |3|:|if. (< y.) e. inspect 0                      |
    | | |do.  0                                       |
    | | |else. add_Collection_ f. y.   NB. see below !|
    | | |end.                                         |
    +-+-+---------------------------------------------+

There are some questions to be answered.

### 25.6.1 First Question

How are methods inherited? In other words, why is the `inspect` method of the parent `Collection` class available as a `Set` method? In short, the method is found along the path, that is,

- a `Set` object such as `s1` is a locale. It contains the field-variable(s) of the object.
- when a method of a class is executed, the current locale is (temporarily) the locale of an object of that class. This follows from the way we invoke the method, with an expression of the form `method__object argument`.
- the path from an object-locale goes to the class locale and thence to any parent locale(s). Hence the method is found along the path.

. We see that a `Set` object such as `s1` has a path to `Set` and then to `Collection`.

       copath > s1
    +---+----------+-+
    |Set|Collection|z|
    +---+----------+-+

### 25.6.2 Second Question

In the definition of `add_Set_`

       add_Set_
    +-+-+---------------------------------------------+
    |3|:|if. (< y.) e. inspect 0                      |
    | | |do.  0                                       |
    | | |else. add_Collection_ f. y.   NB. see below !|
    | | |end.                                         |
    +-+-+---------------------------------------------+

Given that the parent method `inspect` is referred to as simply `inspect`, why is the parent method `add` referred to as `add_Collection_`? Because we are defining a method to be called `add` and inside it a reference to `add` would be a fatal circularity.

### 25.6.3 Third Question

why is the parent `add` method specified as `add_Collection_ f.` ?

Because `add_Collection_` is a locative name, and evaluating expressions with locative names will involve a change of locale. Recall from Chapter 24 that `add_Collection_ 0` would be evaluated in locale `Collection`, which would be incorrect: we need to be in the object locale when applying the method.

Since `f.` is built-in, by the time we have finished evaluating `(add_Collection_ f.)` we are back in the right locale with a fully-evaluated value for the function which we can apply without change of locale.

Would not some other adverb, say the identity- adverb `]:` do instead of `f.`? No, because `]:` does not evaluate its argument - its result is still a locative.

`add_Collection_ f.` | `add_Collection_ ]:`

    +-+-+-------------------------+
    |3|:|# items =: (< y.) , items|
    +-+-+-------------------------+

    +---------------+
    |add_Collection_|
    +---------------+

## 25.7 Library Verbs

Here is a brief summary of selected library verbs.

`coclass 'foo'` | introduce new class `foo`
`coextend 'foo'` | this class to be a child of `foo`
`conew 'foo'` | introduce a new object of class `foo`
`conl 0` | list locale names
`conl 1` | list ids of object locales
`costate ''` | list objects and classesS
`names_foo_ ''` | list the methods of class `foo`
`copath <'foo'` | show path of class `foo`
`codestroy ''` | method to destroy this object
`copathnlx__`o`` | show field-names in object `o`
`coname ''` | show name of current locale

This brings us to the end of Chapter 25

NEXT
Table of Contents

Copyright © Roger Stokes 2002. This material may be freely reproduced, provided that this copyright notice is also reproduced.

last updated 15 Mar 2002