How to Work with Functions
This page explains how to define, call, and manage functions in q, including arguments, scoping, projections, and return behavior.
A function in q is a reusable block of logic, a sequence of expressions that execute in order. Functions can take zero or more inputs and return an output.
Overview of steps
1. Define a function
Define a function in q using lambda notation, which consists of:
-
A pair of curly braces
{}
-
An optional argument list (up to 8 named parameters) inside square brackets
[]
-
One or more expressions separated by semicolons
;
For example, let's define a lambda named plus
that adds two arguments:
q
q)plus:{[x;y] x + y}
In q, the term lambda refers to any function defined using this notation, whether it's anonymous or assigned to a name.
2. Treat functions as data
Functions in q behave like other data types such as integers or symbols. Use the type
function to confirm a function’s type:
q
q)func:{[x;y]x+y}
q)type func
100h
A return value of 100h
indicates a lambda function. You can:
-
Pass functions between processes via IPC
-
Use them as arguments or return values in other functions
-
Store them in collections like lists or dictionaries
Functions that operate on or return other functions are called higher-order functions.
3. Understand function rank
The rank (or valence) of a function refers to the number of arguments it accepts.
You may define functions with up to 8 arguments. Using more raises a params
error:
q
q)f:{[a;b;c;d;e;f;g;h]a*b-c+d*e-f*g+h}
q)f:{[a;b;c;d;e;f;g;h;i]a*b-c+d*e-f*g+h+i}
'params
[0] f:{[a;b;c;d;e;f;g;h;i]a*b-c+d*e-f*g+h+i}
^
This limit does not restrict the amount of data passed; each argument may be a complex structure such as a list or dictionary.
Functions are often described by their rank:
- Niladic: accepts no arguments (rank of 0)
- Monadic/Unary:accepts one argument (rank of 1)
- Dyadic/Binary: accepts two arguments (rank of 2)
Functions can inspect the type or length of their arguments at runtime using type
or count
.
This allows them to branch logic accordingly; for example, built-ins like load
and hopen
behave differently depending on input type.
4. Call functions with arguments or projections
Call a function in q using function application syntax: write the function name followed by arguments inside square brackets []
, separated by semicolons ;
:
q
q)func:{[x;y]x+y}
q)func[22;33]
55
Call functions without arguments
If the function doesn’t require arguments, include empty brackets when calling it:
q
q)func:{show "func called";}
q)func[]
"func called"
Passing extra arguments to such a function has no effect:
q
q)func:{show "func called";}
q)func[111]
"func called"
Evaluate expressions in arguments
q evaluates expressions inside argument positions before passing them:
q
q)func:{[x;y]x*y}
q)func[2+8;2]
20
Handle rank errors
Calling a function with too many arguments results in a rank
error.
q
q)func:{[x;y]x*y}
q)func[22;33;231;123]
'rank
Call functions by name
You can call a function directly using its variable name or indirectly by symbol:
q
q)func:{[x;y]x+y}
q)func[10;2]
12
q)`func[10;2]
12
q)a:`func
q)a[10;2]
12
Use prefix notation
Functions that take only one argument also support prefix notation, omitting brackets:
q
q)f:{[x]x*x}
q)f[22]
484
q)f 22 / prefix notation
484
Calling a function with fewer arguments than declared results in a projection.
5. Use anonymous functions
Use anonymous functions when you want to define logic inline without assigning it to a named variable. Anonymous functions are useful for one-off operations or dynamic evaluation.
Define and call inline
Define an anonymous function using {}
and call it immediately:
q
q){[x]x*x} 2
4
Apply to a list
Use an anonymous function with each
to apply logic to every item in a list. In the example below, we convert a list of server addresses into connection handles. The anonymous function first transforms each string into a symbolic handle using hsym
, then opens a connection using hopen
:
q
q)machines:("qa-machine1:1234";"qa-machine2:5234";"qa-machine3:2234")
q)h:{[x]hopen hsym `$x} each machines
6 7i
Store functions in collections
Store anonymous functions in data structures like lists, dictionaries, or tables to support dynamic dispatch. This allows you to choose and execute a function at runtime based on context, rather than hardcoding function names.
6. Work with function arguments
Use different techniques to define and validate function arguments in q. This includes implicit argument names and optional type checking introduced in kdb+ 4.1 (2024.02.13).
Use implicit arguments
Define a function without naming its arguments. q automatically provides up to three implicit arguments:
-
x
(first) -
y
(second) -
z
(third)
These names become available based on which are referenced in the function body.
q
q)f:{22+x+x} / implicit single argument x
q)f[2]
26
q)f:{y-x} / implicit single argument x and y
q)f[2;10]
8
q)f:{z*z} / z used, but x and y are not used. implicit argument z, with x and y added even though not used
q)f[2;3;4]
16
Anonymous functions also benefit from implicit arguments:
q
q)h:{hopen hsym `$x} each ("qa-machine1:1234";"qa-machine2:5234") / anonymous function with an implicit single argument x
A function with named arguments is called signed. Without named arguments, it is unsigned.
Apply argument type checking
As of kdb+ 4.1 (2024.02.13), define argument types inside the function signature using a single-character type code after a colon (:
). For example, use `j
to restrict a parameter to type long
.
q
q)f:{[someLong:`j] 2*someLong}
q)f[2]
4
q)f[2.2]
'type
[1] f:{[someLong:`j] 2*someLong}
This enforces type safety and improves code robustness. For more details, see the kdb+ 4.1 release notes.
7. Manage local and global variables
Use local and global variables to control scope and data visibility in q functions. Understand how variable assignment and access behave depending on the context.
Define local variables
Create local variables inside a function using :
. Local variables:
-
Exist only during function execution
-
Remain invisible outside the function scope
-
Cannot be used in call-by-name functions
-
Stay hidden from local functions nested inside the same scope
Local assignments don't affect external values:
q
q)v:22
q)f:{[x]x:100}
q)f[v]
100
q)v
22
Modifying elements of a passed-in vector also has no lasting effect:
q
q)v:1 2 3
q)f:{[x]x[0]:100}
q)f[v]
q)v
1 2 3
Assignments with :
apply locally and don't overwrite global variables:
q
q)a:22 / global 'a'
q)f:{[x]a:33;b:44;a+b+x} / function creates local variables 'a' and 'b', local 'a' takes precedence
q)f[1]
78
q)a / global 'a' remains unchanged
22
q)b / no global 'b' defined, error occurs
'b
[0] b
^
Resolve references to names not defined locally from the current namespace:
q
q)a:42 / assigned in root
q)f:{a+x}
q)f 1 / f reads a in root
43
q){a:1000;f x}1 / f reads a in root
43
Avoid assigning locals conditionally - local variables initialize to empty list ()
and conditional paths may leave them undefined:
q
q)t:([]0 1)
q){select from t}[] / global t
x
-
0
1
q){if[x;t:([]`a`b)];select from t} 1b / local t
x
-
a
b
q) {if[x;t:([]`a`b)];select from t} 0b / local t is ()
'type
[4] {if[x;t:([]`a`b)];select from t}
^
Use global variables
Access
Resolve names not defined locally from the current namespace:
q
q)v:10
q)f:{[x]x+v}
q)f[2]
12
Resolution depends on the active namespace:
q
q)v:10
q)\d .foo
q.foo){[x]x+v}[2] / errors as .foo.v doesn't exist
'v
[1] {[x]x+v}
^
q.foo))\
q.foo)v:20
q.foo){[x]x+v}[2] / resolves to .foo.v
22
q.foo)\d .
q){[x]x+v}[2] / resolves to v in top namespace
12
Assign
Assign global values using ::
. These assignments persist beyond the function call:
q
q)v:0
q)f:{[x]v::x;x+x}
q)f[1]
2
q)v
1
q)f[2]
4
q)v
2
Avoid confusion when assigning to globals with the same name as locals. ::
still binds to the local scope if already defined:
q
q)v:0
q)f:{[x]v:0;v::x;x+x}
q)f[1]
2
q)v
0
Use set
to reliably write to global variables:
q
q)v:0
q)f:{[x]v:0;`v set x;x+x}
q)f[1]
2
q)v
1
Use get
to access global variables by name:
q
q)x:22
q)f:{[x]show x;} / prints local variable x
q)f 101
101
q)f:{[x]show get `x;} / prints global variable x
q)f 101
22
8. Pass variables by reference
Avoid copying large datasets by passing variable names to functions and modifying them using get
and set
.
By default, q passes arguments by value. When working with large vectors or tables, use symbolic references instead to access or update values efficiently.
q
q)a:10 20 30 40 50
q)f:{[x]show get x;x set 10 20 30;}
q)f[`a] / call function, passing variable name
10 20 30 40 50
q)a / function used set to change variable
10 20 30
This method avoids unnecessary data copying and enables controlled updates from within functions.
9. Return values and exit behavior
Control what your function returns, whether it's a result, an early exit, or no value at all.
Return the result of the last expression
If the final expression does not end with a semicolon, the function returns its value:
q
q)f{[x]x+x}
q)f[2]
4
Return early with an empty assignment
To return early (before the final expression), use an empty assignment (:
followed by a value):
q
q)c:0
q)f:{a:6;b:7;:a*b;c::98}
q)f 0
42
q)c
0
Create a void function
Create a void function
Use a semicolon on the last line to suppress the return value. The function returns the generic null
.
q
q)f:{2*x;} / last statement is empty
q)f 10 / no result shown
q)(::)~f 10 / matches generic null
1b
Signal an error or abort
To abort evaluation immediately, use signal
, which is '
with a value to its right.
q
q)c:0
q)g:{a:6;b:7;'`TheEnd;c::98}
q)g 0
{a:6;b:7;'`TheEnd;c::98}
'TheEnd
q)c
0
Example: Check argument type before proceeding. Use early return or error signaling to validate input:
q
q)f:{if[7h<>type x;'"type"]count x}
q)f[33 22]
2
q)f[22.4]
'type
[0] f[22.4]
^
10. Use projections
When you call a function with fewer arguments than its defined rank (i.e. the number of required parameters), q returns a projection - a partially applied function. This projection “locks in” the arguments you’ve supplied and waits for the rest. The rank of the resulting projection is the original rank minus the number of arguments provided.
Other languages may refer to this concept as currying.
Create a projection by omitting arguments
Call a function with fewer arguments than it requires to return a projection (a partially applied function):
q
q)add:{[x;y]x+y}
q)add42:add[42;] / create a projection with the first parameter value fixed to '42'
q)add42[2] / execute the original function with parameters 42 and 2
44
q)add42[4] / execute the original function with parameters 42 and 4
46
Create projections in stages
Chain projections by supplying arguments over multiple stages:
q
q)addm:{[x;y;z]x+y+z}
q)addm[2;;3][6] / projection with 1st and last parameter, then executed providing the remaining 2nd paramter
11
q)addm[2;;][2;][6] / projection with the 1st parameter (2nd/3rd remaining), then create a new projection with only the first of the remaining 2 parameters needed, then execute the function when last parameter provided
10
Preserve logic in projections
The function logic is frozen at the time you create the projection. Redefining the original function later does not affect previously created projections:
q
q)f:{x*y}
q)g:f[3;] / triple
q)g 5
15
q)f:{x%y}
q)g 5 / still triple
15
Use full-length argument lists
Use placeholders (;
) for omitted arguments to clearly show the projection:
q
q)foo:{x+y+z}
q)goo:foo[2] / discouraged
q)goo:foo[2;;] / recommended
Check the type of projections
As with functions, projections have their own data type. Use type
to inspect it:
q
q)f:{[x;y]x+y}
q)type f
100h
q)type f[1;]
104h
Projections let you fix arguments and reuse or defer execution until more input is provided. Use them to simplify function reuse and construct dynamic logic flows.