Functions
Projections
Practice questions on projections — fixing some arguments of a function to create a specialised version with fewer parameters.
What Is a Projection?
Imagine you have a function that takes two arguments, and you find yourself calling it repeatedly with the same value in the first slot. Instead of writing that value out every time, you can lock it in and get back a new, simpler function that only asks for the remaining argument. That locked-in version is called a projection.
add: {x + y}
/ Lock in the first argument as 10
add10: add[10;]
add10[5] / 15
add10[100] / 110add10 behaves exactly like calling add[10; something] every time — you've just saved yourself from repeating 10 at every call site, and you've given the specialised version a meaningful name.
Keeping All the Semicolons
When you write a projection, you mark each open slot with a gap between semicolons. A good habit is to always write out every semicolon, even when a slot is at the very end.
For a function that takes three arguments, there are seven ways to project it (here a, b and c stand for the values you fix):
f[;;] / leave all three open — unusual but valid
f[;;c] / fix the third argument only
f[;b;] / fix the middle argument only
f[;b;c] / fix the middle and third
f[a;;] / fix the first argument only
f[a;;c] / fix the first and third
f[a;b;] / fix the first and middleWhy the trailing semicolon is worth writing:
f:{x + y + z}
f[1;2;] / a projection waiting for the third argument
f[1;2;][3] / 6
f[1;2] / also a projection — trailing arguments may be omitted
f[1;2][3] / 6Applying a function to fewer arguments than it takes always produces a projection, so f[1;2] and f[1;2;] are the same. Writing the trailing semicolon makes it obvious to a reader that you meant to leave that slot open, rather than forgot an argument.
Projecting Built-In Operators
Any q operator can be projected, not just user-written functions:
double: 2* / lock 2 in as the left-hand multiplier
halve: %[;2] / lock 2 in as the divisor on the right
above100: >[;100] / lock 100 in on the right of >
double 7 / 14
halve 50 / 25
above100 150 / 1b
above100 80 / 0b2* is shorthand for *[2;] — q lets you write a constant directly to the left of an operator as a convenient way to project its left argument.
Combining Projections With each
Once you have a rank-1 projection, it slots straight into each without any extra wrapping:
asPct: %[;100] / divide by 100
asPct each 25 50 75 / 0.25 0.5 0.75Compare with the lambda alternative:
{x % 100} each 25 50 75 / works, but you lose the reusable nameThe projection is more useful when you need that function in more than one place — define it once, use it everywhere.
Projection or Lambda?
Both get the job done. The choice comes down to how clearly each one reads:
/ Projection — the meaning is obvious from the name alone
above200: >[;200]
/ Lambda — the same, with more ceremony
above200: {x > 200}For anything this simple, the projection wins. But when the logic involves combining arguments in a non-obvious way, a lambda with named parameters tells a clearer story:
/ Lambda wins — explicit names explain what lo, hi, and v mean
clamp:{[lo; hi; v] lo | v & hi}
clamp01: clamp[0; 1;]
clamp01[0.75] / 0.75
clamp01[1.5] / 1f
clamp01[-0.2] / 0fA useful test: if you can read the projected function aloud in plain English in five words or fewer and it makes sense, a projection is probably right. If you need more words than that to describe it, reach for a lambda.
A Common Real-World Pattern
Projections are handy for building query functions that share a table but differ by what they filter:
trade: ([] sym:`AAPL`MSFT`GOOG; price:185.5 410.1 142.8)
quote: ([] sym:`AAPL`GOOG`GOOG; bid:185.4 142.7 142.75)
getBySyms: {[tbl; syms] select from tbl where sym in syms}
getTrades: getBySyms[trade;] / always queries the trade table
getQuotes: getBySyms[quote;] / always queries the quote table
getTrades[`AAPL`MSFT]
getQuotes[`GOOG]Each specialised function locks in the table it owns, so callers only need to think about which symbols they want.
Practice Questions
Test your understanding with 3 hands-on questions