J release 4.01 has extensions to locales that not only enhance their utility in general, but in particular make OOP easy in J.
A prime motivation for the new extensions was the development of the new Grid Control. The underlying primitives in J4 allow you to follow the OOP model exactly, as has been done for the Grid Control. However the new facilities are of general applicability, and so powerful and easy to use that we expect them to dramatically change the way we build systems in J.
For a good introduction to the ideas behind the new locale extensions and OOP, see the labs Locales and Object Oriented Programming included in the J4 system.
The design of the new locales, and the development of the Grid Control, was largely the work of Eric Iverson.
In this article, I will illustrate with a simple example, then discuss the new language facilities, and finally show how they are used in the Grid Control.
Example: Plot Package
Let's begin with a problem: how to create multiple plot windows with the plot package.
Suppose you draw a plot, as follows:
load 'plot' plot i.10
Even a simple plot like this generates a host of globals describing the plot - the labels, the axis placements and so on - in fact, about 140 of them. In J3, in order to avoid name conflicts, the code is run in a single locale, which has about 500 definitions in total for the plot system.
If you now try a different plot, with:
plot sin i.10
this redraws the plot in the same window. The code is run in the same locale and simply overwrites the plot globals.
What if, instead of redrawing the plot, you wanted to create another plot window? In J3, you would have to load the entire plot system into another locale and run it from there; and similarly if you wanted to attached a plot to your own form.
Clearly, the use of locales helps - without them you would have to draw both plots in the same namespace - but there is unnecessary duplication of code in the two locales.
J4 permits an easy solution to this. You can still draw plots in exactly the same way as in J3, but if you want to draw two plots, you can now do so by creating two plot objects, as follows:
load the plot class|
since it also loads the plot class
(Read jzplot as "e j "e meaning a J system facility, followed by "e z "e meaning a basic class, followed by the name "e plot "e.)
a=. conew 'jzplot'
create plot object
b=. conew 'jzplot'
create plot object
coprefix stands for class/object. In J, you can use the OOP term class to refer to the script defining the class, or to the definitions once the script has been loaded in; and use the OOP term object to refer to the locales created by
conew, i.e. numbered locales as described below.)
draw plot in
plot__b sin i.10
draw plot in
bcontain only the globals specific to each plot; while the main plot code is stored elsewhere, in
jzplot. Thus J4 trivially permits more than one plot to be created, while not duplicating the main plot code.
Now, the problem solved relates only to the separate sets of globals defining the plots. But exactly the same mechanism permits customization of the code for one plot, leaving the other unchanged.
For example, the verb
drawlines specifies the lines to be drawn; if you wanted to create a customized version for the plot in
a, you could define, for example:
Of course a problem of this type (creating multiple plot windows) can be solved in other ways. But the point is that in J4, it is completely trivial, whereas before, a fair amount of work would be involved in setting up a suitable mechanism.
New Locale Facilities
There are essentially three new facilities, which are upward compatible with J3:
In contrast, in J3:
Locales in J3 had a simple definition that worked surprisingly well; and the new facilities are in turn, pretty simple, and fit nicely on top of the old structure.
I was amused to see that in my previous Vector article on Locales in J (Vol 11 No. 4), I specifically mentioned two of the new facilities (locale paths and switching locale under program control); and said they could be provided as an extension to the current system, but seemed more trouble than they were worth! But it is just as well that we did not add them then - likely without a compelling reason such as the OOP definition of the new Grid Control, we would have got it wrong!
Now lets see how these new facilities are used in the plot example above:
This loads the plot definitions from a script file. At the beginning of the script is a command to switch the locale to jzplot, and the plot definitions are then loaded into that locale. In J3, you could load a script into a specific locale (giving the locale name as the left argument to
load), but in this case, it only makes sense to load jzplot into a locale of that name, hence it is better to specify the locale in the script itself.
In common with all new locales, the jzplot locale has a path that points to the
z locale only.
a=. conew 'jzplot
This creates a new locale (object), sets its path to point to
jzplot and then to
z, and returns the locale reference in variable
a. Locales created by
conew have a single definition
COCREATOR, which identifies the creator locale, but otherwise they are empty.
plot__a (with 2 underscores) can be read "e plot in a "e, or "e plot in indirect a"e. Informally, we can talk of locale
a, with the understanding that this is the locale referenced by
In evaluating the expression
plot__a i.10, the system searches the locale path, finds the plot definitions in
jzplot, and executes them in locale
a. All global definitions created will then be defined in
Note that the name of the locale created by
conew has not been used, only its reference. To make this clear, compare this with what you have to do in J3, where the name itself had to be used. First, you would have to create a suitable name for the temporary locale. Then to use it, you would need an execute statement, for example:
|temporary locale name|
"e. 'plot_',a,'_ i.10'
|execute to use the locale name|
Since, in general, the names generated by
conew are not used directly, they are chosen to avoid any likely name conflict, and in fact are simply character representations of the integers. Thus in a new session,
a would be
b would be
'1'. We call these numbered locales. The numbers are never reused in a session.
The new Grid Control in J4 is for the most part written in J. The display is an isigraph control, and there are some extra
wd (Window Driver) facilities to enable drawing a grid in the control.
There is a great deal of J code, and it needs to be organized. As with other facilities, it should be defined in a locale to avoid name conflicts. But it also must be easy to extend and customize; and easy to run multiple grids. The new language extensions are a natural for this, and the definition exactly follows the OOP model.
The Grid Control is more complex than Plot, in that there is much greater use of event handlers (e.g. for scrolling and keyboard entry), and we would expect a much greater customization of the basic facilities. To that end, the control is implemented in several classes.
The basic grid class is jzgrid, and this provides facilities that should be required for any grid, including:
setting attributes, e.g. font, color, border, cell size
While the jzgrid class could in theory be used directly (for a very simple grid), there would typically be another class on top of the basic grid class that provides a specific type of grid, for example class jwgrid. This would include data-related definitions such as providing formatted data for the grid and support for copying and editing. Also, yet another class is needed for the Windows form definition.
For example, provided with J4 are three specific grids:
The following is a typical session:
dat=. ?30 30$100
|data to be watched|
|load the jwatch class|
a=. conew 'jwatch'
|creates a watch object|
Let's examine what happens in detail.
This loads the script (class) jwatch into the jwatch locale, which in turn loads the scripts jwgrid and jinput, each into their own locale. jwgrid in turn loads the basic grid script, jzgrid. At this stage, all that has happened is that these scripts have been loaded, and the locale namelist looks like:
conl'' +----+-+------+------+------+------+-+ |base|j|jinput|jwatch|jwgrid|jzgrid|z| +----+-+------+------+------+------+-+
No locale path settings have been made, so each locale has a path of z, as is the case with all new locales.
a=. conew 'jwatch'
This now creates a new locale, referenced by
a and named
conl'' +-+----+-+------+------+------+------+-+ |0|base|j|jinput|jwatch|jwgrid|jzgrid|z| +-+----+-+------+------+------+------+-+
The new locale is empty, but has a locale path that includes jwatch and z.
create__a is called in the new locale, and gets its definition from jwatch. This verb creates a new form to display the grid, and then itself calls
conew to create a jwgrid object and sets its path to include jwgrid and jzgrid:
Therefore the locale list now includes two numbered locales:
conl'' +-+-+----+-+------+------+------+------+-+ |0|1|base|j|jinput|jwatch|jwgrid|jzgrid|z| +-+-+----+-+------+------+------+------+-+
The paths in these two numbered locales are:
copath a locale '0' +------+-+ |jwatch|z| +------+-+ copath grid__a locale '1' +------+------+-+ |jwgrid|jzgrid|z| +------+------+-+
The structure of numbered locales is returned by
costate'' +----+--+-------+---------------+ |refs|id|creator|path | +----+--+-------+---------------+ |a |0 |base |jwatch z | +----+--+-------+---------------+ | |1 |0 |jwgrid jzgrid z | +----+--+-------+---------------+
Now suppose the mouse is clicked on one of the grid cells. The form is defined in locale
a, so the mouse event handler
watch_grid_mbldown will be called there. Since this locale has no event handlers of its own, the definition in the
jwatch locale is used. This is defined as:
watch_grid_mbldown=: 3 : 'mbldown__grid sysdata'
Therefore the actual handler called is
mbldown in the
grid locale, which will pick its definition up from either
jzgrid, whichever it sees first.
At first sight, these may seem like a lot of mechanism to set up a form with its event handlers. But the point is that each of the classes loaded can be customized independently (except perhaps for jzgrid which should work for all grids). Therefore, this provides a simple and effective way of utilizing code modules in a variety of applications, and avoiding name conflicts between two instances of an application.
OOP is easy in J!
Example Report grid