Functions
Default Arguments
Practice questions on simulating default parameter values in q functions — q has no built-in default syntax, but a few short patterns cover most cases.
The Challenge
Many languages let you write something like def f(x, n=10) so that n is optional and falls back to 10 if the caller doesn't supply it. Q has no equivalent syntax. When you want the same behaviour in q, you handle it yourself at the top of the function.
The agreement between the caller and the function is simple: if you want to use the default, pass the appropriate null value for that type — 0N for a long integer, 0n for a float, ` for a symbol. The function checks whether it received a null and substitutes the real value before doing any work.
Approach 1: Conditional Null Check
The most readable approach. Check for null at the start, replace it with your default, then proceed as if the caller always passed a real value:
The examples on this page use this small table:
trade:([] date:.z.d; sym:`AAPL`MSFT`GOOG`AAPL`MSFT; price:185.5 410.1 142.8 187.0 411.2; qty:100 200 300 150 250)topPrices:{[t; n]
n: $[null n; 10; n]; / if n is missing, use 10
select[n; >price] from t
}
topPrices[trade; 3] / caller wants 3 rows
topPrices[trade; 0N] / caller wants the default (up to 10 rows)$[condition; valueIfTrue; valueIfFalse] is q's if-else. null n is true for any typed null, so the check works regardless of what type n is. After this one line, the rest of the function treats n as a guaranteed real value.
Approach 2: Fill Operator ^
^ replaces nulls with a fallback value. It's shorter than the conditional form and reads naturally for simple cases:
topPrices:{[t; n]
n: 10 ^ n; / substitute 10 wherever n is null
select[n; >price] from t
}10 ^ n means "use n, but if it is null, use 10 instead." For a single (atom) argument, the two approaches behave identically. Pick ^ for brevity, $[null n; ...] when you want the intent to be unmistakeable at a glance.
Approach 3: Bake Defaults Into a Projection
If you find yourself calling a function with the same arguments over and over, a projection can remove the repetition entirely — no null-checking required. The default is locked in when the projection is created:
fetchTrades:{[tbl; syms; startDt; endDt]
select from tbl where sym in syms, date within (startDt; endDt)
}
/ Lock in the table and today's date — callers only supply the symbols
todaysTrades: fetchTrades[trade; ; .z.d; .z.d]
todaysTrades[`AAPL`MSFT]This approach is cleaner than null-checking when a specific combination of "default" arguments is used so frequently that it deserves its own name.
One gotcha to know: .z.d is captured at the moment you write the projection, not each time you call it. If todaysTrades is defined at startup and then called tomorrow, it will still hold yesterday's date. For projections that need a live value each time they run, wrap in a lambda instead:
todaysTrades: {fetchTrades[trade; x; .z.d; .z.d]}Which Approach to Use
| When... | Use... |
|---|---|
| One or two optional args with simple fallback values | $[null x; default; x] or default ^ x |
| The default is computed or changes depending on context | Null check ($) — gives you full control |
| A common combination of fixed args has its own identity | Projection — name the specialisation, remove the repetition |
Whichever approach you choose, place the default-handling at the top of the function. A reader should see what the real inputs are before they see the logic that uses them.
Practice Questions
Test your understanding with 2 hands-on questions