Combining # & pure functions with post-fix notation
I use this style sometimes, and I see not issues with it.
You mention precedence. A nice thing about //
is that it has very low precedence, even lower than &
. Thus it is not necessary to parenthesize the functions.
You can use
f @ (#&) @ arg
with the parentheses, or you can use
arg // #& // f
without them.
One annoying thing about chaining with //
is the auto-indentation done by the front end:
I do not like this. Using RightComposition
can alleviate the issue:
This is what this looks like in practice in an example in the IGraph/M documentation:
One thing to watch out for with Composition
and RightComposition
is that they effectively negate any Hold*
attributes of the composed functions:
1 + 1 // Hold // f
(* f[Hold[1 + 1]] *)
1 + 1 // Hold /* f
(* f[Hold[2]] *)
Precedence is only an issue if your input contains a lot of different operators and you aren't aware of their precedence. For example, something like
expr1 /* expr2 // expr4~expr5~expr6 @ expr7
Here you can find a list with the most common operators listed in order of decreasing precedence. This answer contains information on how to find the precedence of an operator and a link to the A New Mathematica Programming Style presentation from the 2007 Wolfram Technology Conference.
The main reason not to use postfix and prefix notation excessively in the final code is performance. Especially when using a lot of computational cheap pure functions, such as in your input example, the introduced overhead is significant:
Table[
{(Range[100] // Partition[#, 10] & //
Grid[#, Spacings -> {1, 1}] & // AbsoluteTiming //
First)/(Grid[Partition[Range[100], 10], Spacings -> {1, 1}] //
AbsoluteTiming // First),
(Grid[#, Spacings -> {1, 1}] &@Partition[#, 10] &@Range[100] //
AbsoluteTiming //
First)/(Grid[Partition[Range[100], 10], Spacings -> {1, 1}] //
AbsoluteTiming // First)}
, 200] // Mean
{1.23117, 1.23615}
I mainly use postfix notation for the output styling part of my code. The personal preferences for different input forms is probably due to one's flow of thinking and programming. f[x]
is closer to the mathematical notation $ f(x) $ and f @ x
is like saying " f of x" or "f applied to x". Both these notations are very similar in their logic, as they start with the output one wants to get or the thing one wants to do, whereas x // f // g
is more inline with a step-by-step approach of "first take/make x, than apply f to it, and finally do g".
As you said, there could be some precedence issues. You can use Defer to check if there is any precedence issues. (I tried to use PrecedenceForm to check precedence, however there is an error message found by Karsten 7. So I used Defer instead.
Defer[Range[10] // Partition[#, 10] & // Grid[#, Spacings -> {1, 1}] &]
(*(Grid[#1, Spacings -> {1, 1}] &)[(Partition[#1, 10] &)[Range[10]]]*)
Or you can use ReverseComposition to avoid precedence issues.
In:
ReverseComposition[fs__] := Composition[Sequence @@ Reverse@{fs}]
partition[xs_] := Partition[xs, 10]
grid[xss_] := Grid[xss, Spacings -> {1, 1}]
f = ReverseComposition[Range, partition, grid]
f@100
Out:
grid@*partition@*Range