Skip to content

Lambda notation

The lambda notation is how you define a function.

A lambda function is defined by an optional function signature1 followed by zero or more expressions, separated by semicolons; all embraced by curly brackets.

{[arg1;arg2] a:arg1*arg1; b:arg2+acos -1; a%b}

Semicolons are separators

Avoid the mistake of thinking of semicolons as terminators. Expressions do not have terminators.

Semicolons in a lambda separate its expressions; they are part of its structure.

The last semicolon in a lambda precedes its last expression. Evaluating its last expression gives the lambda’s result. If the last expression is empty, the lambda’s result is the general null.

q)(::)~{x;} 42
1b

Result

When the lambda is evaluated, its expressions are evaluated sequentially from left to right. The result of evaluating the lambda is the result of evaluating the last expression. If the last expression is empty, the result is the general null ::.

Every lambda returns a result

The general null :: does not have a visible representation on the console. But it is returned and can be assigned.

q)q:{}[]
q)q ~ (::)
1b

q)(::) ~ {`Global set x;}[42]
1b
q)Global
42

Function rank

In computer science, the number of arguments a function expects is known variously as its valence or -arity; in q, it is known as its rank2. A lambda can have a rank in the range 1–8.

q)til[3;4]              / oops, til has rank 1
'rank
  [0]  til[3;4]
       ^

The rank of a lambda is determined by the number of arguments specified in its signature; or, if no signature is specified, by references in its definition to the default arguments x, y, and z.

{[a;b;c] a+b*c}         / rank 3
{x+y*z}                 / rank 3
{z*z}                   / rank 3
{y*y}                   / rank 2
{acos[-1]*x*x}          / rank 1
{[r]acos[-1]*r*r}       / rank 1

There is no such thing as a nullary lambda.

The minimum rank of a lambda is 1.

q)(::) ~ {}[]           / rank 0?
1b
q)(::) ~ {}[3]          / no, rank 1
1b

q)(::) ~ {}[3;4]        / no, not 2: 1
'rank
  [0]  (::) ~ {}[3;4]
              ^

If you evaluate a unary lambda on an empty argument list, the argument value is the general null.

q)(::) ~ {x}[42]
0b
q)(::) ~ {x}[::]
1b
q)(::) ~ {x}[]
1b

In the edge case {}[] both argument and result are the general null ::.

The rank of a lambda is fixed.

Signal

A good use case for if is in testing a lambda’s argument/s and signalling an error.

foo:{[x]
  if[x<0;'`type];      / signal type error
  a:..;
  }

If foo above receives a negative number it signals a type error to the expression that called it.

Explicit return

In a similar use case no error is signalled but computation can be cut short with the explicit return.

goo:{[x]
  if[x<0;:0];          / return zero
  a:..;
  }

Exercises

No peeking.

Attempt each exercise before looking at its answer.

The exercises clarify your thinking. Reading answers does not.

  1. What is the rank of {[z]x+y+z}?

    Answer

    The signature of {[z]x+y*z} specifies a single argument z. The references to x and y must resolve from external names. So the lambda has rank 1.

    Default argument names

    It is a terrible idea to use x, y, or z as anything other than the names of (respectively) the first, second, and third arguments.

    And if you are using them as argument names, use tham as defaults: a function signature has no information to add.

  2. Several operators are overloaded by rank; which is to say that they are variadic and their semantics are determined by the number of arguments to which they are applied. What scope does lambda notation provide for doing this?

    Answer

    The lambda notation fixes the rank of a lambda in the range 1–8. But a unary lambda can be applied with bracket syntax to an empty argument, in which case the value of its argument is the general null ::, for which the lambda can test to see if it has been called with one or zero arguments.

    q){x~(::)}[]
    1b
    
    Of course the same can be done with any designated value, such as zero, -1, or an empty list. The general null is just less likely to result from the computation of an argument value.

  3. A lambda with an empty final expression returns a general null. Is it better to assign it than to let it print? Explain.

    Answer

    Every q expression returns a result. If the final expression of a lambda is empty, its result is the general null ::.

    The general null has no display form, so nothing is written to stdout. Nothing is gained by assigning the result if it is always the general null.

  4. What do the expressions if[3], do[3] and while[3] do?

    Answer

    Nothing, respectively once, three times, and forever.

  5. All q expressions return a result. Pick a control word and explore whether it is actually a function of some kind.

    Answer

    q)if[3]      / OK
    q)(if)       / noun syntax?
    'if
    q)3 if/()    / derive a function?
    'if
    q)type(if)   / has data type?
    'if
    q)@[if;3]    / arg to Apply At?
    'if
    q)if         / display a definition?
    'if
    
    Above, all the errors are the same error: the parser won’t accept if in the attempted syntactic role – it’s not a function.


  1. One or more names, separated by semicolons, and embraced by square brackets. 

  2. Sadly, nothing to do with the rank keyword. Sorry.