F# Guide — Function Composition: Building New Functions
In this tutorial, you will learn about F# Guide. We cover key concepts, practical examples, and best practices to help you master this topic.
Function composition combines two or more functions to create a new function, where the output of one function becomes the input of the next, enabling building complex operations from simple building blocks.
What You'll Learn
- Forward composition (>>)
- Backward composition (<<)
- Composition vs piping
- Point-free style
- Practical composition patterns
Why It Matters
Composition lets you build complex operations by combining simple, tested functions. It encourages reusable, modular code. Durga Antivirus Pro composes multiple analysis stages into a pipeline.
Real-World Use
Data processing chains, validation sequences, configuration transformations, and middleware stacks all benefit from function composition.
flowchart LR
A["Composition"] --> B[">> Operator"]
B --> C["<< Operator"]
C --> D["Point-Free"]
D --> E["Patterns"]
A:::current --> B
style A fill:#2563eb,stroke:#2563eb,color:#fff
style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
style E fill:#f1f5f9,stroke:#94a3b8,color:#64748b
Forward Composition
let double x = x * 2
let addOne x = x + 1
let toString (x: int) = string x
// Compose: double -> addOne -> toString
let processThenString = double >> addOne >> toString
processThenString 5 // "11"
// Step 1: double 5 = 10
// Step 2: addOne 10 = 11
// Step 3: toString 11 = "11"
Backward Composition
let double x = x * 2
let addOne x = x + 1
// Backward composition: right-to-left
let doubleThenAdd = addOne << double
// Same as: double >> addOne
doubleThenAdd 5 // 11
// First applies double (5 -> 10)
// Then applies addOne (10 -> 11)
Composition vs Piping
// Piping: applies to a specific value
let result = 5 |> double |> addOne
// Composition: creates a new function
let pipeline = double >> addOne
let result2 = pipeline 5
// Use piping when you have a specific value
// Use composition when you need a reusable function
Point-Free Style
// Pointful (explicit argument)
let process x =
x |> double |> addOne |> toString
// Point-free (no explicit argument)
let process = double >> addOne >> toString
// Both are equivalent
// Point-free emphasizes the transformation pipeline
Composition with Lists
let numbers = [1; 2; 3; 4; 5]
// Without composition
numbers
|> List.filter (fun x -> x % 2 = 0)
|> List.map (fun x -> x * 2)
|> List.sum
// With composition
let processList = List.filter (fun x -> x % 2 = 0) >> List.map (fun x -> x * 2) >> List.sum
let result = processList numbers
Multi-Argument Composition
let add x y = x + y
let multiply x y = x * y
// Partial application enables composition
let addFive = add 5
let double = multiply 2
let addFiveThenDouble = addFive >> double
addFiveThenDouble 3 // (3+5)*2 = 16
Composition with Options/Results
let parse (s: string) =
match System.Int32.TryParse(s) with
| (true, n) -> Ok n
| _ -> Error "parse"
let validate x =
if x >= 0 then Ok x
else Error "negative"
let process = parse >> Result.bind validate
process "5" // Ok 5
process "-3" // Error "negative"
process "abc" // Error "parse"
Common Mistakes
1. Over-using point-free style
Point-free can reduce clarity for complex logic. Use it when it makes code clearer, not as a goal.
2. Confusing composition and piping
>> composes functions. |> pipes values. They serve different purposes.
3. Wrong argument order
f >> g means apply f first, then g. f << g means apply g first, then f.
4. Type mismatches
The output type of the first function must match the input type of the second.
5. Debugging composed functions
Composed functions are harder to debug. Extract intermediate steps or use logging.
Practice Questions
1. What does f >> g do? Creates a new function that applies f, then g, passing the result of f as input to g.
2. What is point-free style? Defining functions without explicitly naming arguments, using composition operators instead.
3. When should you use composition vs piping? Use composition to create reusable functions. Use piping to Process specific values.
Challenge: Compose a data processing pipeline using only composition operators, without explicit arguments.
FAQ
{{< faq question="Is function composition efficient?" >}} Yes. Composed functions are inlined and optimized by the compiler. There's no runtime overhead compared to manual chaining. {{< /faq >}}
{{< faq question="Can I compose functions with different numbers of arguments?" >}}
Yes, using partial application. let addThenDouble = add 5 >> multiply 2 works because partial application makes each step single-argument.
{{< /faq >}}
{{< faq question="How many functions can I compose?" >} As many as needed. But very long compositions should be broken into named intermediate functions for clarity. {{< /faq >}}
{{< faq question="What happens if a composed function throws?" >} Composed functions are just function calls. Standard Exception Handling applies. {{< /faq >}}
{{< faq question="Can I compose methods?" >}
Not directly. Wrap methods in functions first: let toUpper (s: string) = s.ToUpper().
{{< /faq >}}
Mini Project
Build a string processing library using function composition:
let trim (s: string) = s.Trim()
let capitalize (s: string) =
if System.String.IsNullOrEmpty(s) then s
else s.[0].ToString().ToUpper() + s.[1..]
let removePunctuation (s: string) =
System.String(s |> Seq.filter System.Char.IsLetterOrDigit |> Seq.toArray)
let truncate maxLen (s: string) =
if s.Length <= maxLen then s else s.[..maxLen-1] + "..."
let sanitizeTitle = trim >> removePunctuation >> capitalize >> truncate 50
sanitizeTitle " hello, world! this is a long title "
// "Hello world this is a long title"
What's Next
Now that you understand composition, explore asynchronous programming with async workflows.
| Topic | Description | Link |
|---|---|---|
| F# Async | Async programming | {{< ref "18-async" >}} |
| F# Task | Task-based async | {{< ref "19-task" >}} |
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro