ZetCode

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.

single_condition.fsx
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.

multiple_conditions.fsx
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#.

multiple_conditions.fsx
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.

else_keyword.fsx
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.

if_expression.fsx
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.

nested_conditionals.fsx
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.

boolean_operators.fsx
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.

pattern_matching.fsx
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.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.