F# conditionals
last modified May 17, 2025
In this article, we explore conditionals in F# and how they help control program flow based on boolean expressions. Understanding conditional logic is crucial for making decisions within F# programs, ensuring efficient execution paths.
F# conditionals allow programs to evaluate expressions and execute different
code branches dynamically. The language provides if
,
elif
, and else
constructs, similar to other
programming languages, but with functional enhancements. Since F# is
expression-based, conditional statements always return a value, making them more
composable and seamlessly integrating with functional programming paradigms.
Unlike imperative languages where conditionals often lead to side effects, F# emphasizes immutability and expressions, ensuring clarity and predictability in code execution. By leveraging concise conditional expressions, developers can write clean, maintainable code while avoiding unnecessary complexity.
Single conditions
The simplest conditional checks a single boolean expression. In F#, if expressions must have an else branch when used in a context that expects a value, but can omit it for side-effect-only operations.
open System let r = Random().Next(-5, 5) if r > 0 then printfn "positive value" if r < 0 then printfn "negative value" if r = 0 then printfn "zero"
This example shows three separate if expressions checking a random number. Each if stands alone, potentially executing none, one, or multiple print statements.
λ dotnet fsi single_condition.fsx negative value
Multiple conditions
For mutually exclusive conditions, F# provides elif
(else
if
) branches. These allow chaining conditions where only one branch will
execute.
open System let r = Random().Next(-5, 5) if r > 0 then printfn "positive value" elif r < 0 then printfn "negative value" elif r = 0 then printfn "zero"
This version uses elif to ensure only one message prints. The conditions are checked in order until one matches, then the rest are skipped.
λ dotnet fsi multiple_conditions.fsx positive value
The else if
is an equivalent to elif
in F#.
open System let r = Random().Next(-5, 5) if r > 0 then printfn "positive value" else if r < 0 then printfn "negative value" else if r = 0 then printfn "zero"
The previous example can be rewritten using else if. The behavior remains the same, but the syntax is slightly different. The elif keyword is more idiomatic in F#.
The else keyword
The else
branch serves as a catch-all when no previous conditions
match. It must come last in an if expression and doesn't take a condition.
open System let r = Random().Next(-5, 5) if r > 0 then printfn "positive value" elif r < 0 then printfn "negative value" else printfn "zero"
Here we've replaced the final elif with else. This makes the zero case clearer as it handles anything not caught by previous conditions.
λ dotnet fsi else_keyword.fsx zero
If as expression
Unlike many languages, F#'s if is an expression that returns a value. All branches must return the same type, and else is required when used this way.
let describeNumber n = if n > 0 then "Positive" elif n < 0 then "Negative" else "Zero" printfn "%d is %s" 5 (describeNumber 5) printfn "%d is %s" -3 (describeNumber -3) printfn "%d is %s" 0 (describeNumber 0) let absValue x = if x >= 0 then x else -x printfn "Absolute value of -4: %d" (absValue -4)
These examples show if expressions returning values. describeNumber returns strings, while absValue returns numbers. Both include all required branches.
λ dotnet fsi if_expression.fsx 5 is Positive -3 is Negative 0 is Zero Absolute value of -4: 4
Nested conditionals
If expressions can be nested inside other if expressions to create more complex decision trees. Proper indentation is crucial for readability.
let categorizeTemp temp = if temp > 30 then "Hot" elif temp > 20 then if temp > 25 then "Warm" else "Pleasant" elif temp > 10 then "Mild" else "Cold" printfn "15°C is %s" (categorizeTemp 15) printfn "22°C is %s" (categorizeTemp 22) printfn "27°C is %s" (categorizeTemp 27)
This example shows nested conditionals for temperature categorization. The middle range has a sub-categorization between Warm and Pleasant.
λ dotnet fsi nested_conditionals.fsx 15°C is Mild 22°C is Pleasant 27°C is Warm
Boolean operators
Complex conditions can be built using boolean operators like && (and), || (or), and not. Parentheses help clarify evaluation order.
let checkRange x y = if x > 0 && y > 0 then "Both positive" elif x > 0 || y > 0 then "One positive" elif not (x > 0 || y > 0) then "Neither positive" else "Impossible case" printfn "%s" (checkRange 3 4) printfn "%s" (checkRange -2 5) printfn "%s" (checkRange -1 -1)
This demonstrates combining conditions with boolean operators. The not operator inverts the result of its operand.
λ dotnet fsi boolean_operators.fsx Both positive One positive Neither positive
Pattern matching alternative
For complex conditional logic, pattern matching often provides cleaner code than nested if/elif expressions, especially with multiple test values.
let describeNumberMatch n = match n with | x when x > 0 -> "Positive" | x when x < 0 -> "Negative" | _ -> "Zero" let checkCoordinates x y = match x, y with | 0, 0 -> "Origin" | _, 0 -> "On X axis" | 0, _ -> "On Y axis" | _ -> "Somewhere else" printfn "%s" (describeNumberMatch 7) printfn "%s" (checkCoordinates 0 5)
These examples show pattern matching as an alternative to if expressions. The when clauses allow additional conditions on matched values.
λ dotnet fsi pattern_matching.fsx Positive On Y axis
F# conditionals provide flexible ways to make decisions in your code. The if/ elif/else expressions work similarly to other languages but with stronger emphasis on expression-oriented programming. For complex conditions, consider pattern matching as an often cleaner alternative.