F# has many operators available as standard, for example the boolean OR operator ||
, the arithmetic addition operator +
and the less-than comparison operator <
. A binary operator (operator of two arguments) is traditionally used in the infix position, that is between its arguments. For example,
true || false
2 + 3
3 < 5
However, it is also possible to use one as a function of two arguments by enclosing the operator in parentheses. For example,
(||) true false
(+) 2 3
(<) 3 5
Partial application of operators
Because operators can be used as regular functions, it is possible to partially apply them, like you can with any other F# function. One reason you might want to do this would be to make a pipeline more concise and/or easier to read: compare
coordinates
|> List.filter (fun (x, _) -> x = 0)
with
coordinates
|> List.filter (fst >> (=) 0)
The second snippet can be interpreted almost like English: take the coordinates list and filter it to only those whose first element equals 0.
⚠️ Warning: partial application of non-symmetric operators can be confusing
One thing worth noting in the above example is that fun (x, _) -> x = 0
and fst >> (=) 0
don't call =
in exactly the same way. In the first snippet 0
is the second argument to =
, whereas in the second snippet it is the (partially-applied) first argument. This doesn't really matter in this case, because =
is symmetric (for a given x
and y
, x = y
and y = x
always have the same value). However, it is important to note the distinction when partially applying a non-symmetric operator.
For example, it could be easy to read the code below at a glance and interpret the behaviour as "take the coordinates list and filter it to only those whose first element is greater than 0".
coordinates
|> List.filter (fst >> (>) 0)
However, because 0
is applied as the first argument to >
, the behaviour is the same as that for the following code
coordinates
|> List.filter (fun (x, _) -> 0 > x)
which makes it easier to see that the code is actually filtering to values whose x-coordinate is less than 0.
My advice would be to avoid partial application of non-symmetric operators. At the very least, be careful.
Summary
Operators wrapped in parentheses can be used as functions. Because F# lets you partially apply functions, this can be useful for making your code more concise. This technique is particularly useful with symmetric operators, but should be used with caution (or avoided altogether) when working with non-symmetric operators.