Lab: Locales

Overview

This lab shows how to program with locales. Locales are used in modular programming, an effective way to build large applications.

Building an application as a monolith is a recipe for failure. It is better to build and test small modules, and then combine them to build the application.


Names can be a problem in an application built by simply adding modules together.

Name conflict is the most visible problem. Different modules can have different definitions for the same name.

An equally serious problem is just dealing with the sheer number of names in a single set.


The point is:


A locale is a set of names and their definitions that is available for execution.

An application can have several locales. In particular it can have different locales for different modules.

Locales preserve the modularity of modules in execution in the application.


This lab discusses the basic facilities provided by locales, and shows how they can be used to do modular programming.

Lab Object Oriented Programming shows how a certain model of working with locales can be used to do OOP.

The lab first loads utilities in coutil.ijs:

   load 'coutil'

plot example

First a quick example of locales in action.

plot is a module of hundreds of definitions. These definitions are loaded into their own locale, so we do not see their names in the main application.

The next step plots the data in d. After you have seen the plot window, close it and continue the lab.

   clear ''      NB. erase all names

   d=: ?1+i.50
   names ''      NB. list names
d 
   load 'plot'   NB. load plot module
   names ''      NB. no new names
d 

   plot d

Locale

All global definitions are in a locale.

A J session can have several locales, and thereby can have several sets of globals.

colib

The 18!:x foreigns work with locales.

colib.ijs defines utilities using 18!:n and in general you will not use 18!: directly.

colib utilities start with a prefix of co. The co is for class/object which is OOP terminology. One use of locales is in providing OOP.

current locale

Erase all names to start with a clean slate, and define a few.

Since all globals are in a locale, these definitions are in a locale.

   clear '' NB. erase all

   a=: 23
   b=: 20
   f=: +
   a f b
43

locale name

The locale used by execution is called the current locale.

Definitions are found and set in the current locale.

a, b, and f are defined in current locale.

   names ''
a b f 
   a
23
   b
20
   f
+

coname

A locale is identified by a name.

Initially base (locale name base) is the current locale.

Note that after each lab section is run, the current locale is reset to base.

   coname ''
+----+
|base|
+----+

cocurrent

Set foo as current and clear out old definitions.

   cocurrent 'foo'
   clear 'foo'

   names ''

Define a, b, and f in the foo locale.

   cocurrent 'foo'
   a=: 200
   b=: 100
   f=: -
   coname ''
+---+
|foo|
+---+
   names ''
a b f 
   a f b
100

We have definitions of a, b, and f in two locales.

   cocurrent 'base'
   a , b , a f b
23 20 43
   cocurrent 'foo'
   a , b , a f b
200 100 100

Execution uses names FROM the current locale.

   23+7              NB. no names, no locale is used
30
   cocurrent 'base'  NB. base current
   a , 7             NB. a in base
23 7
   cocurrent 'foo'   NB. foo current
   a , 7             NB. a in foo
200 7

locale execution

You can execute a name IN another locale by suffixing the name with the locale name bracketed with _ characters.

The name a_foo_ is read "a in locale foo".

You can use any kind of name (noun, verb,...) with a locale suffix to refer to the name in a locale.

   cocurrent 'base'
   a
23
   a_foo_
200
   a , a_foo_
23 200
   f_foo_
-

A name with a locale name suffix is called a locative.

   cocurrent 'base'
   f            NB. name
+
   f_foo_       NB. locative
-
   1 f 4
5
   1 f_foo_ 4
_3

Execution of a locative first sets the locale of the name as the new current locale.

It then executes the name in the new current locale.

When execution of the name finishes, the current locale is reset.

Locative execution:

Or, more briefly:


If the locative is a noun, set/execute/reset is the same as just getting the value from the locale.

   a
23
   a_foo_
200
   a , a_foo_ , a , a_foo_
23 200 23 200

set/execute/reset is more interesting when the name uses other names.

Executing t_foo_ :

  1. set     -  set foo as current

  2. execute -  execute t (in foo) which sets q (in foo)

  3. reset   -  reset current (as base)

When t_foo_ is executed it sets q in foo, not q in base.

   cocurrent 'base'
   t_foo_ =: verb def 'q=: y'
   t_foo_ 456
456
   q_foo_
456
   q          NB. q isn't defined in base
|value error: q
|[-4] 

Define verb g in both base and foo. The definitions are the same and both reference a global abc.

   cocurrent 'foo'
   g=: verb def 'abc + y'
   cocurrent 'base'
   g=: verb def 'abc + y'

Setting base as current and executing g uses the value of abc in base.

   cocurrent 'base'
   abc =: 10
   g 5
15

Setting foo as current and executing g in foo uses the value of abc in foo.

   cocurrent 'foo'
   abc=: 100
   g 5
105

The interesting case is when execution starts in base and g_foo_ is executed.

Executing g_foo_ uses the global abc in foo.

   cocurrent 'base'
   abc
10
   abc_foo_
100
   g_foo_ 5
105

Executing g_foo_ sets foo as current and executes g in foo. When execution comes to the name abc, it gets the definition from current, foo. When execution of g finishes, current is restored to the previous locale, base.

   cocurrent 'base'
   g_foo_ 10
110
   a_foo_ =: 47
   a , g_foo_ 10
23 110
   coname ''
+----+
|base|
+----+

Define verb h in foo to return the current locale.

See how execution moves from base, to foo, and back.

   cocurrent 'base'
   h_foo_ =: verb def 'coname'''''
   (coname'') , (h_foo_ ''), (coname '') , (h_foo_ '') , coname ''
+----+---+----+---+----+
|base|foo|base|foo|base|
+----+---+----+---+----+

execution stack

The execution stack is the record of the names, locales, and local variables in execution.

Curiously, local (no e!) names used in executing explicit definitions are part of the execution record and are not stored in a locale.

The execution stack is not a part of any locale.


If aa calls bb which calls cc, then during execution the stack records this information.

Snapshots of the stack would show:

                   cc
     bb            bb(calls cc)  bb(resumes)
 aa  aa(calls bb)  aa            aa           aa(resumes)


As well as tracking who calls who, the stack also tracks the current locale.

When a locative is executed, the stack records the current locale name and sets the new current.

When execution of a locative finishes, the stack information is used to reset the current locale of the caller and then resumes execution.


If aa calls bb which calls cc_foo_ which calls dd, then when dd is in execution the stack looks like:

   dd
   cc_foo_
   bb
   aa

cc_foo_ sets current to foo and dd is therefore a name in foo.


Let us make the definitions to see this.

   cocurrent 'base'
   aa=: verb def 'bb 0'
   bb=: verb def 'cc_foo_ 0'
   cc_foo_ =: verb def 'dd 0'
   dd_foo_ =: verb def '13!:18$0' NB. return stack

Study the output of running the example.

In the ijx window execute:

   aa 0

The result is the stack when dd executes.

   cocurrent 'base'
   NB. execute sentence:   aa 0

The result should be:

    aa 0
        dd 0
        cc_foo_ 0
        bb 0
        aa 0

summary so far

Name Resolution

Searching for a name in locale hierarchy lays a foundation for locale inheritance.

base

The base locale is current when J starts.

You can use a locative to refer to a name in the base locale just as you would with any other locale.

   a=: 123
   a
123
   a_base_
123
   a_base_ =: 234
   a
234

t in foo is defined to set global a in the base locale.

   t_foo_ =: verb def 'a_base_ =: y'
   t_foo_ 345
345
   a
345

A locative with no locale name between the bracketing _ characters is treated as if it had the name base.

abc__ is the same as abc_base_

Treating __ as _base_ is for compatibility with earlier versions, and in general you should use _base_ .

   a=: 3
   a
3
   a_base_
3
   a__
3
   a__ =: 23
   a
23

direct locale name

Locatives such as f_foo_ are "direct locatives".

A direct locative suffix explicitly gives the locale name.

indirect locale name

An indirect locative suffix is the name of a variable that contains the locale name.

An indirect locative suffix is separated from the name by two _ characters.

q is defined as the boxed locale name foo.

q can be used in indirect locatives.

   q=: <'foo'
   a_foo_
47
   a__q
47
   f_foo_
-
   f__q
-

a_foo_ is read as "a in foo".

a__q is read as "a in indirect q", or casually as "a in q".


Indirect locatives make it easier to program with locales. In particular it avoids the use of ". "execute".

Indirect locatives are essential in using locales in OOP.

It is difficult to give a simple example of the use of indirect locatives outside of OOP.

The following example is artificial, but does point out some interesting things.


Assume two libraries of statistical routines. One set of definitions is defined in locale s1 and the other in s2.

There is an r verb in both that returns random numbers. The s1 definition of r is ?, and the s2 definition skews by having a minimum of 2.

   r_s1_ =: ?
   r_s2_ =: 2 & >. @ ?    NB. skewed random
   test=: dyad def 'r__s y [ s=. <x'

The test x indicates which library version of r to use.

   d=: 20#5
   's1' test d
1 3 3 0 1 0 4 3 4 1 2 1 1 1 0 4 4 3 4 2
   's2' test d
4 3 4 3 4 2 4 2 2 2 4 2 2 2 2 2 3 4 3 4

Without indirect locatives the solution would have to use ". or conditional execution.

   test2=: dyad def '". ''r_'' ,x , ''_ y'''
   
   's1' test2 d
4 0 1 1 2 0 3 1 2 1 3 3 4 1 0 0 3 1 1 4
   's2' test2 d
4 3 3 2 4 2 2 2 3 2 2 2 3 3 2 2 4 2 2 2

path

We have said execution gets definitions from the current locale. This is not the whole picture.

If a name is not defined in the current locale, execution searches for it in the locales in the CURRENT PATH and the first definition found is used as if it were defined in the current locale.


A path is a list of locales and every locale has a path.

When a locale is created it has a path of the z locale.

The current path is the path of the current locale.

   copath 'foo'
+-+
|z|
+-+
   coname ''          NB. name of current locale
+----+
|base|
+----+
   copath coname ''   NB. current path
+-+
|z|
+-+

z

Erase qwert in base and define qwert in z.

Execution of qwert in base looks for a definition in base. When it is not found, the search continues in the locales in the current path. z is in the path, so the qwert definition in z is used.

   erase 'qwert'
1
   qwert
|value error: qwert
|[-1] 
   qwert_z_ =: 'from z'
   qwert
from z

The definition of qwert from z is used as if it had been defined in base. base remains the current locale and there is no change of current to z.

Executing a locative changes current and we use the phrase "name IN locale".

We use the phrase "name FROM locale" if execution uses a definition from a locale in the current path.

Executing a name FROM a path locale does not change the current locale.


A name can be defined in more than one locale in the current path. The first definition is used.

   qwert               NB. defined in z
from z
   qwert=: 'in base'   NB. defined in base and z
   qwert               NB. first one is used
in base
   qwert_z_
from z

Assignment is always in the current locale.

   ggg=: 'ggg from base'
   ggg_z_ =: 'ggg from z' NB. z current, then assigns ggg
   ggg
ggg from base
   ggg_z_
ggg from z
   ggg=: 123
   ggg
123
   ggg_z_
ggg from z
   erase 'ggg'  NB. erase ggg in base
1
   ggg          NB. not in base, so comes from z
ggg from z
   erase 'ggg_z_'
1
   ggg
|value error: ggg
|[-10] 

Verb p is defined in z.

If it is executed IN base (the definition comes FROM z), the value of K is the value of K from base.

If p is executed IN z, then z is current and the value of K is the value from z.

   erase 'p'   NB. erase p in base
1
   k=: 'K from base'
   k_z_ =: 'K from z'
   p_z_ =: verb def 'k'
   p 0
K from base
   p_z_ 0
K from z

Now that you understand paths and the z locale, something that might have been a mystery before can be cleared up.

When we list the names of verbs in the current (base) locale notice that the verb names is not listed. Yet it can be executed!

   names verb  NB. names of type verb
aa    bb    f     g     test  test2 

names is not defined in base, but is defined in z.

The definition of names from z is executed as if it were in base.

Similarly, names is not defined in foo. Executing names_foo_ sets foo as current and executes names. names is not defined in foo, so the definition from z is used.

   names verb
aa    bb    f     g     test  test2 
   names_base_ verb
aa    bb    f     g     test  test2 
   names_foo_ verb
cc dd f  g  h  t  

IN and FROM summary

Paths

copath

Create two new locales.

   a_m_ =: 'a from m'
   b_mm_ =: 'b from mm'
   copath 'm'
+-+
|z|
+-+
   copath 'mm'
+-+
|z|
+-+
   a_m_
a from m
   b_mm_
b from mm

Dyad copath sets a new path. The left argument is a list of boxed locale names.

Locale m has a path set as mm and z.

b is not defined in m and the definition from mm, which is in the path, is used.

   ('mm';'z') copath 'm'
   copath 'm'
+--+-+
|mm|z|
+--+-+
   b_m_
b from mm

a and b are both valid names when used in m. The a definition comes from m and the b definition comes from mm.

names only lists names in the current locale. names does not search down the path for all names that could be used.

   a_m_
a from m
   b_m_
b from mm
   names_m_ ''
a 

copathnl does not include names defined in z. There are quite few and it is generally not useful to list them.

   copathnl_m_ ''
+-+-+
|a|b|
+-+-+

   copathnlx_m_ ''
+-+-+--+
|a|m|  |
+-+-+--+
|b| |mm|
+-+-+--+

Add another locale and change the path of m to include it.

The copathnlx result shows:

   a_mmm_ =: 'a in mmm'
   c_mmm_ =: 'c in mmm'
   ('mm';'mmm';'z') copath 'm'
   copathnlx_m_ ''
+-+-+--+---+
|a|m|  |mmm|
+-+-+--+---+
|b| |mm|   |
+-+-+--+---+
|c| |  |mmm|
+-+-+--+---+

path summary

Utilities

conl 0

   conl 0
+----+---+-+...------+-+--+---+--+--+-+
|base|foo|j|...jzplot|m|mm|mmm|s1|s2|z|
+----+---+-+...------+-+--+---+--+--+-+
   conames 0
base       foo        j          jafm       jbmp       jbrowser   jcompare   
jfiles     jgl2       jhelp      jijs       jlab       jregex     jscriptdoc 
jviewmat   jwplot     jzgraph    jzplot     m          mm         mmm        
s1         s2         z                                                      

coerase

Erasing a locale that is on the execution stack is tricky. In this case the locale is actually erased when it is no longer on the stack.

   conames 0
base       foo        j          jafm       jbmp       jbrowser   jcompare   
jfiles     jgl2       jhelp      jijs       jlab       jregex     jscriptdoc 
jviewmat   jwplot     jzgraph    jzplot     m          mm         mmm        
s1         s2         z                                                      
   coerase <'foo'
1
   conames 0
base       j          jafm       jbmp       jbrowser   jcompare   jfiles     
jgl2       jhelp      jijs       jlab       jregex     jscriptdoc jviewmat   
jwplot     jzgraph    jzplot     m          mm         mmm        s1         
s2         z                                                                 

cocreate

So far all locales had names that started with an alphabetic and were chosen by the programmer.

cocreate is intended primarily for use in OOP and we will just take a quick look here.

   w=: cocreate ''
   w
+-+
|6|
+-+
   'the locale name is: ' , >w
the locale name is: 6


cocreate returns a new, previously unused, name of digits.

A locale name that references a locale created with cocreate will cause an error if used after the locale is erased. This avoids bugs where a reference is used after a locale is erased.

   [w=. cocreate ''
+-+
|7|
+-+
   coerase w
1
   cocreate ''
+-+
|8|
+-+

Locales with names starting with an alphabetic are "named locales".

Locales with names with only digits are "numbered locales".

In OOP terminology a named locale is a class and a numbered locale is an object.


A numbered locale name can be used just like a named locale name.

   w=: <'200'
   abc_200_ =: 123
   abc__w
123
   abc__w=: 'testing'
   abc_200_
testing

   cocreate ''
+---+
|201|
+---+
   cocreate ''
+---+
|202|
+---+
   conl 0    NB. named
+----+-+...------+-+--+---+--+--+-+
|base|j|...jzplot|m|mm|mmm|s1|s2|z|
+----+-+...------+-+--+---+--+--+-+
   conl 1    NB. numbered
+-+-+-+---+---+---+-+-+-+-+-+
|0|1|2|200|201|202|3|4|5|6|8|
+-+-+-+---+---+---+-+-+-+-+-+
   conl ''   NB. both
+-+-+-+---+---+---+-+-+-+-+-+----+-+...------+-+--+---+--+--+-+
|0|1|2|200|201|202|3|4|5|6|8|base|j|...jzplot|m|mm|mmm|s1|s2|z|
+-+-+-+---+---+---+-+-+-+-+-+----+-+...------+-+--+---+--+--+-+

   conl 1
+-+-+-+---+---+---+-+-+-+-+-+
|0|1|2|200|201|202|3|4|5|6|8|
+-+-+-+---+---+---+-+-+-+-+-+
   coreset''
1 1 1 1 1 1 1 1 1
   conl 1
+-+-+
|0|1|
+-+-+

18!:n

Locales are used by direct and indirect locatives and by the 18!:n foreigns.

Summary of 18!:n and colib.ijs definitions:

summary

Locales are useful in modular programming. You can load different sets of definitions in different locales. There are no name conflicts, yet you can use them all together in execution.

Lab Object Oriented Programming builds on this lab to show how locales are used in OOP, the next level of modular programming.

Studio/Locales (last edited 2008-12-08 10:45:46 by anonymous)