Skip to content

Locales in J

Vector 11:4, page 49

language design

Transcribed from the printed issue; not yet reviewed. Read it in the PDF of the issue, page 49

Introduction

This note describes the locales facility in J Release 2.

Locales allow you to partition the names in use. The main benefits are:

  • name conflicts are avoided when loading two applications at the same time
  • commonly-used utility functions can be made available without their names cluttering up listings of work in progress

Similar facilities are provided by Dyalog APL’s namespaces, though the design is quite different.

Locales were introduced in J Version 7, and enhanced in Release 2 to allow for a parent/child structure — a small change that provided a dramatic improvement in functionality. The new definition is simple and very effective.

Locales are created and deleted automatically — you create a locale by defining an object in it, and delete a locale by deleting all objects in it.

You can create any number of locales. There may only be one parent locale, and any number of child locales, for example:

Child locales:   (base)   j   laserjet   stats   user
Parent locale:                    z

The only difference between the parent and child locales is in how J searches for a name. If a name is referenced in a child locale and not found, J then searches for the name in the parent locale. If then found, the definition in the parent locale is used, as if it had been found in the child locale; if not found, no further search is made.

The base and z locales are treated specially.

The Base Locale

The base locale is the default locale, i.e. if no specific locale reference is made, names are created and referenced in this locale, for example:

x=. 1 2 3

defines x in the base locale (except where noted, these examples assume there is nothing on the stack). Typically, development work is done entirely in the base locale. The base locale’s name is actually empty. In the diagram above, it is shown as (base).

When you invoke a function in another locale, this temporarily switches the default locale from base to this locale; this is the only way of changing the default locale. A key feature of the design is that the default locale is a child locale — and not the parent locale.

The z Locale

The z locale is the single parent locale. It differs from child locales only in the way J searches for a name. For example, consider:

res=. format i.100

J will first search for format in the base locale, and if found, will execute it. Otherwise it searches for format in the z locale, and if found, executes it in the context of the base locale. In both cases, res will be defined in the base locale.

If format is only defined in the z locale, it will not appear in namelists of the base locale. Typically, the z locale is used to store utilities that will be available to programs in any child locale, but whose names would not appear in namelists of those locales.

Note that in the above examples, no locale reference was used. Clearly, this would normally be the case when dealing with the base and z locales, but it is also typical of the definition as a whole — in practice, you rarely need to make locale references.

Locale References

To specifically reference a name in a locale, you append to the name: underscore, locale name, underscore. For example calc_def_ is the name calc in the locale def (read as: calc in def); calc_z_ is therefore calc in z, and calc__ is calc in base. You create a locale by creating objects in it. For example, assume the locale temp does not exist. Enter:

   x_temp_=. 1 2 3
   names_temp_ ''
x

The first sentence creates the locale temp and the name x in it. The second sentence executes the utility names in the context of the temp locale (names formats namelists, and is read into the z locale with J’s standard profile).

Locales have nameclass 6:

   nl 6
┌┬───┬─┬────────┬─────┬────┬────┬─┐
││def│j│laserjet│stats│temp│user│z│
└┴───┴─┴────────┴─────┴────┴────┴─┘

A function calc_def_ is executed in the context of the locale def, i.e. it temporarily switches the default locale from base to def while the function is running. Therefore, names referenced by calc have locale def, unless their locale is specifically given. The following definition assigns global names LEN in locale def, and NDX in locale util:

calc_def_=. 3 : 0                       calc_def_=. 3 : 0
LEN=: 10                equivalent to:  LEN_def_=: 10
NDX_util_=: 5                           NDX_util_=: 5
)                                       )

Using Locales

Now let’s look at a typical example of using locales, and examine why locale names are typically not referenced by the user.

One of the packages supplied with J is a set of functions to support printing with a LaserJet. To load this system, you enter:

load 'laserjet'

This populates a laserjet locale with about 50 utility programs, and reads 5 cover functions into the z locale. These cover functions call corresponding functions defined in the laserjet locale, for example:

ljprint=. ljprint_laserjet_

In actual use, only the cover functions are invoked, and the user need not be aware that the real work is being done in the laserjet locale, or that a large number of utility functions have been read in.

Now suppose some development work has to be done on the system. Would it not be inconvenient to have to keep referring to names in the laserjet locale with their full locale name, e.g. as ljprint_laserjet_? The answer is no, since all development work in J is done on the source script files, which are defined independently of the locales into which they may be read.

Finally, suppose one of the laserjet functions crashes and you want to view local definitions in the suspended function. While the function is suspended, all local names can be referenced without specifying the locale name, since invoking a function in the laserjet locale temporarily switches context to that locale.

Locale Design

Two facilities that have been requested are a system function to change the default locale, and the ability to arbitrarily nest locales.

In fact there seems no compelling reason to provide either facility, and should one ever arise, the definition could be changed with a compatible extension. However, they both seem more trouble than they are worth.

Consider the question of changing the default locale. It may be thought that allowing this would make it easier to reference objects in the new default locale, since its locale name would not have to be used. However, as already mentioned, locale names are rarely used anyway, so there is really no need to be able to switch the default locale. In fact, an early prototype of locales in Release 2 did contain a system function to change the default locale, but this was removed as it became apparent that it was of little use. On the other hand, if we allowed the default locale to be changed, the user would have to be constantly aware of the current setting, and of course recover from errors that may occur because of the wrong setting.

Now let’s look at the question of nested locales. If locales allow you to avoid name conflicts within the session as a whole, would not nested locales avoid name conflicts within a given locale? Yes, but the argument is much weaker. It seems reasonable to insist that the code in a given locale have no name conflicts, and in the event a locale were so large that name conflicts were likely, it would be better to partition the locale into two or more separate locales rather than create nested locales to resolve them. Again, nested locales seem likely to lead to more errors.

Summary

Locales have a simple definition which is nevertheless quite adequate. The facility works well in practice, and has provided a major improvement over the flat workspaces of earlier versions of J and APL.