A higher-order function (HOF) accepts a function as an argument, letting one piece of logic stay fixed while a caller supplies the behavior that varies. This replaces hard-coded actions such as console output with a value that can write to a file, database, or another destination. section 7.1
let executeCalculation logger a b =
let answer = a + b
logger $"Adding {a} and {b} = {answer}"
answer
The logger is an input function: calculation remains unchanged while output becomes selectable at the call site. section 7.1
This is a lightweight alternative to an interface or base class when the required variation is simply behavior. The supplied function must match the expected signature; F# infers that signature from how the argument is used. Parentheses around a function argument help distinguish it from the HOF’s ordinary arguments. section 7.1 section 15.2.2
Inject a printer
A customer-description function can receive a printer and use it wherever it would otherwise call printfn. Pass a console printer in one case and a file-writing printer in another. For a tiny one-off behavior, pass a lambda inline; name the function separately when it grows beyond a small expression. section 15.2.2 section 7.1
HOFs also power collection processing. List.map receives a transformation function and a list, then returns a list of transformed values. Reading its signature—('T -> 'U) -> 'T list -> 'U list—shows the function input, collection input, and resulting collection in order. section 7.2.3 section 7.6
For testing, make a side effect an argument. An invoice workflow can accept save instead of directly calling SQL; production supplies the real save function, while a test supplies a fake. This tests surrounding business logic without a mocking library. section 14.3.6
Use higher-order functions deliberately:
- Choose them when a stable algorithm needs one varying behavior, such as logging, printing, saving, or transforming data. section 7.1
- Read function signatures before combining collection functions; the function argument often explains why a composition does or does not fit. section 7.2.3
- Prefer data-in, data-out functions where possible, then introduce a HOF when varying behavior is genuinely needed. section 15.2.5
- Avoid accumulating many function arguments in one function, because the resulting signatures become difficult to understand. section 15.2.2 section 15.2.5
A function argument may perform I/O, so its presence prevents a compiler guarantee of purity. When purity is required, separate data generation from external behavior: return the text first, then compose that pure function with a specific printer at the edge. section 15.2.3













