- 1 Locales
- 1.1 Summary of the Locale Mechanism
- 1.2 Namespaces and locales
- 1.3 The base locale and the z-locale
- 1.4 What makes a locative?
- 1.5 A good way to understand locatives
- 1.6 Explicit locative and object locative
- 1.7 Name lookup and search paths
- 1.8 Assignment
- 1.9 Changing the implied locale
- 1.10 How to work with locales and locatives
- 1.11 Working with classes and instances
- 1.12 Numbered locales
- 1.13 Class Verbs and Instance Verbs
- 1.14 Inheritance
- 1.15 Verbs Relating To Locales
- 1.16 Adverbs in Locales
myverb =: + mynoun =. 5 NB. inside a verb definition body
the name mynoun or myverb always resides in some given namespace.
Summary of the Locale Mechanism
If you already understand locales, here is a refresher. Otherwise skip to the next heading...
- A locale is a public namespace. A locale is created whenever you create or refer to a name in the space.
- Every action on a name - an assignment to the name, or a reference querying its value - has a starting locale.
- At all times there is an implied locale that will be used as the starting locale if not overridden by a locative.
- A locative is a name that includes within it the namespace to use as the starting locale (instead of the implied locale).
- name_locale_ means "name with locale as the starting locale".
- name__instance means "name with the value of instance as the starting locale".
- Each locale has an associated search path, a list of locales. By convention every search path ends with the locale z. You can change the search path at will.
- References to public names begin the search in the starting locale. If the name is not found in the starting locale, lookup continues with the locales in its search path until the name is found.
- Public assignments are always made in the starting locale.
- When an entity named by a locative is executed (example: names_z_ ''), the implied locale is changed to the locale given in the locative, and changed back after execution.
- An instance of a class named newclass is created by a line like
instancename =: (creation_parameters) conew 'newclass'
which creates a locale for the instance, associates it with the class newclass, executes the create verb in the instance, and returns the locale name of the instance.
Namespaces and locales
There are two kinds of namespace:
J creates one of these when it starts to execute an explicit verb, and destroys it again when the verb stops running. This namespace holds private names defined in the body of the verb, which avoids name clashes with names defined outside the body. A private namespace is created for explicit modifiers too, when they start executing the body
A named locale, also called a class locale, has a name, chosen by its creator, typically you. A numbered locale, also called an instance locale, holds an instance of a class, and is given a system-assigned numeric name when the instance is created. You use either type of locale-name by writing it into an extended kind of name called a locative, as described below.
A locale-name is always a list of characters. In a numbered/instance locale, the characters are all numeric, for example '124'.
Locales are chiefly used to avoid name collisions between software packages, or to create an object-oriented class instance.
The base locale and the z-locale
There are two locales which you can rely on always being present:
- base (aka the base locale) — the default locale that J starts up in
- z (aka the z-locale) — containing standard library (stdlib) words which are always visible, i.e. they behave as if they are defined in every namespace.
See Vocabulary/ZeeLocale. z-locale also contains the few names that constitute the public interface to packages, such as addons or packages that you might write.
You could in principle do all your work in the base locale without knowing about locales or locatives. But when you come to write a significant application for distribution, or try to understand the workings of distributed applications, e.g. addons, then it's vital to know how locales and locatives work. See below: How to work with locales and locatives.
What makes a locative?
To form a locative, typically you join a locale name, e.g. myloc, to the simple name of your entity, e.g. myverb, like this:
myverb_myloc_ =: + mynoun_myloc_ =. 5
But that's not the only way.
In general, a locative
- contains at least two underscores (_)
- consists of a simple name followed by the (designation of the) starting locale
- the (designation of the) starting locale is one of these:
- a locale name surrounded by underscores (_). The name can be
- a valid J name containing no underscores (_)
- a character string of all digits with no superfluous leading 0 digits
- a double-underscore (__) followed by a name.
- a locale name surrounded by underscores (_). The name can be
The __name sequence can be repeated.
Q. Can a name contain underscores (_) without J thinking it's a locative?
A. Yes, but ...
- it must not end in an underscore
- it must not have two underscores together.
When discussing locatives, "by abuse of terminology" (to paraphrase WikiPedia:Bourbaki) we shall simply say
- "the entity" when we mean the simple name of some entity (noun, verb, etc.)
- "the locale" when we mean the name (or other valid designation) of some locale.
A good way to understand locatives
Our explanation of locatives is alas going to be somewhat complicated.
But a good way to understand them is to recall how files are structured in Windows(TM), enabling you to distinguish between files having the same name in different directories (aka "folders).
- A filename may or may not have a path description attached to it. If it does, it's called "a pathfilename", or sometimes just "a path".
- The path description may not take you straight to the target file. It may point instead to the start of some (configurable) "search path" stored elsewhere.
- There is such a thing as the current directory, which in certain limited circumstances lets you (optionally) omit the path description from a given filename.
J uses a comparable way of locating entities having the same name in different locales. But the syntax is different.
You can make a pretty good prediction of how locatives behave if you mentally substitute
- "file" for "entity" (noun, verb, etc.)
- "directory" for "locale"
- "current directory" for "implied locale"
- "path/filename" for "locative".
Failing that, read on...
Explicit locative and object locative
There are two ways of joining a locale to a simple name to produce a locative:
- Explicit Locative: myverb_myloc_
the starting locale is the string between the two underscores.
- Object Locative: myverb__mylocptr
Exception: A locative of the form: myverb__ (i.e. with two trailing underscores) is equivalent to myverb_base_ .
Since the base locale typically serves as a receptacle for test data, this form of locative, e.g. y__ , is a convenient way of exporting test data from within a verb body.
What does it mean when two or more locales appear in an object locative, e.g. myverb__loc1__loc2 ?
A way to think of it is this. In the construct myverb__mylocptr , the locative designation mylocptr can itself be an object locative (in this case loc1__loc2). This feature is illustrated in the example of a doubly-linked queue below.
At any given moment, J recognizes an implied locale, which is the first public namespace J looks inside for the entity in question.
If a private namespace is active, references to non-locatives will look there before trying public namespaces.
Note: The J Dictionary uses the term current locale instead of implied locale. We shall use both here, with current locale serving to emphasize what is seen when the J interpreter is idle.
At startup, the implied locale is the base locale.
The implied locale can change many times during execution of a sentence, as described further down this page.
An example using explicit and object locatives
Make a script containing the code
make=: verb define NB. make doubly-linked queue of (y) numbered locales for_i. i.y do. loc=. < ": i name__loc=: 'This is locale ' , ": i prev__loc=: < ": y| i-1 next__loc=: < ": y| i+1 end. empty'' ) inspect=: verb define NB. inspect contents of numbered locale (y) cocurrent ": y smoutput '----------' smoutput names'' [ smoutput 'contents of locale: ' , ": y smoutput name [ smoutput 'name:' smoutput prev [ smoutput 'prev:' smoutput next [ smoutput 'next:' ) inspec2=: verb define NB. inspect contents of numbered locale (y) NB. Use object locatives instead of: cocurrent'' loc=. < ": y smoutput '----------' smoutput names__loc'' [ smoutput 'contents of locale: ' , ": y smoutput name__loc [ smoutput 'name:' smoutput prev__loc [ smoutput 'prev:' smoutput next__loc [ smoutput 'next:' )
Verbs inspect and inspec2 give identical results, but by different means.
Make a queue of 5 objects and inspect object 3
make 5 inspect 3 ---------- contents of locale: 3 name next prev name: This is locale 3 prev: +-+ |2| +-+ next: +-+ |4| +-+ coname'' +----+ |base| +----+ name_3_ NB. explicit locative This is locale 3
Notice that when inspect exits, J restores the current locale to base without your code having to do this, as we show by coname'' .
Notice too that you don't have to use an object locative to access entities inside a numbered locale. You can use an explicit locative instead, e.g. name_3_ . But it is harder to write code to manipulate a multiplicity of similar locales, which is where an (alterable) object locative has the advantage.
Choose an arbitrary object to be the head of the queue and create a noun: head to point to it:
- Just like next and prev in any given object, the value of head is a boxed string of digits.
- head will be used to form an object locative to refer to the contents of a given object.
- By varying the value of head you effectively rotate the queue.
head=: <,'3' name__head NB. object locative: the value of (name) in locale 3 This is locale 3 next__head NB. object locative: the value of (next) in locale 3 +-+ |4| +-+ prev__head +-+ |2| +-+
Using multiple locale names in an object locative, it's possible to "reach" along the queue in either direction
name__next__head This is locale 4 name__next__next__head This is locale 0 name__next__next__next__head This is locale 1 name__prev__head This is locale 2 name__prev__prev__head This is locale 1
Name lookup and search paths
Namespaces are searched in the following order. If any search step succeeds, the search ends, returning the value that was found:
- Simple names in a sentence from an explicit entity will be sought in the private namespace of that entity. To be in the private namespace, the name must be assigned using =. rather than =:.
The details get tricky when an explicit verb executes a tacit verb and you wonder just what names the tacit verb has access to. In that case you need the full rule, which is: If the original name was not a locative, and a private namespace exists (in otherwords, if an explicit entity is running), the youngest private namespace is searched to see if the simple name is defined there.
- The starting locale is searched.
- The locales in the search path of the starting locale are searched, one by one, stopping at the first successful search.
The search path of a locale is an ordered list of boxed locale names, which is initialized when a locale is created.
Note that a locale's search path does not include the locale itself.
Note also that the search paths of other locales are not used. The search path is a list, not a tree.
When a locale is created implicitly by reference, its search path initially includes only the z locale. An instance locale created by conew is given the search path of its class.
You can modify the search path with the copath and coinsert verbs.
1. If the assigned name is a locative, the name is defined in the starting locale. 1. Otherwise, if the assignment word is =. and a private namespace exists, the name is defined in the youngest private namespace. 1. Otherwise, the name is defined in the implied locale.
Changing the implied locale
The implied locale changes whenever one of these things happen
- executing 18!:4, cocurrent or coclass
- executing a locative
- exiting/returning from a named verb or modifier.
Returning from an anonymous verb, even an explicit one, does not restore the implied locale.
Note: coclass has the same definition as cocurrent, but is used in a script to signify the start of an object-oriented class.
By 1. above, whether it is executed inside the body of a verb, or in immediate mode (i.e. in the J session)
makes mylocale the implied locale for subsequent J sentences.
lists all verbs residing in the current locale, which we imagine here to be the base locale. But
lists all verbs residing in mylocale. This works even though mylocale may happen not to contain a verb called names.
The library verb names resides in the z-locale – and that's where J finds it in the above example. But after exiting verb names, J goes back to the existing implied locale, viz. base.
Note: The implied locale is always set to the starting locale written-into the locative, even if the simple name was found in some other locale in the search path.
For a worked example relying on this feature, see below: How to work with locales and locatives.
When a named explicit verb, or indeed any named entity exits/returns, the implied locale is set to what it was before the verb started running.
This means that when you execute a named verb, even with a locative, you can be sure the implied locale will be restored when the verb finishes. WARNING: You can defeat this feature by calling 18!:4 directly, not via its cover name: cocurrent. Therefore avoid 18!:4 in favor of cocurrent.
How to work with locales and locatives
A small utility you've written for yourself can conveniently be loaded into the base locale, especially if it consists of just one verb. But a professionally written J application, e.g. an addon, generally creates one or more locales for its own use, e.g. to house its working code. Furthermore it may create additional locales (typically: numbered ones) as needed to implement (object-oriented) instances of any classes it employs.
Example: the J6.02 IDE implements multiple edit windows in this manner.
Example: writing your first professional-looking application
NB. Script: MyFirstApp NB. Provides a single verb: sum NB. It's meant to be used like this: NB. sum 1 2 3 NB. 6 sum =: +/
This utility loads into the current locale, which by default is the base locale.
But do you want to clutter your user's base locale with secret words which are not of her making?
So you arrange to load your simple app into the z-locale instead of the base locale. This still makes it visible inside the base locale, indeed any locale at all
NB. Script: MySecondApp NB. Provides a single verb: sum NB. It's meant to be used like this: NB. sum 1 2 3 NB. 6 sum_z_ =: +/
But do you want to clutter the z-locale with words of significance only within the guts of your app,
risking name-clashes which overwrite standard library words which your user needs?
One or two new z-words are okay, but they ought to be public words, which you instruct your user to call.
So you arrange to load your simple app into its own private locale instead of the z-locale. This hides your working code out of harm's way
NB. Script: MyThirdApp NB. Provides a single verb: sum NB. It's meant to be used like this: NB. sum 1 2 3 NB. 6 coclass 'MyThirdApp' NB. All names assigned from here on are defined in the locale MyThirdApp sum =: +/
But your user is still not happy. She now has to call your utility like this:
sum_MyThirdApp_ 1 2 3 6
It's not as convenient as
sum 1 2 3
So you arrange to load a single word into the z-locale as well, which gives your user a convenient handle to work with your app
NB. Script: MyFourthApp NB. Provides a single verb: sum NB. It's meant to be used like this: NB. sum 1 2 3 NB. 6 coclass 'MyFourthApp' sum =: +/ NB. The public interface to MyFourthApp sum_z_ =: sum_MyFourthApp_
Now you have reproduced the structure of a typical professionally written application.
Let's go back to that last definition: sum_z_ =: sum_MyFourthApp_. How did that let the user write
sum 1 2 3
and have it executed in the correct locale?
There were two steps to executing sum 1 2 3.
1. The verb, sum, was looked up starting in the user's locale. It wasn't found there, so the search proceeded until the name was found in z locale. The name sum has the verb value sum_MyFourthApp_, so it is replaced by its value. No change of locale is performed, because looking up a name doesn't change the locale, no matter where the name is found. 1. Now the interpreter executes sum_MyFourthApp_ 1 2 3. This is the execution of a locative, so the implied locale is changed before executing the body of sum.
Example: how addon: 'general/dirtrees' uses locales and locatives
The addon: general/dirtrees provides two utility verbs: copytree and deltree. These enable you to copy and delete folders on disk, together with their contents.
The working code resides in two locales: rgsdirtrees and rgsdirutils, as you can see
conames'' NB. what locales are present to start with? base ctag j jadetag jcompare jp jqtide jregex jrx jtask qtprinter z load 'general/dirtrees' conames'' NB. NOW what locales are present? base ctag j jadetag jcompare jp jqtide jregex jrx jtask qtprinter rgsdirtrees rgsdirutils z
Run copytree, using a stock example
SRCDIR=: jpath '~tools' TRGDIR=: jpath '~temp/tools' TRGDIR copytree SRCDIR NB. copy source directory SRCDIR into ~temp 2 2 deltree TRGDIR NB. delete the folder we've just made and all its contents 1 deltree TRGDIR NB. delete it again -- response 0 means it isn't there any more 0
That all works fine. Verbs copytree and deltree weren't visible before, but they are now.
Take copytree. You can view its definition
...but where does it reside? Not in the current locale, which only contains the two nouns we've defined: SRCDIR and TRGDIR
coname'' NB. What is the current locale? +----+ |base| +----+ names'' SRCDIR TRGDIR
It happens to be in the z-locale, as you can see
names_z_'' ARGV BINPATH CR CRLF Cut DEL Debug EAV ... ... NB. not recommended, because the list is rather big !! 'cop' names_z_'' NB. what are the words in z-locale beginning with 'cop' ? copath copytree
How did addon: general/dirtrees manage this trick? You can view its script, like this
or, more reliably, like this
The last two lines happen to be
copytree_z_=: copytree_rgsdirtrees_ deltree_z_=: deltree_rgsdirtrees_
If we look at locale rgsdirtrees we see it contains only three words
names_rgsdirtrees_ '' copytree deltree fcopy
but these are the heart of the dirtrees addon.
Working with classes and instances
See Studio/Locales for the best available description of how to write an object-oriented class, and create instances of that class. What follows is the barest outline of how to go about it. For a working example of what follows, study the contents of the locale jijs in JWD (the J6.02 wd-based IDE).
- This is a class to manage the editing of scripts.
- To view its code: open '~system/extras/util/jijs.ijs'
- Pay special attention to the definitions of verbs: create and destroy
To create a new class instance
- Create a named locale e.g. myClass that will contain the common code plus any data shared by all the class instances
- Within this locale, define two verbs: create and destroy (the names are mandatory), to be executed as the first and last steps in the career of a class instance
- Thus, the instance to be destroyed may own a window. This needs to be closed gracefully before destroy erases the locale controlling its contents.
- The last line of destroy should be the sentence: codestroy''
- Place any information needed to create the given class instance in a noun e.g. InitializingInfo.
Now you can create an instance of myClass as follows
TheInst =: InitializingInfo conew 'myClass'
This creates a new numbered locale, whose search path is altered to include the shared namespace: myClass. (It already includes the namespace: z.)
This new locale is the class instance.
The id of this locale (a boxed string of digits) gets assigned to a noun: TheInst, where you can later refer to it, e.g. to destroy the instance.
- To access a noun: myAttr belonging to the instance, use the object locative: myAttr__TheInst .
- Likewise, to run a verb: myMethod belonging to the instance, use the object locative: myMethod__TheInst y .
- Alternatively, if you discover the value of TheInst is <,'4' say, (signifying 4 is the "number" of the numbered locale) then you can refer to these two entities like this:
- myMethod_4_ y
- To destroy the instance call: destroy__TheInst''
The only thing that makes the new instance an instance of myClass (rather than some other class) is its search path. conew sets the search path in the new instance to include the locale of the class, followed by any other locales in the class's search path, followed by z.
Actually, numbered locales have the same features as named locales, but they are designed to use less memory.
The J6.02 IDE itself (JWD) uses numbered locales as class instances, as outlined above, to support an indefinite number of copies of the IJS window (j602). It is easy to study and makes a good reference example of an object-oriented "application" written in J.
The J8.02 IDE (JQt), on the other hand, uses a single window for all opened scripts, putting each script in a separate tab. Opening a new script does not create a new locale.
This avoids having to invent a name for each new locale, or even bother finding the next available number, bearing in mind that id numbers of destroyed class instances can't be reused.
You can use the same technique yourself to create and manage multiple instances of your own classes.
Class Verbs and Instance Verbs
The verbs of a class are defined in the locale of the class. Since this locale is the first locale in the search path of the instance, references to such verbs from an instance will be found in the class, but the implied locale will remain in the instance; thus, public assignments will happen in the instance rather than in the class. These verbs are called instance verbs. Most verbs in a class using instances are instance verbs.
Class verbs are verbs that are called from an instance but need to execute in the locale of the class, usually because they need to use a shared class resource. For example, you might want to keep a list of all instances that have been created, so that you can destroy them when the program exits. This list would belong to the class rather than to any instance.
To make a verb a class verb, you execute it in the class locale rather than the instance locale, using a locative to make the switch. Thus,
will switch the locale to myClass before executing classverb. Note that mydata is evaluated in the instance's locale, not in the class locale.
To make sure that a name is always used as a class verb, you can build a locale-switch into the name:
classverbA =: verb define NB. This name will never be used outside this definition ... ) classverb =: classverbA_myClass_
Now any use of classverb will run in the class, not the instance.
A class A inherits names (which may include nouns and verbs) from class B by having class B in the search path of A. Then, an instance of A will automatically have access to the values in class B. If class A defines any names also used in B, A's values will override B's.
You inherit from a class by executing
usually right after the coclass that started the class. coinsert inserts parentclass, followed by its entire search path, into the search path of the implied locale. The newly-inserted items go to the end of the search path, except for z locale which stays at the very end.
When you inherit from a parent, you automatically inherit from its parents, since its entire search path becomes included in yours.
You may inherit from multiple parents by coinserting them. You may also pick and choose which locales to inherit from by building your own custom search path.
Verbs Relating To Locales
name y Function coclass
locale name (a character string, either boxed or unboxed) Sets the implied locale to y. The implied locale will revert when a named entity finishes execution. optional creation parameters conew class name (a locale name, as an unboxed character string) Creates an instance of the class y, sets the search path of the new instance to include the locale y followed by the search path of y, and sets COCREATOR in the instance to the name of the locale that it was created from. If x is given, executes create x in the instance, passing the parameters into the create verb. The result of conew is the name of the generated instance locale, a boxed string. codestroy '' Destroys the currently-active locale, after it has finished execution. coname '' The name of the implied locale, a boxed character string. copath locale name (a character string, either boxed or unboxed) Returns the search path for the locale y - a list of boxed locale names. Use copath coname '' to find the search path of the implied locale. new search path (a list of boxed locale names or a single unboxed locale name) copath locale name (a character string, either boxed or unboxed) Sets the search path of y to x. Normally the search path will end with z locale. coinsert list of locale names, either as a boxed list or as a character list containing space-separated locale names The search paths of all the locales in y (each augmented by having the starting locale from y added to the beginning) are concatenated together (in the order given) and added onto the end of the search path of the implied locale. Any occurrence of z locale is moved to the very end of the search path.
Example: If the search path of the implied locale is A,B,z, the search path of locale m is C,D,z, and the search path of locale n is E,z, then executing
coinsert 'm n'
will change the search path of the implied locale to A,B,m,C,D,n,E,z
optional value specifying the size of the name table cocreate locale name (a character string, either boxed or unboxed) or '' If y is empty, creates a numbered instance locale. Otherwise, creates the locale named y. conl list specifying the locales to name Returns a boxed list of active locales. If y is empty or contains 0, named locales are included; if y is empty or contains 1, numbered locales are included. conames list specifying the locales to name like conl, but formats the result into columns cofullname a name, either boxed or unboxed Returns a character list containing a locative for the name. If the original name was a locative, it is unchanged; otherwise the name of the current locale is appended. coerase a boxed locale name Destroys locale y after it finishes execution. coreset '' Destroys all instance locales after they have finished execution.
Adverbs in Locales
Consider this example:
adv_example_=:1 :0 echo coname u y )
When we try this out, we are not in the example locale but instead are in the base locale:
- adv_example_ 3 ┌────┐ │base│ └────┘ _3
What happened here?
The issue is that this adverb returns an anonymous explicit verb during the evaluation of - adv_example_ so it loses track of the original locale before it sees the value 3.
To work around this issue, we must keep track of the locale ourselves:
adv_example_=: 1 :0 u adv_implementation_example_ (coname) ) adv_implementation_example_=: 2 :0 return_locale=. coname cocurrent v echo coname return_value=. u y cocurrent return_locale return_value )
Note that names with _ in them which do not have a trailing _ are not locatives.
Note that we are still working with an anonymous verb here, and the automatic "switch back to previous locale" feature of 18!:4 only happens when executing named code. So we must manually switch back, ourselves (and must also switch back at the command line if we get an error in the switched code).