Studio/Locales

From J Wiki
Jump to navigation Jump to search

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:

Each module has its own names.

Combining modules in a single set of names loses modularity and causes name conflicts.

We want to keep the modular sets of names in execution in the application.


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.

names returns names 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.

coname returns the boxed name of the current locale.

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

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

cocurrent

cocurrent sets the current locale.

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:

set current / execute name (in current) / reset

Or, more briefly:

set / execute / reset


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_ :

0. set - set foo as current 1. execute - execute t (in foo) which sets q (in foo) 2. 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

A locale is a set of global definitions.

Execution uses definitions from the current locale.

A locative is a name with a suffix bracketed by _ characters.

Executing a locative sets a new current locale. When execution of the name finishes, current is reset.

The stack records who called who and which locale is current. The stack also records the local definitions of explicit definitions.

coname returns the current locale. cocurrent sets the current locale.

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.

copath returns the path of a 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.

names lists names in the current locale.

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

Execution is IN the current locale.

cocurrent and locatives (direct and indirect) set the current locale.

Execution gets definitions FROM the current locale and the locales in the current path.

Execution looks for a definition only in the current path.

A definition FROM the path does not set a new current locale and executes as if it were in the current locale.

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 lists names defined in the current locale and its path.

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 returns a result which shows which path locales have definitions for a name. Names in z are not reported.
   copathnlx_m_ ''
+-+-+--+
|a|m|  |
+-+-+--+
|b| |mm|
+-+-+--+

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

The copathnlx result shows:

a is defined in the m and mmm

b is defined in mm

c is defined in mmm

   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

A locale path is a list of locale names.

The current path is the path of the current locale.

Execution gets definitions from the current locale and locales in the current path.

copath 'abc' - returns path of the locale

path copath 'abc' - sets path of the locale

Utilities

conl 0

conl 0 - returns list of locale names

conames 0 - returns formatted list of locale names

   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

coerase erases a locale.

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 creates a locale with an name of digits.

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|
+-+-+-+---+---+---+-+-+-+-+-+----+-+...------+-+--+---+--+--+-+
coreset erases all numbered locales, except for those used by session windows.
   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:

18!:1 conl - 0/1 named/numbered

18!:2 copath - return path : set path

18!:3 cocreate - create numbered

18!:4 cocurrent - set current

18!:5 coname - return current

18!:55 coerase - erase

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.