Back to Functions

Functions

Projections

Skip to 3 practice questions

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]  / 110

add10 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 middle

Why 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]  / 6

Applying 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    / 0b

2* 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.75

Compare with the lambda alternative:

{x % 100} each 25 50 75  / works, but you lose the reusable name

The 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]   / 0f

A 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