Send Feedback
Skip to content

10. Execution Control

10.0 Overview

Basic function application in q provides sequential evaluation of a series of expressions. In this chapter, we demonstrate how to achieve non-sequential execution in q.

10.1 Control Flow and Conditionals

When writing vector operations in q, the cleanest code and best performance is generally obtained by avoiding explicit loops and conditional execution. For those times when you simply must write iffy or loopy code, q has versions of the usual constructs.

Important

Control flow constructs in this section involve branching in the byte code generated by the q interpreter. The offset of the branch destination is limited (currently to 65025 byte codes), which means that the sequence of q expressions that can be contained in any part of $, if, do, or while must be short.

At some point, insertion of one additional statement will break the camel's back, resulting in a 'branch error. This is q's way of rejecting bloated code. In this situation, factor code blocks into separate functions. Better yet, redesign your code.

10.1.1 Basic Conditional Evaluation

Languages of C heritage have a form of in-line if, called conditional evaluation, of the form,

exprcond ? exprtrue : exprfalse

where exprcond is an expression that evaluates to a Boolean (or int in C and C++). The result of the expression is exprtrue when exprcond is true (or non-zero) and exprfalse otherwise.

The same effect can be achieved in q using the ternary overload of $.

$[exprcond; exprtrue; exprfalse]

Here exprcond is an expression that evaluates to a boolean atom. Similar to C, the result of exprcond can be any type whose underlying value is an integer. The result of the conditional is the evaluation of exprtrue when exprcond is not zero and exprfalse if it is zero.

q)$[1b;42;9*6] 
42 
q)$[0b;42;9*6] 
54 

Cond acts like a function in that it always returns a value which may be nil. It is good practice for exprtrue and exprfalse to have the same type. This is not enforced in q as it is in statically-typed functional languages.

Tip

Cond is not a true function even though it does return a value. In fact, it is a control structure. As such, Cond cannot be used in the query templates.

The brackets in Cond do not create lexical scope. This means that variables created within the body exist in the same scope as the conditional. For example, in a fresh q session the variable a in the following is a global that persists outside the conditional.

q)a 
'a 
q)$[1b;a:42;a:43] 
42 
q)a 
42 

While evaluation of function arguments in q is eager, evaluation of the expressions in the conditional is short circuited, meaning that only the one selected for return is evaluated. Again in a fresh q session,

q)a 
'a 
q)b 
'b 
q)$[1b;a:42;b:43] 
42 
q)a 
42 
q)b 
'b 

Observe that a test for zero in exprcond is redundant; simply reverse the order of the second and third arguments.

q)z:0 
q)$[z=0;1.1;-1.1] 
1.1 
q)$[z;-1.1;1.1] 
1.1 

Tip

In contrast with earlier versions of q, some null values are acceptable for exprcond. However, float nulls do not work which is to be expected since float tests should not be used for conditionals due to multiplicative tolerance.

q)v:0N 
q)$[v;`isnull;`notnull] 
`isnull 

10.1.2 Extended Conditional Evaluation

In languages of C heritage, the if-else construct has the form,

if (expr_cond) {
    statement_true1;
    .
    .
    .
}

else {
    statement_false1;
    .
    .
    .
}

where expr_cond is an expression that evaluates to a boolean (or int in C and C++). If the expression expr_cond is true (that is, non-zero) the first sequence of statements in braces is executed; otherwise, the second sequence of statements in braces is executed.

A similar effect can be achieved in q using an extended form of conditional evaluation.

$[exprcond; [exprtrue1; …]; [exprfalse1; …]]

where exprcond is an expression as in the basic conditional. When exprcond evaluates to non-zero, the first bracketed sequence of expressions is evaluated in left-to-right order; otherwise, the second bracketed sequence of expressions is evaluated.

q)v:42
q)$[v=42; [a:6;b:7;`Everything]; [a:`Life;b:`the;c:`Universe;a,b,c]]
`Everything
q)$[v=43; [a:6;b:7;`everything]; [a:`Life;b:`the;c:`Universe;a,b,c]]
`Life`the`Universe

The extended forms of the conditional are still functions with return values.

Languages of C heritage have a cascading form of if-else in which multiple tests can be made,

if (expr_cond1) {
    statement_true11;
    .
    .
    .
}
else if (expr_condn) {
    statement_truen1;
    .
    .
    .
}
.
.
.
else {
    statement_false;
    .
    .
    .
}

In this construction, the exprcondi are evaluated consecutively until one is true (non-zero), at which point the associated block of statements is executed and the statement is complete. If none of the expressions passes, the final block of statements, called the default case, is executed.

A similar effect can be achieved in q with another extended form of conditional execution.

$[exprcond1;exprtrue1; …;exprcondn;exprtruen;exprfalse]

Here exprfalse is optional. In this form, the conditional expressions are evaluated consecutively until one is non-zero, at which point the associated exprtruei is evaluated and its result is returned.

If none of the conditional expressions evaluates to non-zero, and exprfalse is present, it is evaluated and its result is returned as the default case. Observe that exprfalse is the only expression that is not part of a pair, as it has no guarding conditional expression – i.e., it is the odd person out.

If none of the conditional expressions evaluates to non-zero and exprfalse is not present, a nil item is returned. Presumably you are using Cond purely for side effects in this case.

Note

Any condition other than the first is only evaluated if all those prior to it have evaluated to zero. Otherwise put, a condition evaluating to non-zero short-circuits the evaluation of subsequent ones.

q)a:0 
q)$[a=0;`zero; a>0;`pos; `neg] 
`zero 
q)a:42 
q)$[a=0;`zero; a>0;`pos; `neg] 
`pos 
q)a:-42 
q)$[a=0;`zero; a>0;`pos; `neg] 
`neg 
q)$[a=0;`zero; a>0;`pos] 
q).Q.s1 $[a=0;`zero; a>0;`pos] 
"::"

Finally, the previous extended form of conditional execution can be further extended by substituting a bracketed sequence of expressions for any exprtrue or exprfalse.

$[expr_cond1;[expr_true11; ]; ;
    expr_condn;[expr_truen1; ];
    [expr_false1; ]]

If you use this, have the courtesy to align your code properly so that q coders can identify it as non-vector Python.

Tip

When using the final extended form of Cond to return values, be mindful about the placement of semicolons. You must have a semicolon after each closing bracket of the conditional expressions but not after the closing bracket of the default expression. And do not place a trailing semicolon before any of the closing brackets or that branch will return the nil item.

The first (and hopefully last) time the author used this form was in writing a parser for Thai text. It took an entire morning to get the first complicated nested conditional right. After several more bouts, the author realized he should have written the higher-level routines in an imperative language, reserving the core table-driven engine for q.

10.1.3 Vector Conditional Evaluation

Vector Conditional is a true functional form and not control flow but we place it here for comparison with Cond from the previous section. Ternary vector-conditional evaluation ? interleaves two lists and has the form,

?[vb;exprtrue;exprfalse]

where vb is a simple boolean list and exprtrue and exprfalse are of the same type and are either atoms or vectors that conform to vb.

The result conforms to vb and selects from exprtrue in positions where vb is 1b and exprfalse in positions where vb has 0b. All arguments of vector-conditional are fully executed. In other words, there is no short circuiting of evaluation.

Note

Unlike Cond, Vector Conditional only accepts booleans for the conditional test.

Here is a typical example that interleaves two lists.

q)?[101b; "abc"; "ABC"] 
"aBc" 

The following example substitutes 42 for items in a list that are multiples of 3.

q)L:til 10 
q)?[0=L mod 3; 42; L] 
42 1 2 42 4 5 42 7 8 42 

Vector Conditional is especially useful with table columns.

q)t:([] c1:1.1 2.2 3.3; c2:10 20 30; c3:100 200 300) 
q)update mix:?[c1>2.0; c3; c2] from t
c1 c2 c3 mix
--------------
1.1 10 100 10
2.2 20 200 200
3.3 30 300 300

There are no extended forms of vector conditional. You can get a similar effect by nesting vector conditional expressions but like other conditionals it is subject to the restriction that no internal code jump can exceed 65025 byte codes. This limits the degree of nesting that can be used in practice. With t as above,

update band:?[c2 within 5 15; 1; ?[c2 within 16 25; 2; 3]] from t
c1 c2 c3 band
---------------
1.1 10 100 1
2.2 20 200 2
3.3 30 300 3

10.1.4 Case

The Case construct is a generalization of vector conditional and has the following form:

(i1;...;in)'[L1;...;Lm]

where (i1;...;in) is a list of integers and the Li are lists of equal count or atoms. Note that the count m of the lists on the right hand side of ' must be greater than or equal to the count n of the integers on the left hand side. The result is a list of count n whose j-th item is Lj[ij]. This is a form of scattered indexing.

Note

Case was omitted from previous editions of this tutorial through oversight.

We start by showing an example of Vector Conditional re-cast to Case. Note that in Vector Conditional the 1b values choose from the first list while in Case the 1 values choose from the second list. Nonetheless, the two constructs are clearly equivalent in this situation.

q)?[101b; "abc"; "ABC"] 
"aBc" 
q)1 0 1'["ABC"; "abc"] 
"aBc" 
q)(1101b)'["abc"; "ABC"] 
"aBc" 

Next we extend the Case example to index across multiple lists.

q)2 0 1'["abc"; "ABC"; "012"] 
"0bC" 
q)2 0 1'["abc"; "A"; "012"; "<=>"] 
"0bA" 

Here is how to parameterize both operands:

q)Is:2 0 1 
q)Is'["abc"; "A"; "012"; "<=>"] 
"0bA" 
q)Ls:("abc"; "A"; "012"; "<=>") 
q)(Is') . Ls 
"0bA" 

Finally we demonstrate the use case for which we suspect this primitive was created: selecting fields from amongst columns across rows of a table. First we generate some random numbers formatted in US phone number style. Then we use these to construct a table of individuals and their preferred primary contact. For fun we use a sideways join.

q)show pnums:{"(",x[0],")-",x[1],"-",x[2]} each  0 3 6_/:raze'[string 0N 10#90?10] 
"(083)-618-8108" 
"(909)-687-2646" 
"(116)-011-5818" 
   
q)show t:([] name:`nuba`devi`lorenzo; pref:`h`o`m),'flip `o`h`m!0N 3#pnums 
name    pref o                h                m                
--------------------------------------------------------------- 
nuba    h    "(083)-618-8108" "(207)-809-9593" "(290)-536-9023" 
devi    o    "(909)-687-2646" "(323)-580-3742" "(415)-073-3753" 
lorenzo m    "(116)-011-5818" "(459)-819-5476" "(636)-772-6313"    

Now we can use Case to retrieve the preferred calling number dynamically as specified in pref.

q)select name, prefnum:(`o`h`m?pref)'[o; h; m] from t 
name    prefnum           
------------------------ 
nuba    "(207)-809-9593" 
devi    "(909)-687-2646" 
lorenzo "(636)-772-6313" 

10.1.5 if

The imperative if statement conditionally evaluates a sequence of expressions. It is not a function and does not return a value, so it is strictly used to control side effects. It has the form,

if[exprcond;expr1;…;exprn]

The exprcond is evaluated and if it is non-zero the expressions expr1 thru exprn are evaluated sequentially left-to-right. As with other conditionals, the brackets do not create lexical scope, so variables defined in the body exist in the same scope as the if.

Tip

There is no else to go with if; use Cond instead. Should you find that this cramps your coding style switch to Python.

Here is an example that creates two global variables and modifies one.

q)a:42 
q)b:98.6 
q)if[a=42;x:6;y:7;b:a*b] 
q)x 
6 
q)y 
7 
q)b 
4141.2 

Tip

Well-written q code rarely needs if. In previous releases of q, a legitimate use of if was to check function arguments and abort execution to avoid failure. This can now be done more cleanly by using patterns to check/modify the arguments on entry which obviates the need for this in the function body and pushes the responsibility back to the caller. See 6.1.11.

10.1.6 do

The imperative do statement repeats execution of a block of statements. It has the form:

do[exprcount;expr1;…;exprn]

where exprcount must evaluate to an non-negative integer. The expressions expr1 thru exprn are evaluated exprcount times sequentially left-to-right. Note that do is a statement, not a function, and does not have an explicit result.

The following expression is a loopy computation of n factorial. It iterates n-1 times, decrementing the factor f on each pass.

q)n:5 
q)do[-1+f:r:n; r*:f-:1] / do not do this! 
q)r 

Tip

The best recommendation about usage of do is: Don't! Use the equivalent form of Over instead. See 6.7.8.

10.1.7 while

The imperative while statement is an iterator of the form,

while[exprcond;expr1;…;exprn]

where exprcond is evaluated and the non-zero expressions expr1 thru exprn are evaluated sequentially in left-to-right order. This is repeated as long as exprcond is non-zero. The while statement is not a function, does not have an explicit result and does not introduce lexical scope.

Here is the loopy factorial redone with while.

q)f:r:n:5 
q)while[f-:1;r*:f].   / do not do this either! 
q)r 
120 

Tip

The author has never used a while statement in actual code; use the equivalent overload of Over instead – see 6.7.8. That said,while can be used to spin while monitoring an external resource for an expected change but this puts the program in a tight loop that will drive up CPU utilization.

10.1.8 Return and Signal

Normal function application evaluates each expression in the function body in sequence and terminates after the last. There are two mechanisms for ending the execution early: one to return successfully and the other to abort execution.

To terminate function application immediately and return a value, use an empty assignment – that is, : with a value to its right and no variable to its left. For example, in the following instrumented function, application is terminated and the result is returned in the fourth expression. The final expression is never evaluated.

q)f:{0N!"Begin"; a:x; b:y; :a*b; "End"} 
q)f[6;7] 
"Begin" 
42 

Note

In most situations it is best to design your code so that the last expression in a function body is hugged by the closing } and is thus returned. Ending the function with :r} is redundant and repetitious.

To abort function execution immediately with an exception, use Signal, which is single-quote ', with an error message to its right. The error message can be a symbol or a string. For example, in the following function, execution will be aborted in the fourth expression. The final expression that assigns c is never evaluated.

q)g:{0N!"Begin"; a:x; b:y; '"End"; a:b} 
q)g[6;7] 
"Begin" 
'End 
  [0]  g[6;7] 
       ^ 

A function issuing Signal causes the calling routine to fail and this will ripple all the way up the call chain unless protected evaluation is used to trap the exception. See 10.1.8 for details on protected evaluation.

A legitimate use of the if statement is to terminate execution with an exception. The following snippet would typically reside inside a function body.

    {  
   
    if[a<50; 'Bad a]; 
    
  } 

10.1.9 Protected Evaluation

Languages of C++ heritage have the concept of protected execution using try-catch. The idea is that an unexpected condition arising from any statement enclosed in the try portion does not abort the program. Instead, control transfers to the catch block, where the exception can be handled or passed up to the caller. This mechanism allows the call stack to be unwound gracefully.

Q provides a similar capability using ternary forms of Application. Ternary @ is used for unary functions and ternary . is used for multivalent functions. The syntax is the same for both.

@[fmon;a;exprfail]

.[fmul;Largs;exprfail]

Here fmon is a unary function, a is single argument, fmul is a multivalent function, Largs is a list of arguments, and exprfail is an expression or function. In both forms, the function is applied to its argument(s). Upon successful application, protected evaluation returns the result of the application. Should an error arise, when exprfail is a function it is applied to the resulting error string; otherwise its value is returned.

Important

If the application of exprfail results in an exception, the protected call itself fails.

These variations are useful when processing input received from users. In the following examples, you would replace the unhelpful error message with more useful error handling.

Suppose a user wishes to enter dynamic q expressions. You could place the expression in a string and pass it to value. This is a huge security exposure, so you should never do this in such a naïve fashion in a production system. Nonetheless, we can do it in a learning environment. The problem is that if the user types an invalid q expression, the generated exception will cause your application to fail. Instead apply value with protected execution.

s:"6*7" 
@[value; s; show] 
42 

q)s:"6*`7" 
q)@[value; s; show] 
"type" 

Ternary. provides similar protected execution for multivalent functions.

q)prod:{x*y}.             / we have the missing o 
q).[prod; (6;7); show] 
42 
q).[prod; (6;`7); show] 
"type" 

10.1.10 Trap with Backtrace

The utility namespace .Q has two enhanced forms of protected evaluation corresponding to ternary @ and . covered in 10.1.6.

The ternary .Q.trp works just like ternary @ except that it also provides backtrace information in the event that the primary application fails. It has the form:

.Q.trp[f;x;g]

where f is a unary function to be applied, x is the argument to which f is applied and g is binary function to be invoked in case the application fails with a Signal. In this event g is called with two arguments: the error string and the backtrace object.

Here is an example showing how you can emulate q's own debug messages but with full backtrace by using .Q.sbt to format the backtrace object for display.

q)f:{6*x} 
q)trpErr:{2 "error: ",x,"\nbacktrace:\n",.Q.sbt y;} 
q).Q.trp[f;`7;trpErr] 
error: type 
backtrace: 
  [2]  f:{6*x} 
           ^ 
  [1]  (.Q.trp) 

  [0]  .Q.trp[f;`7;trpErr] 
       ^ 

Note

This can be used to send the full backtrace for a remote call. Normally the q IPC protocol only sends the error. See the KX Documentation for sample code. Or better yet, work it out yourself. Simply assign trpErr or similar as the appropriate event handler in the .z namespace.

The ternary .Q.trpd works just like .Q.trp except that it is for protected dot evaluation with . and so expects a multivalent function f.

q).Q.trpd[{x*y};(6;`7);trpErr] 
error: type 
backtrace: 
  [2]  {x*y} 
         ^ 
  [1]  (.Q.trpd) 

  [0]  .Q.trpd[{x*y};(6;`7);trpErr] 

       ^ 

10.2 Debugging

Debugging in q formerly harkened back to the bad old days, before the advent of integrated development environments, when "real men" debugged by inserting println in their code. The q gods didn't give debugging much consideration because their code always ran correctly the first time. The good news is q now has basic debug and tracing capability; the bad news is there is no notion of break points. Things are considerably better than inserting print statements in the early q days.

When expression evaluation fails, the console displays a backtick followed by a short and an often cryptic error message. This is followed by a dump of the failed operation and the the offending line of code with a caret pointing to the offending operation. The operation and operands are usually accessible in .z.ex and .z.ey. Many errors in your program will manifest as either 'type or 'length, indicating you caused an incompatibility in function arguments somewhere in the bowels of q. The challenge is to discover the root cause of this error.

Since q is interpreted, at any point in the execution of a program the entire runtime environment is accessible. If you meditate just a bit, you'll realize that an integrated debugger for a compiled language essentially simulates the environment of an interpreter. The debugger has to go to great lengths to create the environment that is readily available in an interpreter.

10.2.1 Runtime Errors

Let's investigate the debug capabilities in q, which are much improved over earlier versions. We choose simple functions so that the code doesn't get in the way. Here we have a function f that is supplied an argument of bad type for its call to function g.

q)g:{a:x*2;b:a+y;3*b} 
q)f:{a:42; a+g[x;y]} 
q)f[2;`3] 
'type 
  [2]  g:{a:x*2;b:a+y;3*b} 
              ^ 
q)) 

The extra parenthesis ) at the q prompt indicates that execution has been suspended and the debugger entered. At this point, you have the full power of the q environment available at the console to inspect the current state of your suspended program. For example we can examine the arguments and locals of the failing function. Since b was not successfully assigned, it still carries its initialization to ).

q))x,y,a 
2 
`3 
4 
q))b 
q)) 

We can display the offending primitive and argument with system variables.

q)).z.ex 
+ 
q)).z.ey 
4 
`3 

If you enter an expression that Signals an error while you are exploring during a debug session, it will spawn another level debug session. This will be indicated by another ) after the q prompt. To end that additional session enter \ at the prompt.

q)){x-y}[x;y] 
'type
  [5]  {x-y} 
        ^ 
q)))\ 
q)) 

Warning

During development it is easy to forget to clear the debugger and continue editing and testing your code. It is not uncommon to accrue a stack of ) in this situation, which you can unwind one at a time with \. Do not slip and enter \\ as this will terminate the q session with extreme prejudice – i.e., no confirmation prompt.

We can move up the suspended stack frame to the f execution context using (`).

q)). 
'type 
  [2]  g:{a:x*2;b:a+y;3*b} 
              ^ 
q)).Q.bt[] 
>>[2]  g:{a:x*2;b:a+y;3*b} 
             ^ 
  [1]  f:{a:42; a+g[x;y]} 
              ^ 
  [0]  f[2;`3] 
       ^ 

At any time during the debug session you can ask the debugger to (re)display the current frame with &.

q))& 
'type 
  [2]  g:{a:x*2;b:a+y;3*b} 
              ^ 

Now say you have finished your investigations and made any temporary fix-ups. You now have three options:

  • Abort the suspended execution with \.
  • Signal an error from the deepest frame in the stack with the message err 'err. This destroys that frame.
  • Exit f at this point returning value r with :r.

We show each with our suspended execution.

q)f[2;`3] 
'type 
  [2]  g:{a:x*2;b:a+y;3*b} 
              ^ 
q))\ 

q)f[2;`3] 
'type 
  [2]  g:{a:x*2;b:a+y;3*b} 
              ^ 
q)):42 
168 

q)f[2;`3] 
'type 
  [2]  g:{a:x*2;b:a+y;3*b} 
              ^ 
q))'yikes 
'yikes 
  [1]  f:{a:42; a+g[x;y]} 
              ^ 

The q old-timers here will agree that is a significant improvement over the early days. But there is still one limitation. As things stand we can't resume execution of the suspended function with the next statement after the break.

10.2.2 Breakpoints

Let's say we want to set a breakpoint in our program to suspend execution before an identified error and understand how it arises. There is no breakpoint capability built into the debugger but it's easy enough to make our own. Just insert a statement that you know will fail – for example, reference an undefined name. – common are `break or stop.

g:{a:x*2; break; b:a+y; 3*b}
f:{a:42; a+g[x;y]}
f[2;`3]
'break
  [2] g:{a:x*2; break; b:a+y; 3*b}
             ^

As before, the limitation is that we cannot resume execution after the break. The solution is to force the break one level down. This will enable us to exit that level, which will resume execution with the next statement in the suspect function. Make sure the name you choose is not defined in global scope.

)g:{a:x*2;  {break}[]; b:a+y; 3*b} 
q)f:{a:42; a+g[x;y]} 
q)f[2;`3] 
'break 
  [3]  g@:{break} 
        ^ 

First note that our anonymous function is displayed after its caller's name suffixed with @. Since there isn't anything happening at this stack frame we use (`) to direct the debugger to move one level up the execution stack – i.e., to the suspect function g. Now you have the full state of the execution environment of function g available as before.

q)` 
  [2]  g:{a:x*2;  {break}[]; b:a+y; 3*b} 
              ^ 
q))x,y,a 
2 
`3 
4 

We do single-step copy/paste execution of the next statement, discover the offending argument and fix it.

q))b:a+y 
'type 
  [5]  b:a+y 
        ^ 
q))y:3 

Now we have the following options:

  • Abort the suspended execution with \.
  • Signal an error from the deepest frame in the stack with the message err 'err. This destroys that frame.
  • Return from g at this point, returning value r with :r.
  • Direct the debugger back down the call stack with . and resume execution of g with the next statement after the break using an arbitrary return value :0.

We return to the lower level of the stack frame inside our breaking function and continue execution from there with an arbitrary return value 0. Since all is now well, it remains to remove the breakpoint and retry with correct input.

q)). 
'break 
  [3]  g@:{break} 
        ^ 
q)):0 
63 
q)g:{a:x*2; b:a+y; 3*b} 
q)f[2;3] 
63 

It is a lunchtime project to code a tiny library to implement conditional breakpoints that can be turned on and off outside the executing program. We chose to do it using a dictionary of the same form as namespaces to hold our globals so as to avoid name clashes.

Admittedly this isn't as slick as an IDE with integrated debugger primitive but it works well enough in practice that finding and correcting bugs is no longer a hindrance in q development.

Tip

You will spend much more time figuring out why your q code isn't working properly than manually debugging. As my mother used to say, stop yer whinin' and get back to work.

10.3 Scripts

A q script is a collection of a q statements stored in a text file with an extension of .q. A script can contain any q expressions or commands. The lines of the script are parsed and evaluated sequentially from top to bottom. Global entities created during execution of the script live in the workspace after the script is loaded.

10.3.1 Creating and Loading a Script

You can create a script in any text editor and save it with a .q extension. Just make sure that "enhancements" – especially smart quotes, auto capitalization, etc. – are turned off in the editor. For example, enter the following lines and save to a file named trades.q in the QHOME directory which defaults to the current working directory on start up unless you set it.

trades:([] sym:(); ex:(); time:(); price:())

`trades insert (`IBM;`N; 12:10:00.0; 82.1)
`trades insert (`IBM;`O; 12:30:00.0; 81.95)
`trades insert (`MSFT;`N; 12:45:00.0; 23.45)
`trades insert (`IBM;`N; 12:50:00.0; 82.05)
`trades insert (`MSFT;`N; 13:30:00.0; 23.40)

Now issue the `load` command,

```q
\l trades.q
,0
,1
,2
,3
,4

You can verify that the trades table has been created and the records have been inserted:

count trades
5

A script can be loaded at the start of the q session, or at any time during the session using the \l command. The load command can be executed from the console or from another script. See Chapter 12 for more on commands.

10.3.2 Special Notations

You can comment out a block of code (i.e., multiple lines) in a script by surrounding it with matching / and \ each the initial character on its own line. An unmatched naked \ causes everything after it to be ignored, effectively signaling the end of the script.

Here is a script snippet that demonstrates block comments.

a:42
b:42
/
this is a block of
comment text
b:43
and b will not be changed
\
a:43  / this line will be executed
\
nothing from here on will be executed
b:44
and b will still be 42 

After this script (and no other) is loaded in a fresh q session, a will be 43 and b will be 42.

Multi-line expressions are permitted in a script and they must have a special form. The first line must not be indented – i.e., it begins at the left of the line with no initial whitespace. Any continuation lines must be indented, meaning that there is at least one whitespace character at the beginning of the line. Empty lines between expressions are permitted and ignored. The block runs until the first line not starting with whitespace.

Tip

You might be tempted to align the the closing } with the opening of the function definition, as in other languages. This doesn't work; it must be indented.

We recommend that dictionary definition syntax, table definition syntax and function definition syntax follow the same rule for splitting across multiple lines:

A dictionary, table or function definition can have line breaks after a closing square bracket ] or after a semicolon separator ;.

We recommend qSql queries follow this rule:

A qSql query can have line breaks after the template components (select, from, etc.) or after the a subphrase separator ,.

10.3.3 Passing Parameters

Parameters are passed to a q script at q startup similarly to command line parameters in a C or Java program. They are strings that are not explicitly declared and are accessed positionally corresponding to the order in which they are passed.

Note

As of this writing (July 2025), parameters can be passed when a script is loaded at q startup but not when a script is loaded with the \l command.

Specifically, the system variable .z.x is a list of strings, each of which contains the char representation of an argument present when the script was invoked. For example, the script captureargs.q,

/ script that captures its first three arguments
p0:.z.x 0;
p1:.z.x 1;
p2:.z.x 2;

can be loaded during q startup,

q.exe captureargs.q 42 forty 2.0

and in the new q session you will find,

q)p0
"42"
q)p1
"forty"
q)p2
"2.0"

You must cast the string arguments if they are intended to be other data types. For example, if the script convertargs.q,

/ script that converts its first three arguments
p0:"I"$.z.x 0;
p1:"S"$.z.x 1;
p2:"F"$.z.x 2;

is loaded with,

q.exe convertargs.q 42 forty 2.0

in the new q session you will find,

q)p0
42
q)p1
`forty
q)p2
2f

You will find the fully parsed command line with which q was invoked as strings in .z.X.

q).z.X
"q/m64/q"
"captureargs.q"
"42"
"forty"
"2.0"

You will find the symbolic name of the q script executed on load in .z.f.

q).z.f
`captureargs.q

You can extract the string values of any start-up parameters whose names are identified with a leading - by invoking .Q.opt on .z.x. This returns a dictionary of parameter names and values.

q/m64/q captureargs.q /path/for/output -parm1 42 -parm2 -parm3 forty 2.0
q).Q.opt .z.x
parm1| ,"42"
parm2| ()
parm3| ("forty";"2.0")

q).Q.x
"/path/for/output"

Note above that after running .Q.opt you can find non-command line parameters in .Q.x.

You can have q do type checking and set defaults for command line parameters by providing a dictionary of default values of the correct types to be matched in .Q.def and apply this to the result of .Q.opt .z.x.

q -param2 2000.11.19 -param3 bad 
q).Q.opt .z.x
param2| "2000.11.19"
param3| "bad"       
q).Q.def[`param1`param2`param3!(42;1999.01.01;23.1)] .Q.opt .z.x
param1| 42
param2| 2000.11.19
param3| 0n

Observe that a missing value is set to the default and a value that does not match the type specified is set to the corresponding null.

10.4 Pattern Matching

Pattern matching is a powerful technique for analyzing data structure that gained popularity in functional programming languages and is now incorporated in various forms in mainstream languages. One way to think of it is as a generalization of regular expression matching on strings. You present raw data to be compared to a pattern expressed in some formal way. If it matches, portions of the data of interest are identified and extracted. Deconstruction without destruction.

10.4.0 Overview

Pattern matching in q has many features that you will find in the pattern matching in other languages but not all. You can match on all major q data types, from atoms to lists, dictionaries, tables and functions. It provides support for features long missing in q such as multiple assignments in a single statement, type checking, default parameter values and more. It also supports continuations on match – i.e., the generalized case statements common in pattern matching in many languages.

The elephant in the room is the notation for q pattern matching. While it is a natural extension of the normal q notation for data structures and functions, it leads to greater symbolic density. This may take some getting used to, even for veteran q programmers. The solution is the punch line to the vintage New York City joke. A visitor asks a New Yorker on the street how to get to Carnegie Hall and New Yorker replies, "Practice! Practice! Practice!"

Important

Patterns in q are literals, meaning a pattern cannot be stored in a variable.

A pattern in q is syntactically an adaptation of an expression that defines a q entity. For example, a pattern can arise from a literal constant, a variable, a list, a dictionary, a table, or a function parameter. A general pattern can be built recursively from these. Such a pattern serves as the target for comparison with a source data entity, sometimes called the subject in other treatments of the topic. A q pattern is usually enclosed in parentheses unless the context precludes ambiguity.

A q pattern match can be requested in certain situations where a data entity is created. As of this writing (Sep 2025) this can occur during assignment, when arguments are passed to a function or in a conditional pattern. The source data must fulfill all the requirements of the pattern for a match to occur; otherwise, the match fails.

When a match occurs, the matching portions of the source data are extracted and passed to the corresponding entities in the pattern, whose action proceeds – i.e., assignment, argument passing or the conditional continuation. The resulting value of a successful match is the entire source entity that was matched.

10.4.1 Basic Patterns in Assignment

Let's start with a simple example in assignment. A valid q variable name enclosed in parentheses constitutes a name pattern. It its simplest form, it can be used on the left hand side (lhs) of assignment.

q)(a):42
q)a
42

Tip

This syntax was valid in q before the introduction of pattern matching, where it was ordinary assignment.

Here the atom 42 on the right hand side (rhs) of the assignment is the source data and a is the target pattern. In this situation, parentheses around the pattern are necessary to distinguish it from ordinary assignment. There are no other requirements in the pattern to restrict 42 being assigned to a variable a, so the match is successful and the assignment proceeds.

At this point you may be thinking, "So what? We've just added notational complexity to a simple assignment." Don't be too quick to judge. Let's continue to the next example.

A list pattern has the form of a general list. For example

 (a;b) 

is a pattern denoting a list with two name sub-patterns.

Tip

In a list pattern all the items are themselves patterns.

For a list pattern to match it must be presented with a source list of equal length, whereupon all items in the list are matched in turn. Parentheses aren't required around the item patterns.

Note

Optional space is permitted in patterns, just is in the expressions they derived from, but we omit it in this exposition in order to make pattern notation clear.

Placing our pair pattern on the lhs of an assignment requires a source list of exactly two elements on the rhs for a successful match. The rhs can be any expression producing a list and does not have to be in general form. So we expect the following will yield a successful match.

(a;b):1 2

Both slots of the pattern are matched name patterns, so both variables will be assigned from the corresponding items in the source.

q)(a;b):1 2
q)a
1
q)b
2

Voila! The ability to assign multiple variables in a single assignment in q.

Let's continue with this example and introduce some additional basic patterns. A constant pattern is made from a literal constant. In our example, suppose we are interested only in pairs whose initial item is 42. We could match as follows.

q)(42;b):42 43
q)b
43

What happens if the match fails? An exception is generated and the variable b is not assigned.

q)(42; b):1 2
'match
  [0]  (42; b):1 2
        ^
q)b
43

Here is a more complicated example showing that a nested list pattern can assign items to variables at arbitrary levels of nesting:

q)(x;(10;y);(30;(300;z))):(1;(10 20);(30; 300 400))
q)x,y,z
1 20 400

The following example shows how to assign multiple variables by choosing values from two lists using vector conditional (see 10.1.3):

q)(x;y;z):?[0=(til 3) mod 2; `a`b`c; `A`B`C]
q)x,y,z
`a`B`c

The next example shows how to assign multiple variables by choosing values from multiple lists using the Case operation (see 10.1.4).

q)(x;y;z):0 1 2["abc"; "ABC"; "123"]
q)x,y,z
"aB3"

We can also specify a null pattern by leaving the corresponding position empty. This indicates that something must be there but we don't care what it is. In this case, when the match is made, the corresponding value on the rhs is captured but it isn't assigned individually.

Tip

The null pattern is often denoted by _ in mainstream languages.

q)(;b):1 2
q)b
2
q)(;b):((10 20; 30 40); 2)
q)b
2

Note

In contrast to other situations in q,
- You do not use :: to indicate an elided element in the null pattern.
- Scalar values are not extended to match a list pattern.

q)(:: ;b):1 2
'match
  [0]  (:: ;b):1 2
      ^
q)(a;b):42
'type
  [0]  (a;b):42
      ^

As with all patterns, you can assign the overall value of the pattern once the match processing is complete.

q)L:(a;b):1 2
q)a
1
q)b
2
q)L
1 2

To capture only pairs starting with 42,

q)L:(42;):42 43
q)L
42 43
q)L:(42;):43 42
'match
  [0]  L:(42;):43 42
        ^

Observe here that while the value matching a null pattern isn't itself assigned, it is captured as part of the overall pattern.

Some more notes on the list pattern. You can use the empty list as a pattern to match a general (untyped) empty list.

q)L:():0#(1;`1)
q).Q.s1 L
"()"
q)L:():0#42
'match
  [0]  L:():0#42
         ^

You can use an empty list with an unassigned list type check – i.e., upper case – to match a untyped empty list. (see 10.4.3 for type check pattens).

q)L:(:`S):0#`
q).Q.s1 L
"`symbol$()"
      ^
q)L:(:`s):0#(1;`1)
'type
  [0]  L:(:`s):0#(1;`1)
        ^
q)L:(:`S):0#(1;`1)
'type
  [0]  L:(:`S):0#(1;`1)

For pattern matching a singleton list see 10.4.2 for a welcome addition that brings singleton lists a bit closer to first class citizens.

Finally, as noted previously, the lists in a list pattern match must have the same length, otherwise an exception is raised.

q)(a;):1 2 3
'length
  [0]  (a;):1 2 3
      ^

Consequently you cannot use the list pattern to decompose a list into its head and tail as is common in many implementations of pattern matching. But we can get close.

q)(hd:first;tl:1_):{(x;x)} 1 2 3 4 
q)hd 
1
q)tl
2 3 4

Editorial

This isn't too surprising since q does not construct lists using cons to add a single item to a list as in some functional languages. The operator , appends a list vector rather than just a single item. Nevertheless, we think list pattern matching would be more useful if it allowed matching on lists of unequal length just as matching is allowed on dictionaries when not all keys are present in the pattern.

10.4.2 Data Structure Patterns

The index pattern is an extension of the name pattern that allows an item or items in a list to be assigned. In this case, specified index is a value and not a pattern itself.

q)L:10 20 30
q)(L[1]):200
q)L
10 200 30

You can also match and assign on multiple indices provided the source conforms to the pattern.

q)(L[1 2]):2000 3000
q)L
10 2000 3000

q)(m[0 1; 1 2]):(22 33;222 333)
q)m
1   22  33 
10  222 333
100 200 300

The dictionary pattern can be used with either the functional form ! or the bracketed dictionary definition form of dictionary creation. Akin to items in the list pattern, each item of the value portion of a dictionary pattern is itself a pattern. Matching is done by key and a matched value from the source is passed to the corresponding sub-pattern within the dictionary pattern.

q)(10 20!(v1;v2)):10 20!"ab"
q)v1
"a"
q)v2
"b"
q)(10 20!(v1;v2)):10 30!"ab"
'match
  [0]  (10 20!(v1;v2)):10 30!"ab"
             ^

q)([low:l;high:h]):`low`high!42 98
q)l
42
q)h
98
q)([low:l;high:h]):([low:43; high:99])
q)l
43
q)h
99

Unlike the case for list patterns, dictionary match does not require a full match on all keys. Keys in the source that are not present in the pattern do not signal an exception and are still present in the overall pattern assignment.

q)([high:h]):`low`high!41 97
q)h
97
q)d:([high:h]):`low`high!41 97
q)d
low | 41
high| 97

Analogous to lists, a null pattern can be used in the value portion of the dictionary pattern to indicate that a key-value pair must be present but we don't care to assign its value portion. In the dictionary definition form, the means placing nothing to the right of the : for that key.

q)([open:;low:l;high:h]):([open:77;low:44;high:100])
q)l
44
q)h
100
q)open
'open
  [0]  open
      ^
q)d:([open:;low:l;high:h]):([open: 77;low:44;high:100])
q)d
open| 77
low | 44
high| 100

As before, observe that an unassigned individual value is still part of the overall pattern assignment.

Warning

As of this writing (Sep 2025), while you can assign type checked values of items in nested lists, it does not seem possible to assign values of type checked fields in nested dictionaries even though you can assign or type check independently.

q)(x:`j;(10;y:`j);(30;(300;z:`j))):(1;(10 20); 30; 300 400))
q)x
1
q)y
20
q)z
400

q)([d:([a:v1;b:v2])]):([d:([a:10; b:20])]) 
q)v1
10
q)v2
20
q)([d:([a:(:`j);b:(:`j)])]):([d:([a:10; b:20])]) 
q)([d:([a:v1:(:`j);b:v2:(:`j)])]):([d:([a:10; b:20])])
'nyi
  [0]  ([d:([a:v1:(:`j);b:v2:(:`j)])]):([d:([a:10; b:20])])
                ^

Next, viewing a table as a flipped column dictionary, the table pattern can deconstruct a table into (unadorned) columns.

q)t:([] c1:10 20 30; c2:98.6 99 99.5)
q)([]c1:qty;c2:px):t
q)qty
10 20 30
q)px
98.6 99 99.5

As with dictionaries, not all columns need be present in the pattern to match. Here is how to extract an naked column vector without using exec.

q)([]c1:px):t
q)px
10 20 30

Keyed tables can be similarly deconstructed.

q)kt:([k:`aa`bb`cc] c1:10 20 30; c2:98.6 99 99.5)
q)([k:sym]c2:px):kt
q)sym
`aa`bb`cc
q)px
98.6 99 99.5

Observe that the key column must be present in the pattern for a keyed table match.

q)([]c1:qty;c2:px):kt
'nyi
  [0]  ([]c1:qty;c2:px):kt
         ^

Finally, viewing a table as a list of records, we can use a list pattern to extract records, although this is of limited usefulness since we cannot specify variable length lists. With t as above,

q)(rec1;rec2;rec3):t
q)rec1
c1| 10
c2| 98.6
q)rec2
c1| 20
c2| 99f
q)rec3
c1| 30
c2| 99.5

Certain operators can be used in an operator pattern. As of this writing (Sep 2025) only enlist, ! and the case of flip used in table creation are available.

The enlist pattern can be used to match and unwrap a singleton list.

q)(enlist a):1#42
q)a
42
q)(enlist a):42
'type
  [0]  (enlist a):42
       ^
q)(enlist a):42 43
'length
  [0]  (enlist a):42 43
       ^
q)(enlist L):1#(10 20; 30 40 50)
q)count 1#(10 20; 30 40 50)
1
q)count L
2

Previously we had to check for count 1 and non-positive type to identify a singleton.

Here is how to deconstruct a dictionary into its key and value lists in one swell foop.

q)(k!v):10 20!"ab"
q)k
10 20
q)v
"ab"

q)(k!v):([low:43; high:99])
q)k
`low`high
q)v
43 99

q)(k!):([low:42; high:98])
q)k
`low`high

q)kt:([k:`aa`bb`cc] c1:10 20 30; c2:98.6 99 99.5)
q)(tkeys!tvalues):kt
q)tkeys
k 
--
aa
bb
cc
q)tvalues
c1 c2  
-------
10 98.6
20 99  
30 99.5

How often we could use these!

We can use the flip pattern to extract the underlying column dictionary from a table.

q)(flip dc):([]a:1 2;b:3 4)
q)dc
a| 1 2
b| 3 4

q)(flip([c1:qty;c2:sym])):([]c1:10 20 30;c2:`a`b`c)
q)qty
10 20 30
q)sym
`a`b`c

10.4.3 Data Check and Modification Patterns

The type check pattern has the form:

p:`x

where p is a optional pattern – i.e., it can be the null pattern – and x is the type character for the type to match. A lowercase letter matches an atom and an uppercase letter matches a simple list.

q)(x:`f):3.1
q)x
3.1
q)(x:`f):3
'type
  [0]  (x:`f):3
        ^
q)(:`f):42.0

q)((x;y):`F):3 4f
q)x
3f
q)y
4f
q)((x;y):`F):3 4
'type
  [0]  ((x;y):`F):3 4
           ^

Here are patterns to match a string or a single char.

q)(str:`C):"abc"
q)str
"abc"
q)(ch:`c):"c"
q)ch
"c"

Here is how to match a nested list of symbols.

q)(a:`S;b:`S):(`a`b;`c`d`e)
q)a
`a`b
q)b
`c`d`e

Here is how to type check a field in a dictionary. Note that the parentheses on the lhs are required to avoid ambiguity with :: when the checked value is not assigned but are not required when the value is assigned.

q)([inp:(:`S)]):([inp:`d`e])
q)([inp:(:`S)]):([inp:`d])
'type
  [0]  ([inp:(:`S)]):([inp:`d])
           ^
q)([inp::`S]):([inp:`d])
'parse
  [0]  ([inp::`S]):([inp:`d])
          ^

q)([inp:(inp:`S)]):([inp:`x`y`z])
q)inp
`x`y`z
q)([inp:inp:`S]):([inp:`x`y`z])
q)

We shall see how to use type checking with function arguments in the following section.

The filter function pattern has the form:

p:expr 

where p is a optional pattern and expr is an expression that evaluates to a q entity that can be applied as a function – i.e., a q operator, a user-defined function or a projection. The (reduction of) expr is applied to the source value and the result is then matched to p.

q)(a:6*):7
q)a
42

q)(a;b:1+):1 2
q)a
1
q)b
3

q)(hd:first;tl:1_):{(x;x)} 1 2 3 4
q)hd
1
q)tl
2 3 4

q)([low:l;high:h:1+]):`low`high!42 98 
q)l
42
q)h
99

Q test question

Describe the three different uses of : in the last assignment.

10.4.4 Patterns on Function Parameters

Thus far we have only demonstrated patterns being used in assignment. They show real utility when used with function parameters where they can normalize incoming arguments, relieving the function author from handling bothersome edge cases in the function body.

We lead off with a use case that is the bane of any q programmer: coercing an argument to be a list. For example, the following function assumes that it receives a valid string (list of char) and fails when it is called with a single char.

q)f:{[str] str[0]}
q)f "a"
'type
  [1]  f:{[str] str[0]}
              ^

Traditionally the function author would insert a line of code to coerce the argument to a list but now she can do it with a pattern on the parameter. Observe that implicit parameters must be made explicit in this pattern.

q)f:{[str:(),] str[0]}
q)f "a"
"a"
q)f:{[x:(),] x[0]}

Next up, finally we can type check function parameters in a simple fashion.

q))f:{[ix:`j] "abc" ix}
q))f 1
"b"
q))f 1.1
'type
  [4]  f:{[ix:`j] "abc" ix}
            ^

But we can do better. When it makes sense to do so, we coerce the argument to the expected type using a filter function. This also gives a meaningful error message if the cast fails.

q)f:{[ix:`long$] "abc" ix}
q)f 1h
"b"
q)f 1.0
"b"
q)f `1
'type
  [1]  f:{[ix:`long$] "abc" ix}
                 ^

We can also use a filter function to default null values.

q)f:{[x:42^] x}
q)f 2
2
q)f 0N
42

Here is a snippet of code used by the author to allow the caller to supply a custom binary handler function yet also provide a default of retaining the first argument when the caller passes {}. In this example f is a dummy function that merely displays the result of the parameter pattern.

q)f:{[x:{$[x~{}; {[x;]x}; x]}] x}
q)f {}
{[x;]x}
q)f {y}
{y}

Q test question

What is the valence of each result?

10.4.5 Conditional Pattern

The conditional pattern is the q version of a common type of pattern matching found in many languages. Roughly speaking, it is a generalized "case" statement that allows you to specify different actions depending on the internal form of an entity. The format of the conditional pattern is a slightly modified version of $ Conditional.

:[v;p1;r1;p2;r2;;rdef]

where v is a value to be matched; p1, p2, ... are patterns; and r1, r2, …, and the optional rdef are expressions for return values. The value v is matched to each pattern in turn until a match succeeds, at which point the corresponding return expression is evaluated and the result returned. If no match occurs for any p, rdef is evaluated and returned.

As with $ Conditional, you can extend this form to compute a result inside a bracketed sequence of statements, with the right-most value inside the brackets being returned.

:[v;p1;[;r1];p2;[;r2];;[;rdef]]

As with $ Conditional, no lexical scope is created within the body of the conditional pattern and the bracketed portions can have side effects in the containing scope (even globally…caution advised). Finally, result expressions corresponding to failed matches, as well as any match after the first successful match, are not evaluated.

Here is a simple example in which the type of the value is checked against various alternatives and the corresponding null is returned upon match.

q):[1i;:`e;0ne;:`f;0n;:`h;0Nh;:`i;0Ni;:`j;0N;0N]
0Ni
q):[1e;:`e;0ne;:`f;0n;:`h;0Nh;:`i;0Ni;:`j;0N;0N]
0Ne

Observe that while rdef is optional, its absence will trigger an exception if no match occurs.

q):[1i;:`e;0ne;:`f;0n;:`h;0Nh;:`i;0Ni;:`j;0N]
0Ni
q):[2025.09m;:`e;0ne;:`f;0n;:`h;0Nh;:`i;0Ni;:`j;0N]
'match
  [0]  :[2025.09m;:`e;0ne;:`f;0n;:`h;0Nh;:`i;0Ni;:`j;0N]
       ^
q):[2025.09m;:`e;0ne;:`f;0n;:`h;0Nh;:`i;0Ni;:`j;0N; 0N]
0N

Here is a conditional pattern used with list patterns to determine how many bytes are in a UTF-8 character in q by matching high order bits.

q)sz:{[uch] :[`long$5#0b vs first uch;(1;1;1;1;0);4;(1;1;1;0;);3;(1;1;0;;);2;(0;;;;);1]}
q)sz "a"
1
q)sz "ñ"
2
q)sz "ॐ"
3
q)sz "😄" 
4

The cast to long is solely to banish a swarm of binary b's buzzing in the bit patterns. Also, we don't need a default pattern since the patterns are exhaustive.

Tip

When assigning the result of the conditional pattern you can include a space between the multiple : or not. The q interpreter will not confuse this with other uses of the digraph :: even though the person reading your code might. In the following, the first form is kinder to your reader.

q)sz: :[`long$5#0b vs first uch;(1;1;1;1;0);4;(1;1;1;0;);3;(1;1;0;;);2;(0;;;;);1]
q)sz::[`long$5#0b vs first uch;(1;1;1;1;0);4;(1;1;1;0;);3;(1;1;0;;);2;(0;;;;);1]

We continue the previous example with the extended form of the conditional pattern to extract the encoded numeric value of a UTF-8 character. We name the intermediate variables according to which bits they extract and return the value as three hexadecimal bytes, so we can backtrack to the UTF-8 encoding. For ease of reading, we have indented the function code in a .q text file, which is loaded with \l instead of being entered manually at the console.

utf8xval:{[x:(),]
   v: :[`long$5#0b vs first x; 
      (1;1;1;1;0); [
         uvv:-3#0b vs x[0]; 
         vvwwww:-6#0b vs x[1]; 
         xxxxyy:-6#0b vs x[2]; 
         yyzzzz:-6#0b vs x[3];
         2 sv uvv,vvwwww,xxxxyy,yyzzzz]; 
      (1;1;1;0;); [
         wwww:-4#0b vs x[0]; 
         xxxxyy:-6#0b vs x[1]; 
         yyzzzz:-6#0b vs x[2];
         2 sv wwww,xxxxyy,yyzzzz]; 
      (1;1;0;;); [
         xxxyy:-5#0b vs x[0]; 
         yyzzzz:-6#0b vs x[1]; 
         2 sv xxxyy,yyzzzz]; 
      (0;;;;); [
         yyyzzzz:-7#0b vs x[0];
         2 sv yyyzzzz]
  ];
  -3#0x00 vs v}
\l /Users/jeffryborror/Documents/scratch.q

q)utf8xval "*"
0x00002a
q)utf8xval "ñ"
0x0000f1
q)utf8xval "ॐ"
0x000950
q)utf8xval "😄"
0x01f604

Our final example demonstrates the power of the conditional pattern to deconstruct a data structure according to cases. This is a common use in functional programming. In our situation we use a record (dictionary) to mimic a simple discriminated union, a structure that q does not have. The idea is to combine the fields from the two "sides" in the union and use a boolean flag to indicate which "side" is active. Here is an object used to hold the input and output streams in a parser, along with a flag indicating success and an error message for failure.

q)strm:([flg:1b; out:1#`a; inp:`b`c`d; err:""])

Now suppose the successful result of the next parse step is,

q)pres:([flg:1b; out:1#`b; inp:`c`d; err:""])

Here is a conditional pattern that deconstructs the result object and updates the original stream. We display it indented for ease of reading.

:[pres; ([flg:1b; out:out; inp:inp; err:err]); strm,([out:strm[`out],out; inp]); strm,([flg:0b; err])]

The names to the right of : in the pattern are local variables that are assigned upon match and are used in the result portions of the match. All names to the left of : are record field names.

q):[pres; ([flg:1b; out:out; inp:inp; err:err]); strm,([out:strm[`out],out; inp]); strm,([flg:0b;err])]
flg| 1b
out| `a`b
inp| `c`d
err| ""

Notice that this also handles the case of failure, which is not shown here. For those who know or care, this is essentially bind in a parser stream monad – i.e., it prescribes how to compose parsers sequentially.