Display Classes


Objects and Classes

A+ applications are constructed from variables, bound and free. A bound variable, or object, is a global variable which has been tied to a display class through the is{} function:
     a10
`a is `array
The display class specifies the general form of how the variable looks on the screen. Some of these characteristics, or attributes, can be changed through use of the has{} function:
     `a has (`rows;10;`fg;`blue)
But even prior to binding, a variable has attributes. These include both the primitive A+ attributes of shape, type, and depth, which are properties of the data, and certain further attributes, such as foreground color, font, and callback on assignment. Together, these variable attributes are termed persistent, since they are attached to variables and survive re-binding to different display classes. By contrast, the attributes of any particular display class come and go with binding and freeing of global variables.

   Subclasses

There are several kinds of hierarchical relation in A+:

The first two relations have been described in "
Containers" and "Leaders and Followers". The topic of the present chapter is how to use the subclass relation.

A class is, minimally, a set of attributes. An object which is an instance of a particular class has those attributes. For example, a variable which "is" a table, "has" a certain number of fixed fields. The class `table includes the fixedfields attribute.

The attributes which belong exclusively to a class are called the proper attributes of that class. In general, a variable which has been bound to a class has many more attributes than are proper to that class, because the attributes of a class consist of the attributes proper to that class as well as the attributes of its superclasses, which it is said to inherit.

For example, a variable which has been bound to the `table class also has a background color `bg, which is not a proper attribute of `table. It acquires `bg by inheriting it from one of its superclasses, `real.

The class hierarchy of `table is shown in the figure; compare this display with the result of the function s.superClasses{`table}.

A variable bound to `table has all the attributes of every class in this tree, since `table inherits all the attributes of its superclasses.

A Class Hierarchy:

   Metaclasses

None of the superclasses of `table can be instantiated. That is, one cannot say:
     a10
     `a is `real
The `real class is simply a convenient place to put certain attributes which many subclasses can access though inheritance. Classes such as this, which cannot be instantiated, are called metaclasses, and in A+ they significantly outnumber the classes which can be instantiated. The set of metaclasses of A+ is enumerated by the niladic function s.metaClasses:
     s.metaClasses{}
 `object `virtual `print ...

   Virtual Classes

Not all of the instantiable classes in A+ can be directly instantiated. For example, one cannot say:
     a10
     `a is `tableField
But one can say:
     t`a
     `t is `table
     `class of `a
< `tableField
Classes such as `tableField can only be instantiated by binding a variable to some other class, in this case `table; that variable's children are then implicitly bound to the class in question. The `tableField class is called virtual, and A+ contains two such classes:
     s.virtualClasses{}
 `tableField `graphTrace
whose virtual parents are `table and `graph, respectively.

   Real Classes

A class which can be directly instantiated is called "real". The set of real classes of A+ is enumerated by the niladic function s.realClasses:
     s.realClasses{}
 `slot `check `radio ...

   Simple Subclassing

Suppose one has an application in which there are two kinds of arrays: A-arrays and B-arrays. And suppose that the application creates multiple instances of both kinds of array.

A-arrays should always have the following characteristics:
bg: green
fg: black
font: helvetica-bold-18

and B-arrays:
bg: black
fg: cyan
font: courier-bold-14

Define two subclasses of array, thus:

     `aArray s.isClass `array
     `bArray s.isClass `array
and set the appropriate class defaults:
     `aArray s.hasDefault (
          `bg;     `green;
          `fg;     `black;
          `font;   'helvetica-bold-18'
          )
and
     `bArray s.hasDefault (
          `bg;     `black;
          `fg;     `cyan;
          `font;   'courier-bold-14'
          )
So that:
     a10
     `a is `aArray
     b3 4
     `b is `bArray
     `class of `a
 < `aArray
     `class of `b
 < `bArray
In this case, subclassing allows the application to dispense with attribute bookkeeping.

Gadgets

   As One of a Kind Objects

Consider the following "gadget":
     file{s;c;v}:
       {
       (c%`FILE)(t' ')/tDir,'/',(0`row of c,v)#c%v
       }
     files{dir}:
       {
       t@1 matsys.agetdents{dir};
       t((t=' ')@10)@0 1 t;
       (t[;2]@1 'm.')/mat
       }
     Dir''
     Files:files{Dir}
     Filepick(`Dir;`Files)
     `Files is `array
     `Dir is `scalar
     `Filepick is `layout
     `Files has (`protect;1; `refer;file)
     show `Filepick
Entering a directory name displays the .m files in that directory. Referring to an item in the display list stores that item in the global FILE. By setting the appropriate callback on FILE, we can use this gadget as a tool for displaying, printing, editing, or otherwise using the referenced file.

   As Generator Functions

The single most important reason for recoding such a structure as a class is that we wish to be able to create multiple instances of it. Our first step in this direction should therefore be to define a function which generates such structures. Continuing the above example:
     filepicker{cx}:
       {
       (cx%`Dir)'';
       (cx%`Files)'';
       (cx,`Files) has (`def;'.files{',(cx),'.Dir}');
       (cx%`Filepick)(`Dir;`Files);
       (cx,`Files) is `array;
       (cx,`Dir) is `scalar;
       (cx,`Filepick) is `layout;
       (cx,`Files) has (`protect;1; `refer;file);
       cx,`Filepick
       }
Note that filepicker{} returns the symbol of the generated gadget, so that
     show filepicker{`my}
instantiates a filepicker in context `my.

   As Composite Classes

Here is an example of how the filepicker could be defined as a composite class, and then used as the control panel for a simple file editor. Continuing from above:

Filepicker instantiation function:

     newFp{s;c;v}:
       {
       (d;f)c%v;
       (c,f) has (`def;'.files{',(c),'.',(d),'}');
       (c%d)'';
       (c,f) has (`protect;1);
       }
Composite class has layout structure and new function:
     `fp s.isClass (`scalar;`array);
     `fp s.hasDefault (`new;newFp);
Reference function for file list:
     fileEdit{s;c;v}:
            {
            dir(dir' ')/dirc%0%0`parent of c,v;
            fil(fil' ')/fil(0`row of c,v)#c%v;
            (c%`Edit)1nam(if (0<#dir) dir,'/'),fil;
            (c,`Edit) has (`title;nam);
            show c,`Edit
            }

     Edit''
     `Edit is `array
     DirectoryFiles''
     EditFile(`Directory;`Files)
     `EditFile is `fp
     `Files has (`refer;fileEdit)
     show `EditFile
The right argument to isClass{} is a schema which obeys the semantics of (nested) layouts. The symbols in the schema are class names. So `fp will be a `layout, with a `scalar above and an `array below.

The `new function newFp{} is called when a new instance of `fp is created. newFp{} retrieves the symbols of the children of the new `fp, redefines the file list in terms of the directory variable, which it initializes, and sets protection and reference functions on the file list.

Rudimentary structural error checking is performed:

     uvw''
     z(`u `v;`w)
     show `z is `fp
 !! .z: variable cannot be bound to fp

Extending the Class Hierarchy

The decision to use A+ subclasses rather than write functional gadget generators rests entirely with the programmer. Subclasses hide detail and guarantee uniformity of approach across applications, but can be tricky to design. Gadget generators fit more comfortably into the experienced A+ programmer's bag of tricks, but cannot exploit the inheritance and attribute-management mechanisms which subclasses provide. As an example, consider how the file editor application of the `fp class in the above example can itself be reconstructed as a subclass:

File editor instantiation function:

     newEfp{s;c;v}:
       {
       (d;f)c%v;
       (c,f) has (`def;'.files{',(c),'.',(d),'}');
       (c%d)'';
       (c,f) has (`protect;1; `refer;fileEdit);
       (c%`Edit)'';
       (c,`Edit) is `array;
       }
File editor subclass and (overriding) new function:
     `efp s.isClass `fp
     `efp s.hasDefault (`new;newEfp)
     DirectoryFiles''
     EditFile(`Directory;`Files)
     `EditFile is `efp
     show `EditFile;

   Synthetic Attributes

Making composite objects through the use of subclasses hides detail. In particular, we may wish to refer to attributes of the components through new synthetic attributes of the composite object. For example, in the composite layout of three arrays `array3, we may wish to be able to set the background colors of the components without specifically addressing them through has{}:
     `array3 s.isClass (`array;`array;`array)
     g_bg{var}:`bg of(cx,cx%var)  (cx;var)var
     s_bg{var;x}:{
           (cx,cx%var) has(`bg,x)  (cx;var)var}
     `array3 s.classHas (`bg3;(g_bg;s_bg))

     abc3
     d(`a;`b;`c)
     show `d is `array3

     `d has (`bg3;`red `blue `green)

     `bg3 of `d
< `red `blue `green

     `bg3 s.ofClass `array3
< < g_bg
  < s_bg
To eliminate a synthetic attribute:
     `d has (`bg3;)

   Subclassing from Virtual Containers

The children of `table and `graph are virtual objects, viz., `tableField and `graphTrace. Consider the case:
     `xTable s.isClass `table
     a10
     b`a
     `b is `xTable
     `class of `b
< `xTable
     `class of `a
< `tableField
Since `xTable is a subclass of `table, its children are of class `tableField. Suppose, however, that we wish to define a new kind of table, whose children are themselves a subclass of `tableField:
     `yTableField s.isClass `tableField
     `yTable `yTableField s.isClass `table
     a10
     b`a
     `b is `yTable
     `class of `b
< `yTable
     `class of `a
< `yTableField
The left argument to isClass{} can be a simple vector of count 2, in which case the second symbol specifies the class of virtual children.

   Metaclasses and Multiple Inheritance

A metaclass is a class defined strictly for the purpose of naming a set of attributes. Metaclasses cannot be instantiated.

For example, consider the case where one wishes to define subclasses `subtable, `subslot, `subarray, `subscalar of `table, `slot, `array, and `scalar (the input objects of A+) all of which make use of some set of attributes on input `r1, `r2, and `r3. Suppose, moreover, that `s1 and `s2 are strictly attributes of `subtable and `subslot, `t1 and `t2 of `subtable and `subarray, and `u1 and `u2 of `subarray and `subscalar:

r1r2r3s1s2t1t2 u1u2
subtable+++++++   
subslot+++++     
subarray+++  + +++
subscalar+++     ++

Let us then define the metaclass `r as a subclass of `object, the most general metaclass in A+:

     `r s.isClass `object
and add to `r the attributes `r1, `r2, and `r3:
     `r s.classHas (
           `r1;(g_r1;s_r1);
           `r2;(g_r2;s_r2);
           `r3;(g_r3;s_r3)
           )
Similarly, the metaclasses `s, `t, and `u to name their respective sets of attributes:
     `s s.isClass `object
     `s s.classHas (
           `s1;(g_s1;s_s1);
           `s2;(g_s2;s_s2);
           )
     `t s.isClass `object
     `t s.classHas (
           `t1;(g_t1;s_t1);
           `t2;(g_t2;s_t2);
           )
     `u s.isClass `object
     `u s.classHas (
           `u1;(g_u1;s_u1);
           `u2;(g_u2;s_u2);
           )
Finally, the subclass definitions:
     (`subtable;`r `s `t) s.isClass `table
     (`subslot;`r `s) s.isClass `slot
     (`subarray;`r `t `u) s.isClass `array
     (`subscalar;`r `u) s.isClass `scalar
If the left argument of isClass{} is nested, then the second item is a vector of metaclasses.

Also note that if v is a vector of metaclasses mentioned in the left argument, and `a is an attribute of several classes which appear in v, then the g_ and s_ functions which actually attach to the subclass being defined are the ones which attach to the class which appears earliest in the vector. More generally, if `a appears as an attribute in the class from which subclasses are being derived, then the g_ and s_ functions of the superclass override those of `a in the specified metaclass vector (if any).


doc@aplusdev.org© Copyright 1995–2008 Morgan Stanley Dean Witter & Co. All rights reserved.