J-ottings 16: A Composition on Composition
Vector 14:4, page 48
Transcribed from the printed issue; not yet reviewed. Read it in the PDF of the issue, page 48
Handling conjunctions is an important part of J — arguably the two most fundamental are atop (@) and with (&).
An example of composition using with is the verb
in which the noun <0 1 is joined with the verb transpose, |:, to form the composed verb diag which returns the leading diagonal of a matrix argument. Composing a noun and a verb to form a new verb is a familiar part of the fabric of natural language. For example, “daydream” is a verb whose meaning cannot be fully conveyed by using day or dream separately, and the same applies to compound verbs such as “headhunt”, “facelift”, “datestamp”, etc. Some compositions of two verbs are “sleepwalk”, “tumbledry”, “stirfry”, and again in each case the composition has an extra layer of meaning which exceeds the sum of its two constituents.
The definitions of & and @ in the two-verb case are clearly spelt out in the Dictionary :
u&v- is the composition whose result is
(v x) u (v y)(dyadic), oru (v y)(monadic) so that v is always applied monadically. u@v- is the composition whose result is
u (x v y)(dyadic), oru (v y)(monadic) so that u is always applied monadically.
A hook is also a composition of two verbs, for which the two possible results are: x u (v y) and y u (v y), so that, as with &, v is always applied monadically.
Because an absent left argument is given the default value y, the composed verb arising from a hook is always inherently dyadic, unlike the composed verbs resulting from monadic atop and with, which are identical.
Thinking of u as the prime verb, and v as a transformation, the dyadic cases can be thought of as
@- u is applied to a transformation which uses both arguments;
&- u is applied after a transformation has been made to both arguments separately;
- hook
- u is applied following a transformation to the right argument only.
Composition takes into account a property called rank inheritance. Rank inheritance means that the verb rank of the transforming verb v is inherited by the prime verb u. The verb ranks of any verb, primitive or defined, can be obtained by issuing v b. 0, the result of which is three numbers, which give respectively the monadic, left and right ranks. These are respectively the maximum ranks of nouns which can be accepted by the operational routine without any shape constraint. One or more of these will often be _ , that is infinity, which means that the verb rank is unbounded. The rank of monadic + for example is unbounded, which means the operation is valid, whatever the frame of the argument, even although eventually + will be applied at the scalar or cell level. The left and right ranks of dyadic + on the other hand are 0, meaning that the underlying operational routine can only be applied at the cell level, since for any higher argument rank compatibility constraints restrict the frames of the arguments.
Any composite verb whose transformation component is modified by each=.&> has rank 0. The definition of @ includes observance of rank inheritance, whereas its variant at (@:) ignores it, and so in cases where the transforming verb is adverbially qualified by each, @ and @: offer a rank inheritance option.
Here are some simple examples to illustrate these points. Define a rank 1 verb
so that for a matrix argument m, diag m returns the leading diagonal, and rotv m returns m with each of its rows rotated.
Issuing the expression diag rotv m=.i.3 4 does not create a hook; but is an instruction to execute one verb rotv, and then perform another, diag, on the result of applying the first. On the other hand issuing either (diag rotv) m or
does create a hook, since what is executed in both cases is a new verb formed by composition before any association with data. To pursue an analogy with English, frying the pork and then stirring it is not the way to produce a stirfry! Both (diag rotv) m and diagrotv m give rise to domain errors, because diag can only be applied monadically, whereas hooks are inherently dyadic in their application.
Now consider (diag&rotv) m and (diag@rotv) m. If the rules at the head of this article are followed without taking rank inheritance into account the result of each should be the same as diag rotv m, namely 3 6 9. However both expressions give rise to index errors. Why? Because rotv is a rank 1 verb, and so rank 1 is inherited by diag. But as m is a matrix, at rank 1 it consists of a vector of rows, for which, as an argument to diag, the index value 1 is out of range. This rank inheritance argument can be checked by issuing the composition (rotv@rotv)m which returns a result which is identical to m. The conjunction @: allows rank inheritance to be overruled, so that (diag@:rotv) m does indeed return the same result as diag rotv m.
As a further example, consider the differences between the following compositions:
The first is the function max(ln n,ln m), the second is ceil(logn m) which is the minimum number of digits required to represent a strictly positive number m in number base n, and the third is the function max(n, ln m) — in this case the same as n >.^.m .
Since with atop the transformation verb v uses both x and y, atop can be thought of as a sequencing conjunction, that is the prime verb u is applied only after some transformation has been applied to all the data, and the result passed on to it.
The verb n(>.@^.)m above is an archetypal case of atop. The composed verb addresses the question of, for example, how many hexadecimal digits are required to represent 1000. Step one is to find the logarithm of 1000 in base 16 thereby consuming and transforming all the data. This is then passed on to the prime verb ceil ( >. ).
An archetypal instance of with is multiplication by addition of logs, in which the two multiplicands are both transformed by log before the prime verb + is applied. &. (under) is variant of & which applies the reverse transformation to the result, in this case completing the multiplication process.
The difference between a hook and issuing the same verbs in sequence verbs is illustrated by the fact that --y and %%y both evaluate to y whereas (--)y is 2y and (%%)y is y². As a further example %+/y is the reciprocal of the sum of items of the vector y, (%+/)y is the vector in which each item of y has been replaced with its proportion of the total.
The “bridge hook” u~v is useful in circumstances where transformation is required on the left rather than the right argument. A simple example is using the material in x to construct an object of the same shape as y, for which the appropriate bridge hook is ($~$).
Another example is the transformation of an integer x into a vector of y decimal digits. First transform y into a vector of y 10’s by $&10 and then apply this as the left argument of #: to define the hook (#:~$&10).
As well as rank inheritance, there are two traps to be avoided in sequencing compositions. The first is that it is easy to omit @ when it is vital for correct meaning as in (+/@*:) to mean “sum of squares”. Without the @ the verb is a hook, and the result is the addition table for the squares of the items of y.
The second trap is the omission of parentheses where these are mandatory. Conjunctions “capture” the object (verb or noun) immediately to their right (or expressed in another way, they have short right scope), and so a verb sumdev defined as
… has the meaning (+/@-)mean rather than the intended +/@(-mean). (+/@-)mean is a hook, for which, following the definition above, the result is y(+/@-)mean y. This means perform y-mean y so that mean y is calculated first to obtain a scalar at scalar level (rank 0), then apply +/ at inherited rank 0, that is perform +/ on the vector items, which on scalars is a “do nothing” operation, leading to, as the overall result, the vector y- mean y.
By contrast, with
the hook (-mean) is the transforming verb of the atop composition, and so after performing y-mean y , the resulting vector is presented to +/ with rank inheritance 1 (that is the rank of the hook).
With
rank inheritance is overruled, +/ applies at the maximum rank, and so the result is equivalent to +/@(-mean). The point is that minus is, in APL terms, a scalar function, whereas mean is not, and a fortiori neither is -mean.
It is prudent when using @ or @: to check verb rank by issuing v b. 0, which was described earlier. For example
so that 5 6 7 (+/@-) 3 5 1 is 2 1 6 and 5 6 7 (+/@:-) 3 5 1 is 9.
In summary, a thorough understanding of with, atop and hook is a major step to confidence in handling J.