Whenever you run a function with an array argument in Rowan, you are running it at some level. Does the function look at the whole array, or the elements within it? Most of the time you don't notice, because each function has an intrinsic level which is correct for the most common uses.
A positive level tells Rowan how many levels to go down the array's structure before calling the function. Level 0 means use the entire argument at once. A negative rank tells it to come up that many levels from the deepest possible in each section of the array before calling the function, so -1 is the lowest-most items. (Strictly, calling at level -n means the interpreter goes down the structure until it finds sub-arrays with a depth less than n.)
For example, for the complex-structure x
below:
x←(((1 2)(3 4))(5 6))(7 (8 9)) // Max depth 4, mixed depth
The items are arranged vertically by their maximum depth, corresponding to the negative levels on the right of the diagram. The colours, and regions separated by the thin grey lines, correspond to the positive levels on the left (e.g. the orange boxes are the level 3 items). Using the monad Output to demonstrate ... Positive level:
(=^0)x Output: [[((1 2) (3 4)) (5 6) ] [7 (8 9) ] ] (=^1)x Output: [((1 2) (3 4)) (5 6) ] Output: [7 (8 9) ] (=^2)x Output: ((1 2) (3 4)) Output: (5 6) Output: 7 Output: (8 9) (=^3)x Output: (1 2) Output: (3 4) Output: 5 Output: 6 Output: 7 Output: 8 Output: 9Negative level:
(=^-1)x Output: 1 Output: 2 Output: 3 Output: 4 Output: 5 Output: 6 Output: 7 Output: 8 Output: 9 (=^-2)x Output: (1 2) Output: (3 4) Output: (5 6) Output: 7 Output: (8 9) (=^-3)x Output: ((1 2) (3 4)) Output: (5 6) Output: [7 (8 9) ] (=^-4)x Output: [((1 2) (3 4)) (5 6) ] Output: [7 (8 9) ] (=^-5)x Output: [[((1 2) (3 4)) (5 6) ] [7 (8 9) ] ]
For an array of even depth, negative levels can be thought of as specifying the element type to work on: -1 for scalars, -2 for vectors, -3 for depth-2 arrays and so on. However, as this example shows, if there are mixed-depth segments of the array, functions can receive arguments of less depth than they expect, e.g.:
(≡^-2)x // expect all 1s [[(1 1) 1 ] (0 1) ] (≡^-3)x // expect all 2s [(2 1) 2 ]
Using level with a monad is simple. A dyad is a little more complicated, because you need to specify the level on both sides so that the arguments match up how you want them to. To do this you pass a two element vector as the right argument to the level conjunction:
((1 2)(3 4)) + ((10 20)(30 40)) ((11 22) (33 44)) |
Add the two arguments together as usual |
((1 2)(3 4)) (+^(1 0)) ((10 20)(30 40)) (((11 21) (32 42)) ((13 23) (34 44))) |
Go one level down in the left and none in the right, i.e. do(1 2) + ((10 20)(30 40)) then(3 4) + ((10 20)(30 40)) |
((1 2)(3 4)) (+^(-2 0)) ((10 20)(30 40)) (((11 21) (32 42)) ((13 23) (34 44))) |
Equivalently, take the depth 1 items on the left against the whole right. |
((1 2)(3 4)) (+^(0 1)) ((10 20)(30 40)) (((11 12) (23 24)) ((31 32) (43 44))) |
Go no levels down in the left and one in the right, i.e. do((1 2)(3 4)) + (10 20) then((1 2)(3 4)) + (30 40) |
If you only specify a one-element right argument to level, it sets the left and right levels to be the same (as well as setting the monadic level).
Each function has a default level, at which it will be called if you don't explicitly set its level. The Reference and the documentation for individual functions will tell you what the default level is. It is designed to be the level most useful for the common way a function is used, so you should only need to specify a level if you're doing something slightly unusual.
Some common cases of setting levels have one-symbol shorthands:
¨
(Each, AltGr+Semicolon) is equivalent to ^1
⊢
(Each Right, AltGr+9) is equivalent to ^(0 1)
⊣
(Each Left, AltGr+0) is equivalent to ^(1 0)
⍩
(All, AltGr+Shift+Semicolon) is equivalent to ^-1