Core Concepts

q Terminology

A concise glossary of core q vocabulary — functions, rank, operators, iterators, projections, apply, and composition.

You can write a lot of q without knowing this vocabulary. But once you start working with iterators, projections, compositions, and the functional forms of qSQL, these terms appear constantly — and reading documentation or production code becomes much easier once they're familiar.

Note: This page summarizes the terminology most useful for day-to-day q development. For complete definitions, see the code.kx reference and Q for Mortals.

Function

A function maps one or more arguments to a result. Every function in q is either a primitive — built into the language — or built from primitives:

Function
├── Primitive            — built into q
│   ├── Operator   +  -  %  &  |  ...
│   ├── Keyword    sum  count  avg  ...
│   └── Iterator   '  /  \  ':
│
└── Built from primitives
    ├── Derived function   +/         (iterator applied to a function)
    ├── Lambda              {x+y}
    ├── Projection          f[10;;]
    └── Composition         neg sum   (a "train" of functions)

The rest of this page works through each of these in turn. The most common way to create your own function in q is with a lambda.

Lambda

A lambda is a user-defined function written inside {}:

{x+y}    / add two numbers
{x*x}    / square a number

The implicit argument names are x, y, and z — these are the only names q recognises automatically. For more control, or more than three arguments, write an explicit signature inside [...] at the start:

{[a;b] a+b}           / name arguments freely, up to 8
{[a;b;c;d] a+b+c+d}

A lambda with a signature is called signed. A lambda without a signature is called unsigned. Expressions inside {} are separated by ;, and the value of the last expression is returned.

Argument & Rank

An argument is an input to a function. In a lambda, arguments are named x, y, z — or whatever names appear in its signature:

{x+y}[10;4]   / x:10, y:4 -> 14

A function's rank is the number of arguments it takes:

RankNameExample
0Nullary{42}
1Unaryneg
2Binary+
3Ternaryssr
4Quaternary{[a;b;c;d] a+b+c+d}
VariableVariadic@

A variadic function accepts a variable number of arguments — common with iterators and derived functions.

Operator & Keyword

An operator is a primitive binary function that can be written infix as well as prefix/bracket, e.g. +, -, %, &, |:

1+2        / infix
+[1;2]     / bracket form — same result

A keyword is a built-in function written as a word rather than a symbol: sum, count, avg, deltas, mod. Unary keywords such as sum are applied prefix or with brackets — 1 2 3 sum isn't valid. Binary keywords such as mod, within and xbar can also be applied infix, like operators: 10 mod 3.

Both operators and keywords are built into q — the difference is in how they're written and applied.

Iterator

Think of an iterator as a function modifier: applied to a function, it returns a new — derived — function that iterates differently. The core iterators are ' (each), / (over), \ (scan), and ': (each prior) — a few more (each-left \:, each-right /:, each-parallel peach, and case) are covered on the Iterators page:

+/    / + modified by over       -> reduces a list:  +/[1 2 3] = 6
+\    / + modified by scan       -> running totals:  +\[1 2 3] = 1 3 6
,'    / , modified by each-both  -> pairwise join:   ,'[1 2;10 20] = (1 10;2 20)

Derived Function

A derived function is the result of applying an iterator to a function — it isn't the iterator itself. The whole chain, from primitive to result, looks like this:

+              operator (primitive function)
│
│  / is the iterator — "modify + with over"
▼
+/             derived function
│
│  applied to data
▼
+/[1 2 3] -> 6   application
  • / is the iterator.
  • +/ is the derived function produced by applying / to +.

A derived function is a normal function and can be applied like any other.

A derived function can even have a different rank from the function it's built from. + is binary (+[10;20] → 30), but +/ can be called as either unary or binary:

+/[1 2 3 4]        / unary  -> 10  (sum the list)
+/[100;1 2 3 4]    / binary -> 110 (100 plus the sum of the list)

This is common among derived functions, and is one reason they're often treated as functions in their own right rather than as a shorthand notation.

Advanced — syntax details: A derived function's calling syntax also depends on how many arguments you give it. +\ (scan) can be called unary or binary — and the binary form can be written with brackets or infix:

+\[1 2 3]      / unary  -> 1 3 6   (running totals)
+\[1; 1 2 3]   / binary -> 2 4 7   (seed 1, then running totals)
1 +\ 1 2 3     / same as above, written infix -> 2 4 7

Note what is missing from that list: +\ 1 2 3, written bare with nothing on its left, is an error. This reflects a general rule: a function derived by postfix iteration is always infix, regardless of its own rank. Written bare, it expects something to its left; with nothing there, +\ 1 2 3 fails to parse — and so does count' txt. Bracket notation always works (+\[1 2 3], count'[txt]), and wrapping the derived function in parentheses turns it into an ordinary value that can be applied prefix instead:

(+\) 1 2 3        / 1 3 6
(+\)[1; 1 2 3]    / 2 4 7
txt:("the";"quick";"brown";"fox")
(count') txt      / 3 5 5 3

Projection

A projection is created by supplying only some of a function's arguments, leaving the rest as placeholders to be filled in later — similar to partial application in languages like Python or JavaScript:

f: {x+y+z}
g: f[10;;]     / fix x:10, leave y and z open
g[20;30]       / 60

Projections are heavily used in functional qSQL and iteration.

Atomic, Uniform & Aggregate Functions

These describe how a function's result relates to the shape of its input:

  • Atomic — applies itself to a list item-by-item automatically: neg 1 2 3 already returns -1 -2 -3, so wrapping it in each changes nothing. Formally, atomic is a special case of uniform where f x ~ f' x — applying f to a list gives the same result as applying it to each item individually. Most primitive operators (and functions like signum) are atomic. The same idea applied to binary functions is called scalar extension: when one argument is an atom and the other is a list, the atom is automatically broadcast to match — 1 2 3 + 10 → 11 12 13.
  • Uniform — preserves the top-level structure of its input. For example, deltas 10 15 20 18 returns 10 5 5 -2 — one result for each input item. Not all uniform functions are atomic — deltas depends on consecutive items and can't be applied atom-by-atom.
  • Aggregate — reduces a list to a smaller result, often an atom: sum, min, max, avg, count.

Function Application

q supports several ways to apply a function to its arguments:

StylePositionExample
Bracketfunction, then arguments in [...]{x+1}[2], +[1;2]
Prefixunary function, then its argument — no bracketsneg 10, count 1 2 3
Infixbinary operator, written between its two arguments1+2, 10 mod 3
Postfixiterator, written after the function it modifies+/, count'

Bracket form is the most general — every function can be applied this way, regardless of rank.

Prefix and infix describe where a function sits relative to its arguments — the data it operates on. Postfix is different: it describes where an iterator sits relative to the function it modifies, not relative to any data. In count', the Each iterator ' is postfix to count — together they form a derived function, which is then applied to data: count'[(1 2;3 4 5)] → 2 3. Only iterators can be applied postfix — for any other function, this isn't valid syntax.

(The keyword each gives the same result with friendlier syntax: count each (1 2;3 4 5). Here each is a binary keyword applied infix, with the function count as its left argument.)

Applicable Value

An applicable value is anything that can be applied to arguments or indexes — not just functions. Lists, dictionaries, and tables are applicable too, via the same [...] syntax — even file and process handles are applicable values, used for I/O and IPC:

count[1 2 3]          / function applied to an argument -> 3
(10 20 30)[1]         / list applied to an index         -> 20
(`a`b!10 20)[`a]      / dictionary applied to a key       -> 10
t:([]a:1 2 3)
t[1]                  / table applied to a row index      -> a| 2

This is why "application" in the q documentation isn't limited to functions — the term applicable value covers any of these.

Application & Invocation

To apply a function means to evaluate it on its arguments. Application and invocation are used interchangeably in q — both simply mean "calling" a function. Bracket, prefix, and infix (see Function Application above) are just different notations for the same thing:

{x+y}[10;20]   / bracket
sum 1 2 3      / prefix
1+2            / infix

Each is simply an application of a function to its arguments — the syntax differs, but the meaning doesn't. The operators @ (Apply At) and . (Apply) let you write application explicitly, as a function in its own right, which is useful when the function to apply is itself held in a variable or passed as an argument.

Apply At (@)

Apply At applies a unary function to a single argument value — that value may itself be a list:

count @ 1 2 3    / same as count 1 2 3  -> 3
neg @ 10         / same as neg 10       -> -10

@ has other forms too — e.g. Amend At uses it for indexing and in-place updates:

@[1 2 3; 1; :; 100]   / set index 1 to 100 -> 1 100 3

These indexing/amendment forms are covered in later sections — the point here is just that @ is more than a simple "call this function" operator.

Apply (.)

Apply applies a function to a list of arguments — useful once a function takes more than one:

{x+y} . (1;2)        / 3
{x+y+z} . (1;2;3)    / 6

Composition (Trains)

When two or more values are written next to each other with no argument between them, q treats the whole sequence as a single function called a train, evaluated right to left — each value is applied to the result of the one on its right. neg sum is a 2-element train, and is q's standard way to write a composition. Applied straight to data, it just works:

neg sum 1 2 3   / sum 1 2 3 -> 6, then neg 6 -> -6

But if you try to save the train under a name with no data attached, you'll hit a type error:

c: neg sum   / type error — q tries to apply neg to the value `sum` itself

One way to turn the train into a reusable function is to finish it with @ (for a unary chain). This tells q "this is a function waiting for an argument", not "apply neg to sum right now":

c: neg sum@
c 1 2 3      / -6
c (1 2;3 4)  / -4 -6

neg sum@ is shorthand for neg@sum@, with all but the last @ dropped — the same pattern extends to longer chains:

tc: til count@   / count and til are both unary
tc "abc"         / count "abc" -> 3, then til 3 -> 0 1 2

Advanced: if the last function in the chain is binary, use . instead of @:

di: reciprocal(%).   / % is binary ("divide into")
di 2 3               / 2%3 -> 0.6667, then reciprocal -> 1.5

Summary

Before tackling iterators, make sure these terms are familiar: function, primitive vs built from primitives, lambda (signed/unsigned), argument, rank (nullary/unary/binary/ternary/variadic), operator vs keyword, iterator, derived function, projection, atomic/uniform/aggregate, the four application styles (bracket/prefix/infix/postfix), applicable value, application/invocation, apply (@/.), and composition (trains).