@RGS Well, you can't of course. So by all means, continue to use ∘ if indeed you mean preprocess the (right) argument. But if, conceptually, you are simply applying one function after another, then use ⍤
Of course you engineered an example that was good for you and the example made sense; thanks. I think ⍤ is particularly helpful when dyadic g is similar to monadic g, like - and ÷. My take on this is that when dyadic g is not that related to monadic g, using ∘ might be as suitable as ⍤ a priori, but then dyadic g ≠ monadic g means I probably won't be in any situations similar to the one you just described.
So I can just roll with f⍤g for the monadic cases; when extensions make sense ⍤ is already there, when they make no sense it makes no difference
@Adám Yup, just corrected "roll with f⍤g for the monadic cases"; in the dyadic case f∘g still preprocesses the right arg. Very nice! I am happy to move on now :)
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. (Ping me if you find an example where you actually do want that!)
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.
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.
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.
@Adám Mine is an atop too but a single function; without naming it ⊃⍤(//) will behave differently in a train when compared to the other two. Or did I get it wrong?
So, remember 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.
So 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 ;-)
Yup. And: 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.
But now it is good. 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.