Skip to content
AgileLogics
5 min read

Pipelines and Composition

Prerequisites: Functions

The pipe operator |> passes the value on its left as the last argument of the function on its right. x |> f means f x:

let double x = x * 2
let increment x = x + 1

printfn "%d" (increment (double 5))   // nested calls: read inside-out
5 |> double |> increment |> printfn "%d"   // pipeline: read left to right
11
11

Pipelines read in the order things happen. They are the backbone of idiomatic F#, especially with collections:

[1 .. 10]
|> List.filter (fun n -> n % 2 = 0)
|> List.map (fun n -> n * n)
|> List.sum
|> printfn "Sum of even squares: %d"
Sum of even squares: 220

Each line takes the previous result: keep the even numbers, square them, add them up, print. Putting each step on its own line starting with |> is the standard style.

Composition

The composition operator >> joins two functions into a new one: f >> g means "do f, then g".

let double x = x * 2
let increment x = x + 1

let doubleThenIncrement = double >> increment
let incrementThenDouble = increment >> double

printfn "%d" (doubleThenIncrement 5)
printfn "%d" (incrementThenDouble 5)

let normalize = (fun (s: string) -> s.Trim()) >> (fun s -> s.ToLower())
printfn "[%s]" (normalize "  HeLLo ")
11
12
[hello]
  • |> applies functions to a value now.
  • >> builds a new function to use later.
  • <| and << are the backward versions; <| is occasionally handy to avoid parentheses (printfn "%d" <| square 4), << is rare.

Why the last argument?

|> fills in the last parameter, so F# library functions take the data last: List.map f list, String.replicate 3 "ab". Design your own functions the same way — configuration first, data last — and they'll fit into pipelines.

Practice

Starting from the list [" apple "; "Banana"; " cherry"], write a pipeline that trims each word, converts it to upper case, joins them with " | ", and prints the result.

Show solution
["  apple "; "Banana"; " cherry"]
|> List.map (fun s -> s.Trim().ToUpper())
|> String.concat " | "
|> printfn "%s"
APPLE | BANANA | CHERRY