Overview

The collection of all q entities that have been created in a q session comprises the workspace. This includes not only explicitly created variables but also ancillary items such as enumerations, open handlers, tables mapped from storage and other internal structures.

Like any programming environment of reasonable complexity, q has the potential for name clashes — e.g., when two different scripts define variables of the same name. Because q does not have lexical scoping and local variables are strictly scoped to the function in which they are defined, name clashes between locals is not a problem. But global variables are exposed. To illustrate, suppose you load a script that creates a global foobar. Then you load another script that also creates foobar. The result is that the second script overwrites the value assigned in the first script.

Namespaces

A partial solution to name clashes is namespaces, which is the idea of placing a (usually hierarchical) structure on names.

The idea is that a compound name with embedded separators represents a nested series of containers.

Namespaces in many programming languages work similarly, using . as the separator. In q the containers are called contexts. For example, .jab.x represents a variable x contained in the jab context. The context jab has the symbolic name `.jab and the fully qualified name of its variable is `.jab.x.

Contexts

Namespacing in q is implemented with dictionaries. A context is a dictionary whose keys are the symbolic names of the variables in the corresponding namespace. The context dictionary associates each variable name with its currently assigned value.

With that out of the way, we can nest context

.cat.bengal.nuba
.cat.abyssinian.orion
Copy the code

KX reserves all root namespaces of a single letter, as well .kx, for its own use. It is worth noting that most q built-in functions that are not written in C live in the .q namespace. As of this writing (Sep 2015) the namespaces that are actively used by KX include .h, .j, .o, .q, .u, .z and .Q.

q).foo.a:42 / context foo created
q).foo.double:{2*x} / existing context foo used
Copy the code

Now suppose a separate script uses the bar namespace with different definitions for a and double.

q).bar.a:43 / context bar created
q).bar.double:{2*x} / existing context bar used
Copy the code

We can now load that script without clobbering our root variables, so we have solved the original problem posed in the introduction to this chapter.

When working with contexts, apply an overload of the key operator to list the names of all contexts in the root.

q)key `
`q`Q`h`j`o`foo`bar
Copy the code

Context as Dictionary

As mentioned previously, a context is a specially formatted q dictionary. Its keys are the symbolic names of the variables it contains; each is associated with the current value of the corresponding variable.

When we start a fresh q session, the root dictionary is empty. As with any variable, we can reveal the root context by applying get (or value) to its name and then applying the utility .Q.s1 to display its internal form since it is empty.

q).Q.s1 get `. "(`symbol$())! ()" q)get `. a | 42 double| {2*x} q).jab.wrong:43 q)get `.jab | :: wrong | 43Copy the code

You might wonder about the significance of the :: in the first entry of the .jab context dictionary. It is there to prevent the value list of the context dictionary from collapsing to a simple list in case all the variables should have values of the same scalar type. This would then prevent subsequent definition of variables of other types in that context.

As they say in certain quarters, q eats its own dog food, meaning all ordinary operations can be performed on a context dictionary. For example, we can look up the values associated with `a in the root and with `wrong in the .jab context.

q)`.[`a]
42
q)`.jab[`wrong]
43
Copy the code

In fact you can use this to access an obscured global inside a function.

q)a
42
q){a:43; `.[`a]}[]
42

 q){`a set 1+ get `a}[]
 `a
 q)43
 43
Copy the code

Expunging from a Context

q provides a special overload of the delete template explicitly for this purpose.

q)a:42
q).jab.wrong:43
q)delete a from `.
`.
q)a
'a
q)delete wrong from `.jab
`.jab
q)\v
`symbol$()
q)\v .jab
`symbol$()
Copy the code

Indeed, we issue the \v command to display the names of variables in a context (current context if none is specified) to verify that the variables are gone.

Saving and Loading Contexts

Since a context is a dictionary, you can persist it — and all its contents — as a serialized Q object using set.

For example, to write out the root context, retrieve it by name and set it into a file.

q)`:/data/root set get `.
`:/data/root
Copy the code

Conversely, you can read the serialized object back into memory using get on the file and then overwrite the current root context using set

q)`. set get `:/data/root
Copy the code

Working in a Context

A q session has a similar concept. At any point in the session there is acurrent working context. At the beginning of a fresh q session, the current working context is the root context and you use absolute names for all global variables. We can change the Working context with the \ D command (for “directory” by analogy to the file system). After you change the current working context, you can use relative names for global variables. Here we create two globals in the root context and then switch the current working context to show relative names.

q).jab.util.counter:0
q).jab.util.incrctr:{[] `.jab.util.counter set 1+get `.jab.util.counter}
q).jab.util.incrctr[]
`.jab.util.counter
q)\d .jab
q.jab)util.incrctr[]
`.jab.util.counter
q.jab)util.counter
2

q)state:`NY
q).jab.f1:{[] state}
q)\d .jab
q.jab)state:0N
q.jab)f2:{[] state}
q.jab)\d .
q).jab.f1[]
`NY
q).jab.f2[]
0N
q).jab.f1
{[] state}
q).jab.f2
{[] state}
Copy the code

PS: You can only set the current working context one level down from the root. This is truly unfortunate.

Recommendations for Namespacing:

  • Use namespacing at arbitrary depth to create hierarchical organization of global variables, both data and functions.

  • Keep related functions and any globals they require together in an appropriate context in the hierarchy with a descriptive name.

  • By convention, q namespaces are all lower case.

  • Define all global entities from the root context using fully qualified names.

  • Always refer to global entities using fully qualified names.

Reference: code.kx.com/q4m3/12_Wor…

By Jeffry A. Borror