An expression is code that evaluates to a value. In F#, expressions are the default building block: calculations, bindings, and even if/then branching produce results that can be named, passed onward, or returned. This shifts attention from executing a sequence of updates to transforming input values into output values. section 4.0 section 4.1.4
Table 4.1 Expressions vs. statements (view table figure)
Use the comparison to distinguish expressions, which always return something and may have side effects, from statements, which return nothing and always have side effects. section 4.1
Expression-oriented code keeps calculation separate from its use. Rather than initialize a variable and assign it in several branches, let the branches compute one result and assign it once. Each branch must produce the same type, so a missing fallback branch becomes a compiler error instead of an unnoticed default or null value. section 4.1.3 section 4.1.4
Classifying an age
A conditional can turn an age into "Child", "Adult", or "OAP", then bind that result to ageDescription. The surrounding function can return a greeting string rather than print it directly. The calculation is now independently reusable for console output, file output, or a test. section 4.1.4 section 4.1.5
Expressions compose naturally because one result can become another operation’s input. Extract a calculation such as calculateAgeDescription into a function, give it the values it needs as inputs, and replace the original nested code with a call. Lightweight syntax and type inference make this refactoring practical to repeat. section 4.1.5
Prefer expressions when code is primarily computing a result:
- Make branching return the value needed by the next step instead of mutating a variable across branches. section 4.1.3
- Separate result generation from output or other external actions so the calculation can be reused and unit tested. section 4.1.5
- Pair expressions with immutable bindings to reduce bugs caused by changing state and to make chains of function calls easier to observe. section 4.4 section 14.1.1
Not every operation has a meaningful result. F# uses unit for functions with no useful input or output value, while still treating scopes as value-producing. For ordinary application logic, favor returned values where possible: they reveal behavior through inputs and outputs and make testing more direct. section 4.1.4 section 4.3










