Entries in protected execution (2)

How do I use the functional forms of apply and amend?

Typical q code operates on all of the elements of a container at once:

q)container: 1 2 3
q)100 * container
100 200 300
q)

Sometimes we are interested in only a subset of elements from a container:

q)container: til 10
q)container
0 1 2 3 4 5 6 7 8 9
q)container[where 0 = container mod 2]
0 2 4 6 8
q)                                                                                                         

Sometimes, however, you need to update particular elements of a structure while leaving the remaining elements unchanged. That's what functional apply and amend are for; they transform specific elements of a container without touching the others. The variations are distinguished by 3 choices:

1. @ or .

Which operator is used, @ or ., determines the interpretation of the indices used to select the elements to transform.

2. container or name

The first argument is either the value of a container or the name of a global variable referring to a container. In the former case, a new object is returned; in the latter, the global variable is modified and its name is returned.

3. monadic or dyadic function

If the transformation requires additional information beyond that contained in each element itself, that is accomplished by using a dyadic function and supplying the additional information in a fourth argument to the operator.

For a detailed discussion, please see the following faqs:

Functional @ (apply)
Functional . (apply)

Lastly, note that there is another pair of overloads for @ and . - each with three arguments - called protected execution, which are invoked when the first argument to @ or . is a function or projection; protected execution is discussed in another faq.

How does 'protected execution' (or exception handling) work in q?

Protected execution is q's model of exception handling. If you are familiar with exception models (such as try, catch, and throw) from another language, you will notice a resemblance.

In any exception handling model, there is a block of code to attempt to execute and one or more blocks of code to invoke should the attempted block fail. Moreover, the error handling blocks have a proscribed form. Lastly, there is a mechanism to raise an exception.

Let's look at a simple example in q and break it down:

LaidBackLoad: {[file]
    @[{system "l ", x; 1b}; / code to attempt
      file;                 / argument
      {[err] 0N! err; 0b}]} / error handler

In q, there is only one error handling block, and both the try block and the error handling block must be functions. In addition, the error handler takes a single parameter, which - if the error handler is called - will be a string.

In our example, the function to try takes only a single parameter, and that's why the protected execution expression starts with an @. If your try function takes more than one parameter, use . instead:

LaidBackEqual: {
    .[{x = y};              / code to attempt
      (x; y);               / arguments
      {[err] 0N! err; 0b}]} / error handler

You signal an error (which may be a symbol or a string) using the ' (signal) operator:

CantBeNegative: {if [x < 0; ' "Must be >= 0"]; x}

This is exactly the same mechanism that q uses to let us know when we have done something wrong:

q)(1 2 3) = 1 2
'length
q)

There is one last detail to note when reviewing the above examples. Protected execution expressions are expressions, and they have a value. If the try function does not signal, then the expression's value is the try function's return value. Otherwise, the protected execution expression's value is the value returned by the error handler.