Reading — step 1 of 7
Learn
F# functions are first-class values. Combined with currying and composition operators, they make pipelines natural. Microsoft's F# docs treat this as central to the language's style.
Functions as values
let double x = x * 2
let f = double // alias
f 5 // 10
let apply g x = g x
apply double 7 // 14
Functions can be passed, returned, stored. Type signatures use ->: int -> int for one-arg.
Currying
All F# functions are curried by default:
let add a b = a + b // int -> int -> int
let add5 = add 5 // int -> int (partial)
add5 10 // 15
Calling add 5 returns a function. Apply more args to fully evaluate.
The pipeline operator |>
let result =
[1..10]
|> List.filter (fun x -> x % 2 = 1)
|> List.map (fun x -> x * x)
|> List.sum
// 1 + 9 + 25 + 49 + 81 = 165
x |> f is f x. F#'s signature operator — used everywhere for top-down value flow.
<| is the reverse pipe: f <| x is f x. Less common but useful for one-shot avoiding parens:
printfn "%d" <| List.sum [1; 2; 3]
Composition operators
>> — forward composition:
let addThenDouble = (+) 1 >> (*) 2
addThenDouble 5 // (5 + 1) * 2 = 12
f >> g is fun x -> g (f x). Reads left-to-right. Builds reusable composed functions.
<< — backward composition:
let doubleThenAdd = (+) 1 << (*) 2
doubleThenAdd 5 // (5 * 2) + 1 = 11
f << g is fun x -> f (g x). Reads right-to-left.
Pipeline vs composition
// Pipeline — value flow:
5 |> double |> string
// => "10"
// Composition — function-building:
let stringDouble = double >> string
stringDouble 5
// => "10"
Pipeline for ad-hoc transformation of a value. Composition for building reusable composite functions.
Common combinators
id x // x — identity
fst (a, b) // a — first of tuple
snd (a, b) // b — second
List.map (fun x -> x * 2) // partial application
List.filter (fun x -> x > 0)
List.fold (fun acc x -> acc + x) 0
// Unflipped vs flipped:
List.map double [1; 2; 3] // [2; 4; 6]
[1; 2; 3] |> List.map double // same; pipeline-friendlier
Notice List.map takes the function FIRST, list SECOND. Pipeline applies the list — so list |> List.map fn works naturally.
Higher-order helpers
List.collect f xs // map then concat — flatMap
List.partition pred xs // (yes-list, no-list)
List.tryFind pred xs // Option<T>
List.fold f init xs // left fold
List.foldBack f xs init // right fold (note arg order)
List.iter (printfn "%d") [1; 2; 3] // for side effects
The *Back family (foldBack, scanBack) takes the list FIRST and accumulator LAST — opposite of fold. Easy to mix up.
Common mistakes
- Using
f xwhen you meantf(x)— F# doesn't need parens;f xis the call. Parens for grouping only. - Forgetting to fully apply —
add 5with no second arg is a function value, not 5+nothing. >>vs|>— easy to confuse.>>composes functions;|>applies value through function.foldvsfoldBackargument order — different. fold takes (f, init, list); foldBack takes (f, list, init).- Passing methods directly — .NET methods are not F# functions. Wrap in lambda:
(fun x -> obj.Method x)or useobj.Method(works in many cases via auto-conversion).
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…