Send Feedback
Skip to content

12. Workspace Organization

12.0 Overview

The collection of all q entities that have been created in a q session comprises the workspace. This includes not only explicitly created global variables but also ancillary items such as enumerations, open handlers, tables mapped into memory 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 programs or 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.

This is the simplest example of a condition in which two portions of a program contend for the same resource. Things get more complicated when concurrency is introduced, in which case the contention can lead to what is called a race condition. There is a school of programming that says mutable shared state is the root of all evil in distributed programming. Fortunately q is single-threaded by default and the implementation of threads (wisely) does not support mutating global variables. Nonetheless, simple name clashes are still a real problem.

12.1 Namespaces

A partial solution to name clashes is namespaces, which is the idea of placing a (usually hierarchical) structure on names. This is customarily done by specifying a separator character, which is distinct from other valid name characters. You are familiar with this construct from the file system used by most operating systems. The Unix file system uses the separator / and the Windows file system uses \.

The idea is that a compound name with embedded separators represents a nested series of containers. For example, in the Unix file system, the path /mydir/myfile represents a directory (folder, container) mydir that holds a file myfile. Or /d1/d2/fn represents a directory d1 that contains a directory d2 that contains the file fn. The root container, designated / in Unix, holds all other entities. We point out that, as written, there is an ambiguity as to whether /d represents a directory d or file on the root. This can be resolved by the file having an extension such as f.txt while a directory does not have such an extension; but this is not always the case.

Like namespaces in many programming languages, q works similarly, using . as the separator. For example, .jab.x represents a variable x defined in the jab namespace. The namespace jab has the symbolic name `.jab and the fully qualified name of the variable x is `.jab.x.

Note

In prior generations of q, as well as previous versions of this text, the term "context" was used to represent the collection of all entities instantiated in a specific namespace. That usage has been deprecated on the KX Documentation website and now a namespace is conflated with the collection of entities living in it. We shall try to follow this convention but will occasionally use the term context in its original meaning when we wish to emphasize the actual entities as contrasted with the naming convention. Hey, we try to follow the rules.

A notable difference between q namespaces and other hierarchical structure notations is the way the root is handled. Naming for entities in the root does not work as you might expect based on the file system analogy. The syntactic form .name does not refer to the global variable name in the root context; rather, it refers to the namespace name which lives alongside the root. The fully qualified name for an entity x in the root is simply x or in analogy with the file system `..x.

This has the following consequence that causes grief to qbies.

Namespaces Fact of Life #1: Inside a function body there is no syntactic way to distinguish between a local variable x and a global x in the root namespace.

If you follow the namespace conventions at the end of this chapter, you can minimize potential discomfort. However, based on real-world experience, we point out the following.

Important

Inside a function you cannot access the global variable foo in the root context as .foo. Instead, .foo refers to the dictionary that refers to all entities in the foo context. If you assign a value to .foo you wipe out the entire .foo context. I have seen this too many times in code from folks who come from other language environments without learning the idiosyncrasies of q. Some even argue with me about it.

12.2 Namespace Implementation

Namespaces in q are implemented with dictionaries. A namespace is implemented as a dictionary of special form whose keys are the symbolic names of the variables created in the namespace. The dictionary associates each symbolic variable name with its currently assigned value.

A namespace name has the same restrictions as q variable names except that . is used as separator. The following are all valid names of namespaces in the root.

.q                      / reserved by KX
.a                      / also reserved by KX
.cat
.an_ugly_ namespace_name

Thus .cat.bubba represents the entity bubba in the .cat namespace.

With that out of the way, we can nest namespaces.

.cat.bengal.nuba
.cat.abyssinian.orion
Here we have the entity nuba in the bengal namespace under the cat namespace; or the orion entity in the abyssinian namespace under the cat namespace.

Astute readers already have a question. No, not how many cats do I have. How do we distinguish between the first case, in which .cat.bubba is the variable bubba in the cat namespace, and the second case where .cat.bengal is a namespace? For example, can we emulate a file system, which sometimes appends the separator / to indicate a directory (container). The answer is pretty much the same as for file system pointed out above. It depends on the actual semantics of the entity.

Namespaces Fact of Life #2: The interpretation of a fully qualified name depends on its underlying value. If it is a properly constructed dictionary, it is a namespace; otherwise, it is a variable.

KX reserves all root namespaces of a single letter, as well as .kx, for its own use. As of this writing (July 2025) the namespaces that are actively used by Kx include .h, .j, .m, .o, .q, .u, .z and .Q. Some of these are hidden from public view. It is worth noting that many built-in utility functions and variables live in these namespaces. Those with no documentation on the KX Documentation website are for KX's own use and are not intended for public use and their implementation may change without notice.

Important

Although q prohibits you from re-assigning its own functions during program execution, some programmers think it is clever to meddle with the q.k file and override the KX definitions or insert their own functions. This is bad practice bordering on malfeasance. It courts disaster should KX change the internal implementation of its routines or add routines of the same names in a future release.

12.3 Creating Namespaces

When you start a fresh q session without any specific directives, all global variables you subsequently create live in the root context unless you specify otherwise. All previous examples in this tutorial have created global variable variables in the root because we didn't know any better – i.e., we used only unqualified variable names.

Tip

Be very careful if you leave all your globals in the root as this will almost inevitably lead to name clashes as your program grows and more scripts are loaded. There will be no warnings or complaints from the interpreter when this happens; it will simply overlay the previous definitions. As the author's father was fond of saying: You're cruisin' for a bruisin'.

Start a fresh q session and create some global variables, beginning in the root. To display all names in a namespace we use (key) with `. which is the name of the root.

q)answer:42
q)calculate:{(x*x)+(-1*x)+1}
q)key `.
`answer`calculate

There is no equivalent to mkdir that creates a namespace before it can be used. Simply specify a qualified name and any included namespace(s) will be created if necessary. We display all (visible) namespaces in the root with key ` and nothing more.

q).foo.a:42 / namespace foo created
q).foo.double:{2*x} / existing namespace foo used
q)key `
`q`Q`h`j`o`foo
q)key `.foo
`a`double

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

We can now load that script without clobbering our existing variables. After loading it we see the following, so we have solved the original problem posed in the introduction to this chapter.

q).bar.a:43
q).bar.double:{2*x}
q)key `
`q`Q`h`j`o`foo`bar
q)key `.bar
`a`double
q).foo.a
42
q).bar.a
43

There is no need to stop at one level of namespace. You can nest as deeply as you like and all the intermediate contexts will be created as needed. Suppose we work for a company Adiabatic Programming Laboratory. We might structure our library of computation routines something like the following.

q).apl.core.calc.array.fill0:{0.0^x}
This single assignment causes the nested series of contexts apl, core, calc and array to be created and the function fill0 to be placed in the innermost namespace array. Now we see the context core under the root. The initial backtick is an artifact of the way the context is stored.

q)key `.apl
``core

12.4 Namespace as Dictionary

As mentioned previously, a namespace is implemented as a specially formatted q dictionary. Its keys are the symbolic names of the variables it contains, each of which is associated with the current value of the so-named variable.

Namespaces are just dictionaries that live in the workspace along with ordinary variables. Although they have a special form and meaning to q, we can inspect them just as with our own dictionaries.

When we start a fresh q session, the root dictionary contains q's own namespaces and has no other (visible) entries. We can display the contents of a namespace by applying get to its symbolic name. Here is the root upon startup of a fresh q session. Since it is empty we apply .Q.s1 to unveil its cloak of invisibility.

q)get `.
q).Q.s1 get `.
"(`symbol$())!()"

After defining variables in the root, a populated dictionary emerges.

q)answer:42
q)calculate:{(x*x)+(-1*x)+1}
q)get `.
answer | 42
calculate| {(x*x)+(-1*x)+1}

Tip

The entries in a context dictionary are sorted by name.

Things get more interesting when we create a namespace under the root.

q).jab.wrong:43
q)key `
`q`Q`h`j`o`jab

q)get `.
answer | 42
calculate| {(x*x)+(-1*x)+1}

q)get `.jab
    | ::
wrong| 43
q).Q.s1 get `.jab
"``wrong!(::;43)"

Observe that the newly created namespace dictionary for .jab does not appear in the root namespace display; it is a separate dictionary.

Now let's examine the .jab namespace directory itself. We see the keys are symbolic names including `wrong for our newly created variable. And we also see 43 associated with `wrong in the value list.

Zen moment: Pause as you realize that the variable we defined with wrong:42 is the name-value association in this dictionary.

What is the significance of the first key-value pair for the empty symbol and the nil item? The nil (::) in the first item of the value list is there to keep the list from collapsing to a simple list of the type of the first variable defined. This would happen for an atom variable since the dictionary is updated in place with (,:). The type of nil matches no other type so no collapse to a simple list is possible.

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

q)`.[`answer]
42
q)`.jab[`wrong]
43

In fact you could use this to access an obscured global inside a function but there is a better way.

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

While this sort of thing makes for cute parlor tricks, we do not advise using it in real code.

Recommendation: Always use get and set to retrieve and store global variables by name inside functions. This way you never have to remember arcane visibility rules.

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

Warning

We strongly advise against modifying q namespace dictionaries directly. One misstep could scramble your data. Instead let q maintain them in the normal course of creating and deleting variables.

12.5 Expunging from a Namespace

We have seen that a namespace is a dictionary that maps a namespace's entity names to their values. This means that in order to expunge an entity from a namespace, we could directly remove it from the dictionary. But this is bad practice since it uses the underlying implementation and the actual expression is prone to typing error.

Instead, q provides a special overload of the (delete) template explicitly for this purpose. In a fresh q session,

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$()
Here we issue the \v command to display the names of variables in a namespace (current namespace if none is specified) to verify that the variables are gone.

12.6 Saving and Loading Namespace Content

Since a namespace is a dictionary, you can persist it – i.e., 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

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 `:Q4M/data/root

Warning

When you do this you overwrite any globals in the root with those in the saved version. No warnings or confirmations. Be careful what you ask for.

If you organize your globals carefully, this makes checkpointing the state of your application very simple. This is also done behind the scenes in certain q operations.

12.7 Working in a Namespace

We return to the analogy of paths in the file system. The operating system keeps track of the current working directory for your program. This is merely a bit of state that points to a logical location in the file system that serves as the default "stem" for deeper names. This means that it allows paths for items at or below the current working directory to be specified in relative form.

For example, if we start with the current working directory as the root and we wish to access a file /db/2025.03.01/trades.dat, then we must use the full path. However, if we issue the OS command,

cd /db
then /db becomes the current working directory and we can specify the path of the file as simply 2025.03.01/trades.dat. Note the lack of the leading /, which signifies a relative path as opposed to an absolute one. This provides significant simplification for deeply nested directory structures, which often arises in complex environments.

A q session has a similar concept. At any point in the session there is a current namespace. At the beginning of a fresh q session, the current namespace is the root unless you specify otherwise and you use absolute names for all global variables.

We can change the current namespace with the \d command (for "directory" by analogy to the file system). Once you change the current namespace, you can use names relative to it for global variables. Here we create two globals from the root namespace and then switch the current working context to show relative names.

q).jab.counter:0
q).jab.counter+:1
q)\d .jab
q.jab)counter
1
q.jab)counter+:1
q.jab)counter
2

Notice that the q prompt changes to let you know the current namespace. Things seem all well and good so let's go down another level.

q)\d .jab.util
'.jab.util
[0] \d .jab.util
    ^

What the fun?

Namespace Fact of Life #3: You can only set the current working namespace one level down from the root. This is truly unfortunate.

In our journey through namespace terra incognita, we move on to a subtle point about how globals are bound during function definition. To illustrate, start a fresh q session and do the following.

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}

We began by defining a variable state in the root context to hold a two-character code for one of the 50 United States of America. Also from the root namespace, we define a function f1 in the .jab context that returns the value of the unqualified state. Since the current namespace is root, this refers to state in the root.

Next we switch to the .jab context and define a variable state there that holds the integer null representing the value of a finite state machine, along with a function f2 that returns the value of unqualified state in the current .jab namespace.

We return to the root context, apply each function using an empty argument and observe the expected values. Namespacing has done its job by avoiding a clash between the disparate state variables, so what's the issue? Look closely at the display of the two function bodies. They are identical! How do you know which state variable is accessed at runtime?

Namespaces Fact of Life #4: An unqualified global reference in a function is bound to the current namespace in effect at the point of the function definition. That namespace is not explicitly displayed in the function body. This has the undesirable consequence of requiring a potential user of the function to know the circumstances of its definition. You could use (value) on the function to discover this.

At this point might you be tempted to abandon namespaces altogether, but don't. If you adhere to the following recommendations, you will avoid the issues mentioned above and namespaces will work effectively.

Recommendations for Namespaces:

  • Use namespaces 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 namespace in the hierarchy with a descriptive name.
  • By convention namespaces in q are all lower case with no underscores. Nothing except good taste prevents you from being unconventional.
  • Define all global entities from the root context using fully qualified names.
  • Always refer to global entities using fully qualified names.

Essentially the last two recommendations amount to avoiding use of \d to switch the current namespace. Here is one way to define the functions f1 and f2 above in a fresh q session so that everything is explicit.

q).jab.geo.state:`NY
q).jab.geo.f1:{[] .jab.geo.state}
q).jab.turing.state:0N
q).jab.turing.f2:{[] .jab.turing.state}
q).jab.geo.f1
{[] .jab.geo.state}
q).jab.turing.f2
{[] .jab.turing.state}

Now there is no confusion for readers of this code.