Earlier this week I was looking at a beautiful snippet of F#
which defines a better backwards pipe operator: `^<|`

.

let inline (^<|) f a = f a

This got me thinking...

In Haskell the precedence of
an operator can be defined arbitrarily, via the `infix`

,
`infixr`

, and `infixl`

commands.
The Haskell 2010 report describes many of the defaults
(§4.4.2), like so:

Precedence | Left associative operators | Non-associative operators | Right associative operators |
---|---|---|---|

9 | !! | . | |

8 | ^, ^^, ** | ||

7 | *, /, `div`, `mod`, `rem`, `quot` | ||

6 | +, - | ||

5 | :, ++ | ||

4 | ==, /=, <, <=, >, >=, `elem`, `notElem` | ||

3 | && | ||

2 | || | ||

1 | >>, >>= | ||

0 | $, $!, `seq` |

Therefore all the built in operators, operators in libraries, and so on behave however they're defined. For example this Haskell code:

map (3*) $ [1,2,5] !! 1 : []

Returns: [6]

But this doesn't compile because the `!!`

is of higher precedence and therefore `[1]:1`

would
be evaluated:

map (3*) $ [1] : [1,2,4] !! 1

The fixity and precedence of operators in libraries can
be checked in GHCi with the `:info`

command:

Prelude>:module Control.ApplicativePrelude Control.Applicative>:info <$>(<$>) :: (Functor f) => (a -> b) -> f a -> f b -- Defined in Data.Functor infixl 4 <$>Prelude Control.Applicative>:info <*>class (Functor f) => Applicative f where ... (<*>) :: f (a -> b) -> f a -> f b ... -- Defined in Control.Applicative infixl 4 <*>Prelude Control.Applicative>

(Note: applicative functors seem pretty awesome!)

By default, an operator behaves as if it were `infixl 9`

.

F#, on the other hand, uses the initial character of the operator, and a predefined list of operator precedence.

MSDN describes the operator precedence, which behaves like so (highest to lowest, 26 levels):

Left associative operators | Non-associative operators | Right associative operators |
---|---|---|

f<types> | ||

f(x) | ||

. | ||

prefix operators (+op, -op, %, %%, &, &&, !op, ~op) | ||

| (pattern match) | ||

f x (function application) | ||

** op | ||

* op, / op, % op | ||

- op, + op, (binary) | ||

:?>, :? | ||

:: | ||

^ op | ||

&&&, |||, ^^^, ~~~, <<<, >>> | ||

< op, > op, =, | op, & op | ||

&, && | ||

or, || | ||

, | ||

:= | ||

-> | ||

if | ||

function, fun, match, try | ||

let | ||

; | ||

| (pipe) | ||

when | ||

as |

The difference between the built in `<|`

and the
new `^<|`

is based on that, entirely.
(Above, you'll see the `|`

for pipes is in the
lower left and the `^`

is near the middle row right.)

let inline (<|) f a = f a // built in let inline (^<|) f a = f a // new

This means that the `^<|`

operator gets
a higher priority than the `|>`

operator --
like the built in `<|`

-- but the new one is
right associative, so it behaves like Haskell's `$`

.
This allows things like those given by Stephen Swenson in the snippet:

module AssociativityComparison = let forward_pipe = {1..10} |> Seq.map (fun x -> x + 3) |> Seq.toList let normal_backward_pipe = Seq.toList <| (Seq.map (fun x -> x + 3) <| {1..10}) let high_prec_right_assoc_backward_pipe = Seq.toList ^<| Seq.map (fun x -> x + 3) ^<| {1..10} module PrecedenceComparison = let normal_backward_pipe = {1..10} |> (Seq.map <| (fun x -> x + 3)) let high_prec_right_assoc_backward_pipe = {1..10} |> Seq.map ^<| fun x -> x + 3

This will be useful!

-- Kevin