Robert Cemper · Nov 11, 2017 3m read

Summary on Local Variable Scoping

This should be an overview over a subject that pops up over several places in online documentation mostly as remarks and never as dedicated chapter.

Once upon a time ...  No it's not a fairy tale.
In the beginning of Caché (and before) you had your partition to run your code. Part of that partition was a space with all your local variables nicely sorted by %,A,..Z,a,...z

And whatever values or information you had to store locally was there and visible and available to any piece of code running  in your partition. No problem if you are team of developers working well together with complete documentation and excellent discipline.
[sorry it shouldn't be a fairy tale].

If fact working with (own or foreign) software packages it could become a nightmare then and finding non conflicting use of variables was more effort than the code itself. Needless to mention that meaningful naming became an exception. The only help at that time was
the NEW command to push variables on stack and recover them later.

The real solution in Caché 5.0 (2002) was to create an isolated scope for procedures and methods with an empty and independent space for variables dedicated to these BLOCK of code. No trouble anymore with protecting against misused or identic named variables.
For Class and Method definitions the parameter was named ProcedurBlock .

The traditional scope of variables was named PUBLIC and the new scope was name PRIVATE.
With the basic rule: anything starting with % is a PUBLIC variable. Anything else requires explicit setting.

The keyword ProcedureBlock (Default=1) controls if a PRIVATE scope for variables is available.
What a improvement!
Implementing easy to use functions, procedures became rather simple with scoping.
And implementing Methods in Object Classes became remarkable straight forward.
Now in parallel to all PUBLIC variables you could have a large set of PRIVATE variables.
(No excuse for od named variables anymore !)To make it clear: To the set of PRIVATE variables applies the Highlander Principle:
There can be only one !

There is 1 set of Public variables and 1 set of Private variables active at a point in time.

But as in real life there is a minor exception: If you pass a variable ByReference to the called method / procedure you have a small door back to the calling scope.

The variant ProcedureBlock=0 or Not ProcedureBlock was mainly targeted to legacy code that was relying on old logic. BUT: every now and then there are some dark corners of generated code that still require / use it.

In general conflicts with variable scoping is a rare thing.
With 2 exceptions and 1 workaround:

Xecute just works on public variables in its basic definition.
There is an extent that allows passing of parameters somewhat similar to a procedure call.
A further extension in this direction is provided by the $Xecute() function.

Indirection just works on public variables. No workarounds.
Another good reason to stay away from indirection.

You find more related documentation on variable scoping here:

2 1 3 552
Log in or sign up to continue


Hi Robert,

There are at least two additional exceptions:

3. JOB command with passing the symbol table to the spawned job


When switch#2=1, only public variables of parent process are passed to its child.

4.  Class %SYS.ProcessQuery, query VariableByPid() (or $zutil(88,2,Pid,Variable) for old schoolers)

Only public variables of another process can be queried.

Thank You Alex!
I knew it's a can of worms and spread over documentation like feeding pigeons.
And honestly I was pretty sure not catch them all and hesitated quite some time to touch it..   
yes  Great contribution.

...and spread over documentation

...Or undocumented, as it is in cases #3 and #4. 

It seems that it's possible to form a simple rule whether you can use a private variable: if you can consider its name to be evaluated at compile time, you can, otherwise - no:

1) Indirection: variable names (or expression that contains them) are evaluated at runtime.

2) Xecute: similar to #1. In contrast, $xecute() arguments names can be evaluated at compile time.

3) Child process's symbol table filled with switch#2=1: runtime.  In contrast, job's entry arguments names can be evaluated at compile time.

4) %SYS.ProcessQuery is dynamically querying other process's symbol table, so, run time only.