Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why use a Backward Pipe Operator instead of Function Chaining?

Tags:

f#

Why use a backward pipe operator instead of a function chaining?

let distanceFromOrigin aPoint =
    let square x = x * x
    sqrt (square aPoint.x + square aPoint.y)

vs

let distanceFromOrigin aPoint =
    let square x = x * x
    sqrt <| square aPoint.x + square aPoint.y
like image 983
unj2 Avatar asked Mar 26 '11 16:03

unj2


3 Answers

Because of the left associativity (f <| g <| x is parsed as (f <| g) <| x and sadly not as f <| (g <| x) which is equivalent to x |> g |> f), I found it useful only when you want to remove parentheses (instead of f (long expression), you write f <| long expression).

like image 58
Ramon Snir Avatar answered Nov 08 '22 11:11

Ramon Snir


Choosing between f x, x |> f and f <| x is mainly a question of style. There's no absolute rule for choosing one instead of the other. The |> operator is very popular, and it's a good idea to use it.

<| is less frequent, but if you look in the compiler's sources, you'll find a couple of uses. For example:

raise <| System.InvalidOperationException (SR.GetString(SR.QillFormedAppOrLet))

if info.precision then
  failwithf "%s" <| FSComp.SR.forFormatDoesntSupportPrecision(ch.ToString())

<| is used to remove a parenthesis, and I think it make the code more readable when used carefully. When you see it, you know the following expression is the argument to your function. You don't have to search for the closing parenthesis. I suggest you use it sparingly, and you should generally avoid mixing <| and |> in the same expression, as it can be very confusing.

I sometimes enjoy using this operator to create a "block", with a fun or lazy keyword.

let f (l: Lazy<_>) = ()
let g (f: _ -> _ -> _) = ()

f <| lazy
    let x = 1 + 1
    x * x

g <| fun x y ->
    let sqr n = n * n
    sqr x + sqr y

The block is based on indentation, so it fits quite well in F# code. Thanks to the <| operator, you don't need a trailing parenthesis.

like image 40
Laurent Avatar answered Nov 08 '22 11:11

Laurent


As Scott Wlaschin pointed out here, the backward pipe operator is useful if you need to pass in data as the first parameter (rather than the last) somewhere along a chain of pipes. Consider the following:

let replace (replaceThis: string) (withThis: string) (input: string) =
    input.Replace(replaceThis, withThis)

let joinWith (input1: string) (input2: string) =
    input1 + " " + input2

let testString = "Happy"

let amendedString = testString
                    |> replace "H" "Cr"
                    |> joinWith "birthday"

amendedString is "birthday Crappy". Let's say I want it to be "Crappy birthday" instead. I can achieve that by using the backward pipe operator:

let amendedString = testString
                    |> replace "H" "Cr"
                    |> joinWith <| "birthday"

Now amendedString is "Crappy birthday", which is what I want.

like image 8
M.Y. Babt Avatar answered Nov 08 '22 09:11

M.Y. Babt