# Verb Rank

J's rank mechanism allows any verb, whether a primitive or one you write, to apply to arguments of any rank. When you write

```a =: b + c
```

or

```salary =: calcsal employees
```

you may be calculating on arrays of any rank. The rank mechanism runs automatically every time a verb is executed, and provides the equivalent of automatic nested loops running through all axes of an array argument.

## The Rank of a Verb

The rank of a verb is the highest rank the verb is prepared to handle. If the rank of an argument does not exceed the rank of the verb, the verb is executed once, on the entire argument. If the argument has a higher rank, the rank mechanism automatically splits the array into pieces, each of maximum allowed rank. The verb is executed on the pieces separately, and the results of the executions are assembled to form the result of the verb.

### Infinite Rank

The rank of a verb may be infinite to indicate that the verb can accept arguments of any rank. The rank mechanism has no effect for verbs of infinite rank.

because the rank of an argument can never exceed infinity.

### The three ranks of a verb

Every verb u has three ranks, one for each possible argument:

• m, for the y argument to the monadic form (u y)
• l, for the x argument to the dyadic form (x u y)
• r, for the y argument to the dyadic form (x u y)

These ranks are independent of each other.

### How to see the rank of a verb

u b. 0 will show you the rank of u, in the order m l r

```   #: b. 0  NB. primitives are born with rank
_ 1 0
+/@:* b. 0  NB. @: creates a verb with infinite rank
_ _ _
+/@* b. 0  NB. @ creates a verb with the rank taken from *
0 0 0
```

## How A Verb Gets Its Ranks

A verb in J is either a primitive or the result of executing a modifier. Every verb must have the 3 numeric ranks.

### Primitives

The rank of a primitive is part of the definition of the primitive. You can find it:

• in NuVoc next to the name of the verb
• by using (u b. 0)
• by hovering over the verb in a dissect display.

Verbs for arithmetic, which operate on numbers, have rank 0, indicating that they operate on each atom independently.

Verbs that operate on entire arrays generally have infinite rank.

Specialized verbs have appropriate rank. For example, the verb for matrix inverse, (%. y), has rank 2.

### Verbs Created By Modifiers

When a modifier creates a verb, it assigns ranks to the verb.

The ranks of the created verb may depend on the part of speech, and even the value, of the operands to the modifier.

#### Explicit definitions (the : modifier)

Verbs created by any of the versions of explicit definition:

• verb define
• 3 : 0
• 4 : 0
• 13 : 0

always have infinite rank. You may create a verb of lower rank, as discussed in the next section, to use the rank mechanism on an explicitly-defined verb.

#### Assigned rank (the " modifier)

u"n creates a new verb whose rank is n. When u"n is executed, the rank mechanism chops up arguments according to the rank n, and executes u on each piece.

n can be specified as:

• the 3 numbers m, l, and r
• the 2 numbers l and r (in this case m=r, i. e. r gives the rank of the right argument whether monad or dyad)
• a single number which is used for m, l, and r.

Note that the rank of u itself is unchanged, so that when u is executed on a piece of the argument, the rank mechanism operates during that execution too, using the ranks of u.

Forms like u"n"p are also useful, and cause the rank mechanism to operate 3 times, with different ranks each time: once for (u"n"p), then for (u"n), and finally for u.

#### Other Modifiers

The definition of the modifier tells you the rank of its created verb. This is an important part of the created verb! For example, the rank of u@v is defined in NuVoc as mv lv rv which means that the verb will have the same 3 ranks as v. The rank of u@:v is infinite. Otherwise the created verbs are identical, but the difference is worth an essay in itself.

#### Undefined Names

Undefined names are assumed to be verbs of infinite rank until they are defined.

## How the Rank Mechanism Works

The rank-mechanism sequence is:

• Subdivision - split the argument(s) into cells that have the desired rank
• Execution - execute the verb on each cell
• Assembly - assemble the cell-results into an array
• Fill - as part of assembly, pad the cell-results to a common shape

We will examine these steps for the execution of a monad u y. Execution of a dyad is similar but with an additional step. But before we start, here we try to visualize verb application and the influence altering the verb’s rank has on it.

### Subdivision

#### An Array Is An Array Of Cells

An array is a collection of nouns of lower rank. A 3x4 table is an array of atoms, but it can also be thought of as an array of 3 4-atom lists.

How you choose to view an array is up to you, and may change in the middle of a sentence.

Wen a verb is executed, the rank of the verb, denoted k, specifies the rank of the argument cells, and the argument is considered to be an array of its argument cells. The list of the lengths of the axes along which these argument cells are arranged is called the k-frame of the argument.

 Different views of i. 2 3 4 k k-frame Shape of a cell 0 2 3 4 empty 1 2 3 4 2 2 3 4 3 or higher including _ empty 2 3 4

Observe that the k-frame, concatenated with the shape of a cell, always equals the shape of the argument.

Execute the following sentence using the dissect addon, and click in the result blocks to see the cells of various ranks:

```dissect ']"0 ]"1 ]"2 i. 2 3 4'
```

### Execution

The verb is executed on each argument cell individually, producing one result for each argument cell.

### Assembly

The results from the arguments cells are assembled into a array, using the k-frame of the argument as the lengths of the axes. If all the results have the same shape, the shape of the result will be the k-frame concatenated with that result-shape.

Use dissect to explore the following examples. Click in a result to show the result-cell and highlight the argument cell that produced it:

```   dissect '+/ i. 3 4'
```

+/ y adds the items of y. It has infinite rank and produces a result whose shape is that of an item of y. Here the rank mechanism is not used, and the result is a list of 4 numbers.

```   dissect '+/"1 i. 3 4'
```

+/ is now applied on lists, whose items are atoms. The 1-frame of the argument is 3. There are 3 separate computations of totals, each producing an atom as result. The results are brought into a 3-atom list.

```   dissect '+/"0 i. 3 4'
```

+/ is now applied on atoms, whose items are atoms. The 0-frame of the argument is 3 4. There are 12 separate computations of totals, each producing an atom as result. The results are brought into a 3x4 table of atoms.

#### Fill

All cells in an array must have the same shape. If the result-cells do not have the same shape, they are brought up to the same shape by adding fill to smaller cells. The fill is of the same type as the results: 0 for numeric, space for character, a: for boxed.

if the result-cells have different ranks, leading axes of length 1 are added to results of lower rank.

Use dissect to explore the following examples. In each, fill was inserted. The filled cells are shown by crosshatching in the dissect display. Click in a result to show the result-cell and highlight the argument cell that produced it:

```   dissect 'i. ,. 3 4 5'
```

Here (,. 3 4 5) produces a table which is then fed in to (i. y). Because the rank of i. y is 1, the table is broken into 1-cells (rows), and (i. y) is applied to each row, producing a list for each.

These lists are assembled into an array using the 1-frame (which is 3). The shorter rows must be extended to the length of the longest to keep the array regular.

```   dissect 'i. z' [ z =. 1 2,2 2,:2 3
```

This is a similar example, but now the fill must be added along two axes.

```   dissect '> z' [ z =. 0;1 2;3 4 5
```

The rank of (> y) is 0, so again the frame (this time the 0-frame) is 3 and the results must be filled.

#### Framing Error

If the result-cells have different types, they cannot be assembled into a single array. The execution fails with domain error: the error is not in the execution of the verb, but in the assembly.

Set up a test array to be used as argument y

```   ] y =: i. 2 3 4
0  1  2  3
4  5  6  7
8  9 10 11

12 13 14 15
16 17 18 19
20 21 22 23
```

Note how the verb rank of Box (<) is infinite. Therefore  <y simply boxes y whole and entire

```   < b.0   NB. The verb rank of monadic (<) is infinite
_ 0 0

< y     NB. Therefore Box (<) simply boxes its y-argument whole
+-----------+
| 0  1  2  3|
| 4  5  6  7|
| 8  9 10 11|
|           |
|12 13 14 15|
|16 17 18 19|
|20 21 22 23|
+-----------+
```

Create verb v using a given value of k (we will do this repeatedly).

 k = _ argument shape frame shape k-cell shape 2 3 4 (empty) 2 3 4
```   k =: _   NB. rank of k-cells for y-argument to monad v
m =: 0   NB. don't bother to alter it (unused by monad v)
n =: 0   NB. don't bother to alter it (unused by monad v)

v =: < " (k,m,n)
v  b.0
_ 0 0
```

But in practice we don't need to bother with m and n since we're only working with the monad v.

So, when specifying the right operand to the conjunction: Rank ("), we only need k, even though this simplified construct changes the dyadic left and right ranks too

```   v =: < " k   NB. Specifying an atom k as (") right operand --> list: 3\$k
v  b.0       NB. But only going to use monad v, therefore m, n don't matter
_ _ _
```

Since k is the original monad rank as shown by  b.0 , we can expect v to behave like the original monad Box (<)

```   v y          NB. Original k --> v behaves like (<)
+-----------+
| 0  1  2  3|
| 4  5  6  7|
| 8  9 10 11|
|           |
|12 13 14 15|
|16 17 18 19|
|20 21 22 23|
+-----------+
```

Now try altering the rank of the k-cells, governing how y is split up.

Setting k=1 makes the k-cells become 1-cells, i.e. lists. The k-cells are now the rows of y:

 k = 1 argument shape frame shape k-cell shape 2 3 4 2 3 4
```   k =: 1   NB. k-cells are now the rows of y
v =: < " k
v y
+-----------+-----------+-----------+
|0 1 2 3    |4 5 6 7    |8 9 10 11  |
+-----------+-----------+-----------+
|12 13 14 15|16 17 18 19|20 21 22 23|
+-----------+-----------+-----------+
```

Setting k=2 makes the k-cells become 2-cells, i.e. matrices. The k-cells are now the (two) items of y — which happen to be matrices:

 k = 2 argument shape frame shape k-cell shape 2 3 4 2 3 4
```   k =: 2   NB. k-cells are now matrices
v =: < " k
v y
+---------+-----------+
|0 1  2  3|12 13 14 15|
|4 5  6  7|16 17 18 19|
|8 9 10 11|20 21 22 23|
+---------+-----------+
```

Setting k=3 makes the k-cells become 3-cells, i.e. bricks. Array y itself happens to be a brick, therefore y is (trivially) "split up" into one single k-cell:

 k = 3 argument shape frame shape k-cell shape 2 3 4 (empty) 2 3 4
```   k =: 3   NB. k-cells are now bricks
v =: < " k
v y
+-----------+
| 0  1  2  3|
| 4  5  6  7|
| 8  9 10 11|
|           |
|12 13 14 15|
|16 17 18 19|
|20 21 22 23|
+-----------+
```