Primitive operators#

Operators take operands, which may be functions, and derive a function. You can think of APL’s operators as higher-order functions.

Reduce / #

The first operator is /, called reduce. It is a monadic operator which derives an ambivalent function. An ambivalent function is one which can be called either monadically or dyadically. For example, - is ambivalent. Monadically, it is negate; dyadically, it is subtraction.

+/ is a derived ambivalent function. The monadic function is plus-reduction (i.e. sum) and the dyadic function is windowed sum, as in sliding windows of size (shorthand for “left argument”).

+/3 1 4 1 5
2 +/ 3 1 4 1 5
3 +/ 3 1 4 1 5 
14
4 5 5 6
8 6 10

Question:

What does 3-/ do? Subtraction isn’t associative.

3-/ 1 2 3 4 5
2 3 4

As functions in APL are right-associative, -/⍵ (this is a shorthand which means the monadic form of -/) is alternating sum.

1 - (2 - 3)
2

f/⍵ is called reduce because it reduces the rank of its argument by 1. For example, if we apply it to a matrix, we’ll get back a vector, even if the function we provide does not “combine” its arguments.

{'(',,,')'}/'Hello'
┌─────────────┐
│(H(e(l(lo))))│
└─────────────┘

Here, the function we gave concatenates its arguments and parentheses. With output boxing turned on, it is clear to see that there is a space in front of the leftmost (. Without output boxing, that space is still there, but you may have to look a bit more carefully in order to notice it. This is APLs way to indicate that the array (a character vector) is enclosed. In other words, it returned ⊂'(H(e(l(lo))))'.

('(H(e(l(lo))))')  {'(',,,')'}/'Hello'
1

We can also apply reductions to higher-rank arrays:

3 4⍴⍳12
+/3 4⍴⍳12
1  2  3  4
5  6  7  8
9 10 11 12
10 26 42

Notice how the rank went down from 2 to 1 (i.e. matrix to vector). Reductions lower the rank. N f/ is called N-wise reduce, and does not lower the rank. Notice that / goes along the trailing axis, i.e. the it reduced the rows of the matrix. It has a twin, , which goes along the first axis, i.e. the columns of a matrix.

+3 4⍴⍳12 
15 18 21 24

If you have higher-rank arrays, you can reduce along any axis with a bracket axis specification:

2 3 4⍴⍳24
(+2 3 4⍴⍳24)(+/[2]2 3 4⍴⍳24)(+/2 3 4⍴⍳24)
 1  2  3  4
 5  6  7  8
 9 10 11 12

13 14 15 16
17 18 19 20
21 22 23 24
┌───────────┬───────────┬────────┐
│14 16 18 20│15 18 21 24│10 26 42│
│22 24 26 28│51 54 57 60│58 74 90│
│30 32 34 36│           │        │
└───────────┴───────────┴────────┘

Note that f/[1] is the same thing as f⌿.

Scan \ #

While / is reduction, \ is cumulative reduction, known as scan:

+\3 1 4 1 5
3 4 8 9 14

/’s cousin \ of course has a twin, too; , behaving analogously.

Each ¨#

The next operator is ¨ which is called each for a good reason. f¨⍵ applies the function f monadically to each element of . applies f between the paired-up elements of and .

1 2 3 , 4 5 6 
1 2 3 ,¨ 4 5 6
1 2 3 ,¨ (10 20)(30 40)(5 6) 
1 2 3 4 5 6
┌───┬───┬───┐
│1 4│2 5│3 6│
└───┴───┴───┘
┌───────┬───────┬─────┐
│1 10 20│2 30 40│3 5 6│
└───────┴───────┴─────┘

Most arithmetic functions are “scalar” meaning they penetrate to the very leaves of the arrays. ¨ is meaningless for scalar functions.

3+¨3 1 4 1 5 9 2 6 5  ⍝ works; but pointless
3+3 1 4 1 5 9 2 6 5   ⍝ scalar function + is pervasive
6 4 7 4 8 12 5 9 8
6 4 7 4 8 12 5 9 8

Power #

is the power operator. f⍣n applies the function f n times.

2×3
2×2×3
2×2×2×3
2(×3)3
6
12
24
24

It did the multiplication 3 times. We need parentheses here to separate the two 3s to make sure the two 3s don’t strand into a single array. Note that (f⍣n) is defined as (⍺∘f⍣n) . In the case above, (×⍣3) therefore is × × × . Operators are never ambivalent. Their derived functions can be, but they are either monadic or dyadic. is dyadic. / and ¨ are monadic. The result of ×⍣3 is a new function which takes arrays as arguments.

f⍣≡ is the fixpoint of f.

0.5×1
0

If you keep halving 1 you end up with 0. 0.5×⍣≡ means keep multiplying 0.5 with the argument until it stops changing. The power operator can take a custom right function operator, too. See the documentation.

Commute f #

Commute, , is a monadic operator taking a dyadic function and deriving an ambivalent function. f⍨ is f . f⍨ is f . We sometimes informally refer to as “selfie” when the derived function is used monadically, because that’s what it does, and it looks like a selfie (photo) too. seems very simple, but it has some neat applications. Monadic +⍨ is double. Monadic ×⍨ is square.

Constant A #

With an array operator, is constant. It always returns the operator array. It might not be immediately obvious when this is useful. Consider the following examples:

neg{-@()}
1 0 1 neg 3
¯1 2 ¯3

Here we have a function that uses a Boolean left argument to indicate where to apply negation in the right argument. Whilst it’s certainly possible to achieve this without constant, it’d be a bit more cumbersome, for example:

1 0 1 {a-@{a}} 3
¯1 2 ¯3

We could use to expand the left argument into indices, but that introduces an unnecessary inefficiency we avoid when using constant:

1 0 1 {-@()} 3
¯1 2 ¯3

Another usecase is when you want to use one array’s structure as a model, but use a particular element instead:

(?3)⍨¨'this is a string'
3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3

The alternatives, again, tend to be either more cumbersome, or inefficient

('this is a string')⍴?3
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2

Note that we can’t do

{?3}¨'this is a string'  ⍝ Not the same thing!
1 1 3 3 2 2 2 2 1 1 2 2 2 2 3 2

Beside/atop #

The Beside operator, . comes from function composition, like how f(g(x)) can be written f∘g(x) in mathematics. So, too, in APL, if f and g are functions, then f∘g x is the same as f g x (APL doesn’t need parentheses for function application). This alone is, of course, not very interesting. However, APL also has dyadic (infix) functions: A f∘g B is A f g B.

Both of these are very important when writing tacit APL code. For example, if we want to write a function which adds its left argument to the reciprocal (monadic ÷) of its right argument, it can be written as f +∘÷.

The golden ratio (phi) can be calculated with the continued fraction

\(\Large \phi = 1 + \frac{1}{1+\frac{1}{1+\frac{1}{1+\frac{1}{\ddots}}}}\)

So \(\phi\) is 1+÷1+÷1+÷…. We can insert the same function between elements of a list with the / operator, for example,

+/1 1 2 3 
7

In our case, we want to insert …+÷…, but that isn’t a single function. However, we can use +∘÷:

+÷/1 1 1 1 1 1 1 1 1 1
1.618181818

X⍴Y reshapes Y into shape X:

+÷/10001  ⍝ A good approximation of phi.
1.618033989

also allows you to compose with argumnents. g←f∘A where f is a dyadic function and A an array (any data) gives g, a new monadic function which calculates x f A. Similarly, g←A∘f makes g a function which calculates A f x.

For operators you can “curry” their right operand. So WithTwo←∘2 is a new monadic operator which can in turn modify a dyadic function to become monadic (using 2 as its right argument). For example, + WithTwo 3 will give 5.

WithTwo2
+ WithTwo 3
5

This is especially useful with the f⍣n power operator which applies its f operand function n times. twice←⍣2 is an operator which applies a function twice. For example, 2+twice 3 is 7.

twice2
2+twice 3
7

inv←⍣¯1 is an operator which will apply a function -1 times, i.e. applies the inverse of that function.

Question:

Do all functions have inverses?

No, but surprisingly many do. If you derive new functions tacitly using only operators and invertible functions, then the resulting function can also (generally) be inverted automatically. Even structural functions can be inverted:

('x',¯1) 'x', 'abc' 
abc

So what happened there is that we applied the function 'x'∘, which prepends the letter x, and then we applied its inverse, which removes an x from the left side. The specific function 'x'∘, is not hardcoded. Instead the interpreter has a bunch of rules which lets it determine the inverse of various compositions.

That’s really all there is to say about . However, a warning is in place: (f g)Y is the same as f∘g Y which may fool you into thinking that X(f g)Y is the same as X f∘g Y. However, they are not the same!

A nice golfing trick using is having the left operand be . This allows using a monadic function on the right argument while ignoring the left argument.

At @#

The at operator, @, does exactly what it says. What’s on its left gets done at the position indicated by its right operand.

('X'@2 5) 'Hello'
HXllX

So we put an X at positions 2 and 5 (APL is 1-indexed by default – you can change to 0-indexing if you want). We can also give an array which matches the selected elements:

('XY'@2 5) 'Hello'
HXllY

So far, we’ve only used @ to substitute elements. We can also use it to modify them:

(-@2 5)10 20 30 40 50 60 
10 ¯20 30 40 ¯50 60

Here we applied the monadic function - (negate) at positions 2 and 5. We can do the same with a dyadic function, too:

7(+@2 5)10 20 30 40 50 60
10 27 30 40 57 60

So far, we have been using an array right operand. If we use a function right operand it gets applied to the right argument, and the result must be a Boolean mask instead of a list of indices.

⎕A ⍝ uppercase alphabet 
'x'@(⎕A)'Hello World' 
ABCDEFGHIJKLMNOPQRSTUVWXYZ
xello xorld

is membership, so the derived function ∊∘⎕A gives a Boolean for where elements of the right (and only) argument are members of the uppercase alphabet:

(⎕A)'Hello World'
1 0 0 0 0 0 1 0 0 0 0

which is then used as mask by @ to determine where to substitute with x. See, for example Goto the Nth Page which uses @ twice.

I-beam #

I-beam, , is a special monadic operator (although it follows normal APL syntax) which uses a positive integer operand to select a functionality, typically from a range of system related services. Note that although documentation is provided for functions, any service provided this way should be considered as “experimental” and subject to change – without notice – from one release to the next.

One example is Format Date-Time, 1200⌶, which formats Dyalog Date Numbers according to a set of pattern rules.

'%ISO%'(1200)1⎕DT'J'
┌───────────────────┐
│2022-07-27T08:38:44│
└───────────────────┘

Stencil #

Next up is stencil (as in stencil code), . The symbol is supposed to evoke the picture of a stencil over a paper. Stencil is useful for Game of Life and related problems. It is a dyadic operator which derives a monadic function. The left operand must be a function and the right operand must be an array.

The right operand specifies what neighbourhoods to apply to. For example, in Game of Life, the neighbourhoods are 3-by-3 sub-matrices centred on each element in the input array. The operand gets called dyadically. The right argument is a neighbourhood and the left is information about whether he neighbourhood overlaps an edge of the original argument world.

To see how it works, we’ll use {⊂⍵} as left operand. It just encloses the neighbourhood so we can see it. As right operand we use 3 3, i.e. the neighbourhood size:

4 6⎕A ⍝ our argument
({}3 3) 4 6⎕A 
ABCDEF
GHIJKL
MNOPQR
STUVWX
┌───┬───┬───┬───┬───┬───┐
│   │   │   │   │   │   │
│ AB│ABC│BCD│CDE│DEF│EF │
│ GH│GHI│HIJ│IJK│JKL│KL │
├───┼───┼───┼───┼───┼───┤
│ AB│ABC│BCD│CDE│DEF│EF │
│ GH│GHI│HIJ│IJK│JKL│KL │
│ MN│MNO│NOP│OPQ│PQR│QR │
├───┼───┼───┼───┼───┼───┤
│ GH│GHI│HIJ│IJK│JKL│KL │
│ MN│MNO│NOP│OPQ│PQR│QR │
│ ST│STU│TUV│UVW│VWX│WX │
├───┼───┼───┼───┼───┼───┤
│ MN│MNO│NOP│OPQ│PQR│QR │
│ ST│STU│TUV│UVW│VWX│WX │
│   │   │   │   │   │   │
└───┴───┴───┴───┴───┴───┘

Here you see that we returned a 4-by-6 matrix of neighbourhoods. Notice that all the neighbourhoods are 3-by-3, even at the edges. They were padded with spaces.

The padding was done sometimes on top, sometimes on left, sometimes on right, and sometimes on the bottom. The information about that is in the left argument () of the operand function:

({}3 3) 4 6⎕A 
┌────┬────┬────┬────┬────┬─────┐
│1 1 │1 0 │1 0 │1 0 │1 0 │1 ¯1 │
├────┼────┼────┼────┼────┼─────┤
│0 1 │0 0 │0 0 │0 0 │0 0 │0 ¯1 │
├────┼────┼────┼────┼────┼─────┤
│0 1 │0 0 │0 0 │0 0 │0 0 │0 ¯1 │
├────┼────┼────┼────┼────┼─────┤
│¯1 1│¯1 0│¯1 0│¯1 0│¯1 0│¯1 ¯1│
└────┴────┴────┴────┴────┴─────┘

Each cell contains two elements, one for rows, and one for columns. Positive indicates left/top. Negative is right/bottom. The magnitude indicates how many rows/columns were padded.

This fits nicely with the dyadic drop primitive, which takes the number of rows,columns as left argument to drop from the right argument:

({}3 3) 4 6⎕A
┌──┬───┬───┬───┬───┬──┐
│AB│ABC│BCD│CDE│DEF│EF│
│GH│GHI│HIJ│IJK│JKL│KL│
├──┼───┼───┼───┼───┼──┤
│AB│ABC│BCD│CDE│DEF│EF│
│GH│GHI│HIJ│IJK│JKL│KL│
│MN│MNO│NOP│OPQ│PQR│QR│
├──┼───┼───┼───┼───┼──┤
│GH│GHI│HIJ│IJK│JKL│KL│
│MN│MNO│NOP│OPQ│PQR│QR│
│ST│STU│TUV│UVW│VWX│WX│
├──┼───┼───┼───┼───┼──┤
│MN│MNO│NOP│OPQ│PQR│QR│
│ST│STU│TUV│UVW│VWX│WX│
└──┴───┴───┴───┴───┴──┘

As you can see, the padding was removed.

Another example. Here you can see that on the far left and right, we have to pad two columns to get a 5-wide neighbourhood centred on the first column:

({}3 5) 4 6⎕A 
┌────┬────┬────┬────┬─────┬─────┐
│1 2 │1 1 │1 0 │1 0 │1 ¯1 │1 ¯2 │
├────┼────┼────┼────┼─────┼─────┤
│0 2 │0 1 │0 0 │0 0 │0 ¯1 │0 ¯2 │
├────┼────┼────┼────┼─────┼─────┤
│0 2 │0 1 │0 0 │0 0 │0 ¯1 │0 ¯2 │
├────┼────┼────┼────┼─────┼─────┤
│¯1 2│¯1 1│¯1 0│¯1 0│¯1 ¯1│¯1 ¯2│
└────┴────┴────┴────┴─────┴─────┘

Now, let’s try implementing Game of Life.

Here are the rules:

  • A cell will stay alive with 2 or 3 neighbours.

  • It will become alive with 3 neighbours.

  • It will die with fewer than 2 or more than 3 neighbours.

Let’s make a world:

4 50 0 1 0 0 1 0
0 0 1 0 0
1 0 0 0 1
0 0 1 0 0
0 1 0 0 1

The 1s indicate live cells while the 0s indicate dead cells. Let’s look at our neighbourhoods:

({}3 3) 4 50 0 1 0 0 1 0 
┌─────┬─────┬─────┬─────┬─────┐
│0 0 0│0 0 0│0 0 0│0 0 0│0 0 0│
│0 0 0│0 0 1│0 1 0│1 0 0│0 0 0│
│0 1 0│1 0 0│0 0 0│0 0 1│0 1 0│
├─────┼─────┼─────┼─────┼─────┤
│0 0 0│0 0 1│0 1 0│1 0 0│0 0 0│
│0 1 0│1 0 0│0 0 0│0 0 1│0 1 0│
│0 0 0│0 0 1│0 1 0│1 0 0│0 0 0│
├─────┼─────┼─────┼─────┼─────┤
│0 1 0│1 0 0│0 0 0│0 0 1│0 1 0│
│0 0 0│0 0 1│0 1 0│1 0 0│0 0 0│
│0 0 1│0 1 0│1 0 0│0 0 1│0 1 0│
├─────┼─────┼─────┼─────┼─────┤
│0 0 0│0 0 1│0 1 0│1 0 0│0 0 0│
│0 0 1│0 1 0│1 0 0│0 0 1│0 1 0│
│0 0 0│0 0 0│0 0 0│0 0 0│0 0 0│
└─────┴─────┴─────┴─────┴─────┘

We can get the number of neighbours by summing. So we make a list with ravel, ,, and use +/ to sum:

({+/,}3 3) 4 50 0 1 0 0 1 0
1 2 1 2 1
1 3 2 3 1
2 3 2 3 2
1 2 2 2 1

We also need to know what the current value is. That is the 5th value in the ravelled neighbourhood:

({5⌷,}3 3) 4 50 0 1 0 0 1 0
0 0 1 0 0
1 0 0 0 1
0 0 1 0 0
0 1 0 0 1

Now we can say that self←5⌷,⍵ and total←+/,⍵:

({self5⌷,  total+/,  self total}3 3) 4 50 0 1 0 0 1 0
┌───┬───┬───┬───┬───┐
│0 1│0 2│1 1│0 2│0 1│
├───┼───┼───┼───┼───┤
│1 1│0 3│0 2│0 3│1 1│
├───┼───┼───┼───┼───┤
│0 2│0 3│1 2│0 3│0 2│
├───┼───┼───┼───┼───┤
│0 1│1 2│0 2│0 2│1 1│
└───┴───┴───┴───┴───┘

Here we have the self and the total for each cell.

The logic is that in the next generation the cell is alive if itself was alive and had 2–3 neighbours (3 or 4 total, including self), or if it was dead and had 3 neighbours. That is

(self  (total3 4))  ((~self)  (total=3))

Let’s plug that in:

({self5⌷,  total+/,  (self  (total3 4))  ((~self)  (total=3))}3 3) 4 50 0 1 0 0 1 0
0 0 0 0 0
0 1 0 1 0
0 1 0 1 0
0 0 0 0 0

This can be shortened considerably, if we so wished. For a detailed walk-though of the shortest possible Game of Life using stencil, see the webinar on dyalog.tv.

can do a further trick, too. If the right operand is a matrix, then the second row indicates the step size. By default it is 1 in every dimension. Consider the following:

({}(2 23))7 7⎕A
┌───┬───┬───┐
│   │   │   │
│ AB│CDE│FG │
│ HI│JKL│MN │
├───┼───┼───┤
│ OP│QRS│TU │
│ VW│XYZ│AB │
│ CD│EFG│HI │
├───┼───┼───┤
│ JK│LMN│OP │
│ QR│STU│VW │
│   │   │   │
└───┴───┴───┘

Here we used a 2-by-2 matrix of all 3s. In other words, we get 3-by-3 neighbourhoods going over 3 rows and 3 columns. Thus, we “chop” the argument, with no overlaps. We can also use even sizes, in which case every “space” between elements (rather than elements themselves) gets to be the centre of a neighbourhood:

({}(2 22))6 6⎕A
({}(2 4))4 6⎕A 
┌──┬──┬──┐
│AB│CD│EF│
│GH│IJ│KL│
├──┼──┼──┤
│MN│OP│QR│
│ST│UV│WX│
├──┼──┼──┤
│YZ│AB│CD│
│EF│GH│IJ│
└──┴──┴──┘
┌────┬────┬────┬────┬────┐
│ ABC│ABCD│BCDE│CDEF│DEF │
│ GHI│GHIJ│HIJK│IJKL│JKL │
├────┼────┼────┼────┼────┤
│ GHI│GHIJ│HIJK│IJKL│JKL │
│ MNO│MNOP│NOPQ│OPQR│PQR │
├────┼────┼────┼────┼────┤
│ MNO│MNOP│NOPQ│OPQR│PQR │
│ STU│STUV│TUVW│UVWX│VWX │
└────┴────┴────┴────┴────┘

Key #

is key, a monadic operator deriving an ambivalent function (i.e. monadic or dyadic depending on usage). The lone operand must be a function, and it gets called dyadically in a manner not too different from stencil’s left operand.

Let’s do the monadic derived function first, i.e. (f⌸) data.

Key will group identical major cells of the data together and call the operand f with the unique element as left argument, and the indices of that element in the data as right argument:

{⍺⍵}'Mississippi'
┌─────┬────────────┬───────────┬────────┐
│┌─┬─┐│┌─┬────────┐│┌─┬───────┐│┌─┬────┐│
││M│1│││i│2 5 8 11│││s│3 4 6 7│││p│9 10││
│└─┴─┘│└─┴────────┘│└─┴───────┘│└─┴────┘│
└─────┴────────────┴───────────┴────────┘

This tells us that “M” is at index 1, “i” at 2 5 8 11, etc. It is very common to use to tally the indices:

{,≢}'Mississippi'
M 1
i 4
s 4
p 2

which gives us the count of each unique element. We can, for example, use this to remove elements which only occur once. We first use key to make a Boolean vector for each unique element:

{1≠≢}'Mississippi'
0 1 1 1

Monadic gives us the unique elements:

'Mississippi'
Misp

We can use / to filter one by the other:

0 1 1 1/'Misp'
isp

Putting it all together, we get

{({1≠≢})/}'Mississippi' ⍝ Unique elements occurring more than once
isp

works on higher rank arrays, too (matrices, 3D blocks, etc.), where it will use the major cells (rows for matrices, layers for 3D blocks…) as “items”.

5 3'AAAABCAAAABBAAA' 
{⍺⍵} 5 3'AAAABCAAAABBAAA' 
AAA
ABC
AAA
ABB
AAA
┌───┬─────┐
│AAA│1 3 5│
├───┼─────┤
│ABC│2    │
├───┼─────┤
│ABB│4    │
└───┴─────┘

Dyadic key then. Behold:

'Mississippi' {⍺⍵} 11
'Mississippi' {⍺⍵} 'ABCDEFGHIJK'
┌─────┬────────────┬───────────┬────────┐
│┌─┬─┐│┌─┬────────┐│┌─┬───────┐│┌─┬────┐│
││M│1│││i│2 5 8 11│││s│3 4 6 7│││p│9 10││
│└─┴─┘│└─┴────────┘│└─┴───────┘│└─┴────┘│
└─────┴────────────┴───────────┴────────┘
┌─────┬────────┬────────┬──────┐
│┌─┬─┐│┌─┬────┐│┌─┬────┐│┌─┬──┐│
││M│A│││i│BEHK│││s│CDFG│││p│IJ││
│└─┴─┘│└─┴────┘│└─┴────┘│└─┴──┘│
└─────┴────────┴────────┴──────┘

Instead of returning the indices of the unique elements (of the right – and only – argument), it returns the elements of the right corresponding to the unique elements of the left.

Atop f⍤g#

Atop has been assigned function⍤function, thus sharing the symbol with the rank operator’s function⍤array. You should be familiar with the 2-train, which is also called “atop”: (f g)Y and X(f g)Y. Maybe you’ve even been burned by f∘g Y being an atop, but X f∘g Y not being an atop. Well, the atop operator is what you would expect, i.e. f⍤g Y is exactly like f∘g Y but X f⍤g Y is f X g Y or X (f g) Y. We strongly recommend transitioning to use in places where you’ve hitherto used monadic f∘g: it will prevent (at least one potential cause of) frustration should you ever decide to add a left argument to your code.

Let’s say you define a function that returns the magnitude of reciprocal:

|÷ ¯4
|÷ ¯5
0.25
0.2

(This could be written without the , but this is a very simple function useful for illustration purposes.)

Now you get a feature request that the function should take a left argument which is a numerator (instead of the default 1).

2 |÷ ¯4
2 |÷ ¯5
1.75
1.8

Oops. However:

|÷ ¯4
|÷ ¯5
2 |÷ ¯4
2 |÷ ¯5
0.25
0.2
0.5
0.4

One way to look at f∘g vs f⍤g is that, when given a left argument, gives it to the left-hand function and gives it to the right-hand function. Other than that, they are equivalent. Another way to look at f∘g vs f⍤g is simply choosing order of the first two tokens in the equivalent explicit expression: X f∘g Y computes X f g Y and X f⍤g Y computes f X g Y. So we’re simply swapping X and f.

Then there’s the classic problem with slashes, especially in tacit programming. If you’ve ever tried using replicate/compress in a train, you’ll have bumped into the fact that slashes prefer being operators over being functions. This means that {(5<⍵)/⍵} doesn’t convert to (5<⊢)/⊢.

While it may not be obvious at first sight, if we define f←5<⊢ it might become clearer that f/⊢ isn’t at all what we want. Now, there’s an axiom in APL that an operator cannot be an operand. (Shh, don’t mention ∘.f). This means that if a slash ends up in a situation where it has to be an operand, it will resort to being a function. You may even have noticed that constructs like ⊢(/⍨)5<⊢ work fine, though ⊢/⍨5<⊢ doesn’t. This is because the / in isolation with the is forced to become the operand of . But since operators bind from the left, ⊢/ binds first, and so ⊢/⍨5<⊢ becomes (⊢/)⍨5<⊢ or (5<⊢)⊢/(5<⊢) which is usually not what you want.

So, to the rescue. If (or any dyadic operator) is found to the immediate left of a slash, then clearly the dyadic operator cannot be the operand of the slash, being a dyadic operator itself, and it can’t be part of the function on the left, since it requires a right-operand, too. Therefore, the slash is forced to become a function. So -⍤/ is the negation of the replicate:

1 0 2-⍤/10 20 30
¯10 ¯30 ¯30

It is easy to think then that “oh, this is an atop, so I should be able to do this with parentheses too; (f g)” but that’d be a mistake: (-/) is just a normal minus-reduction. Since f⍤g is “atop” and (f g) is “atop”, you might think they are interchangeable.

Another mistake is to think: “if a slash is an operand, it’ll be a function” and then think that /∘⊢ would work like ⊢⍤/ by pre-processing the right argument with a no-op rather than post-processing the result with a no-op.

Let’s say we have a two-element vector of a mask and some data, and you want to “apply” the mask to the data… Perhaps your instinct is to try

apply  //

but that gives an enclosed result, which isn’t what we want:

]display apply (1 0 1)'abc'
┌──────┐
│ ┌→─┐ │
│ │ac│ │
│ └──┘ │
└∊─────┘

Instead, we can do something like

apply  ⊃⊢⍤//
]display apply (1 0 1)'abc'
┌→─┐
│ac│
└──┘

In fact, once ⊢⍤/ becomes a common pattern, you can actually help the reader of your code by using ⊢⍤/ so they don’t have to consider if your slash is Replicate or Reduce. For example, if your code says z←x/y it might not be obvious what’s going on. If you instead write z←x⊢⍤/y your reader knows exactly what you’re doing.

Another example. Given a string, replace every character with two copies of itself prefixed and suffixed by a space. For example, 'abc' becomes ' aa  bb  cc '. Yes, you can do this with regex. Please don’t.

( ⍤⍀ 0 2 03×≢)'abc'
 aa  bb  cc 

Over f⍥g#

Recall how f∘g preprocesses the right argument of f using g. One way to look at Over is simply as preprocessing all arguments of f using g. All, as in both or the only. So again f⍥g Y is the same as f⍤g Y and f∘g Y. The difference is, again, when we do a dyadic application. While X f∘g Y is X f(g Y) we have X f⍥g Y be (g X)f(g Y). This may seem like an overly involved operator, but really, the pattern of preprocessing both arguments comes up a lot. Once you start looking for it, you’ll see it all over.

For example, a dyadic function computing the sum of absolute values of its arguments:

¯1 ¯2 3 4 +| 2 3 ¯8 5
3 5 11 9

Given arguments which are vectors, which one has the smallest maximum? Return ¯1 if the left argument has the smallest maximum, 1 if the right one has, or 0 if they are equal.

¯1 ¯2 3 4 ×-(/) 2 3 ¯8 5
¯1

Beautiful use of both Atop and Over. You can, of course, omit the here, unless used inline. OK, how about this: write an alternative to replicate which can take arguments of equal shape, both with rank greater than 1, and replicates the corresponding elements. Since the result might otherwise be ragged, you have to return a vector.

      (2 3⍴⍳6) YourFunction 2 3⎕A
ABBCCCDDDDEEEEEFFFFFF
(2 3⍴⍳6) ⍤/⍥, 2 3⎕A
ABBCCCDDDDEEEEEFFFFFF

Also, in this case, you don’t need ⊢⍤, but it is good for clarity, and necessary if used inline in a train. A golfing tip regarding : you can sometimes use it to pre-process the left argument, when it is a no-op on the right. For example, 1≡⍥,≡,⍴, only ravels the left argument, since the right argument already is a vector.