jonnifyfsharp

Dive C1.04.1

The pipe operator

Forward application: x |> f is exactly f x, with the value already in hand.

The pipe |> is the most-used operator in idiomatic F#, and it does one small thing: it applies a function to a value, written the other way round. By definition let (|>) x f = f x, so x |> f and f x compute the same value. The point is not the saved keystroke — it is reading order. 5 |> double |> inc reads left to right as "5, doubled, incremented", in the order the steps actually run; the nested-call form inc (double 5) reads inside-out, from the middle. This dive defines the pipe, steps a value through a chain, and adds the backward pipe <|.

C1.04.1 · part 1

One line of definition

The pipe is not a compiler keyword; it is an ordinary operator defined in FSharp.Core. Its whole definition is one line, and it explains the behaviour completely: the operator takes a value x on the left and a function f on the right, and returns f x.

// from FSharp.Core — the whole definition
let (|>) x f = f x

let double x = x * 2          // val double : int -> int
let inc x = x + 1             // val inc : int -> int

5 |> double                    // val it : int = 10   ==  double 5
5 |> double |> inc             // val it : int = 11   ==  inc (double 5)

The pipe associates to the left, so a chain reduces from the left: 5 |> double |> inc is (5 |> double) |> inc. The left part runs first and produces 10; that result is then piped into inc. The reading order on the page matches the evaluation order in the machine.

|>
forward application: x |> f = f x. The value is on the left, the function on the right.
reading order
5 |> double |> inc reads as the steps run; inc (double 5) reads inside-out.
associativity
the pipe is left-associative: a chain reduces left to right, each result feeding the next stage.
same value
the pipe changes the spelling, never the result: 5 |> double and double 5 both give 10.
C1.04.1 · part 2

Inside-out, then left to right

Take the chain 5 |> double |> inc and its nested-call twin inc (double 5). They produce the same 11, but a reader walks them in opposite directions. The diagram lays the two forms over the same three tokens — the input, the two functions — so the difference is only the path the eye takes.

5 |> double |> inc · the same result, read two ways

PIPE · reads left to right 5 |> double |> inc 11 NESTED · reads inside-out inc ( double ( 5 ) ) start at 5, work outward
both 5 |> double |> inc = inc (double 5) = 11 · the pipe puts the stages in evaluation order

The pipe and the nested call compute the same value. The pipe earns its place by reading in the order the work happens, not from the middle out.

Step 5 |> double |> inc |> square

Walk the value through a fixed three-function chain. The input is 5; the chain is double (x*2), then inc (x+1), then square (x*x). Each step lights the stage about to run and shows the equivalent f x rewrite. The running value is 5 → 10 → 11 → 121.

5 |> double |> inc |> square · one stage at a time

5 double 10 inc 11 square 121 |> |>
  1. 5 |> double double 5 = 10
  2. 10 |> inc inc 10 = 11
  3. 11 |> square square 11 = 121
  4. 121 the value
step 0 of 3 · input 5 · next, pipe into double

Three functions, one value, four readings of the same number. The pipe is the chain; each stage is one f x.

C1.04.1 · part 3

The backward pipe <|

The pipe has a mirror image. The backward pipe <| applies the function on the left to the value on the right: f <| x = f x. It does not reverse the data flow — it keeps the function first, the value last — and its everyday use is to drop a pair of parentheses on the right-hand argument.

// from FSharp.Core
let (<|) f x = f x

printfn "%d" (1 + 2)            // prints 3
printfn "%d" <| 1 + 2           // prints 3 — no parentheses needed

Both lines print 3. The backward pipe has lower precedence than +, so 1 + 2 is computed first and the result handed to printfn "%d". The forward pipe is the one a program leans on; the backward pipe is a smaller tool, kept for the cases where it reads more cleanly than a bracket.

Forward application

Apply a function to a value, written so the value comes first and the steps read in order.

F# today

x |> f threads the value; f <| x drops the brackets. Both are f x.

Where this leads

The pipe needs a value now — it threads what you already hold. The next dive asks the other question: what if there is no value yet, only functions to wire together for later? That is composition >>, which glues two functions into one new function and names no argument at all.

References

Sources

Related in this course