F# let bindings
last modified May 3, 2025
In this article, we explore let bindings in F#. The let keyword is fundamental to F# programming, used for binding names to values and functions.
A let binding associates a name with a value or function in F#. By default, let bindings are immutable, meaning once a value is assigned, it cannot be changed. This immutability fosters reliability and helps prevent unintended side effects, making F# code more predictable.
The let
keyword supports flexible and expressive declarations,
enabling developers to define variables, functions, and even nested bindings in
a concise manner. It also allows for pattern matching, enabling the extraction
of values from complex data structures like tuples and records.
F# basic let binding
The simplest form of let binding assigns a name to a value.
let x = 5 let name = "John Doe" let active = true printfn "%d" x printfn "%s" name printfn "%b" active
We create three simple let bindings for different types of values.
let x = 5
Binds the name x to the integer value 5. The binding is immutable by default.
λ dotnet fsi basic.fsx 5 John Doe true
F# let binding with functions
Let bindings are used to define functions in F#.
let square x = x * x let add a b = a + b let greet name = $"Hello, {name}!" printfn "%d" (square 5) printfn "%d" (add 3 4) printfn "%s" (greet "John")
We define three functions using let bindings and demonstrate their usage.
let square x = x * x
Defines a function that takes one parameter and returns its square.
λ dotnet fsi functions.fsx 25 7 Hello, John!
F# let binding scopes
Let bindings have lexical scope, visible from their point of declaration.
let outer = "I'm outside" if true then let inner = "I'm inside" printfn "%s" inner printfn "%s" outer // printfn "%s" inner // This would cause an error printfn "%s" outer
Demonstrates the scoping rules of let bindings in F#.
let inner = "I'm inside"
This binding is only visible within the if block where it's defined.
λ dotnet fsi scopes.fsx I'm inside I'm outside I'm outside
F# nested let bindings
Let bindings can be nested within other let bindings.
let calculateTotal price quantity = let subtotal = price * quantity let tax = subtotal * 0.08m subtotal + tax let total = calculateTotal 25.0m 3 printfn "Total: %M" total
Shows how to use nested let bindings within a function.
let subtotal = price * quantity let tax = subtotal * 0.08m
These intermediate calculations are only visible within the function.
λ dotnet fsi nested.fsx Total: 81.00
F# mutable let bindings
Let bindings can be made mutable with the mutable keyword.
let mutable counter = 0 printfn "Initial: %d" counter counter <- counter + 1 printfn "After increment: %d" counter counter <- counter + 1 printfn "After second increment: %d" counter
Demonstrates mutable let bindings and their modification.
let mutable counter = 0
The mutable keyword allows the binding to be changed after declaration.
λ dotnet fsi mutable.fsx Initial: 0 After increment: 1 After second increment: 2
F# let bindings with type annotations
Types can be explicitly specified in let bindings.
let age: int = 34 let name: string = "Roger Roe" let height: float = 172.5 printfn "%s is %d years old and %.1f cm tall" name age height
Shows let bindings with explicit type annotations.
let age: int = 34
The colon followed by the type specifies the binding's type explicitly.
λ dotnet fsi typed.fsx Roger Roe is 34 years old and 172.5 cm tall
F# let bindings in pattern matching
Let bindings can be used with pattern matching.
let person = ("John", "Doe", 34) let firstName, lastName, age = person printfn "Name: %s %s, Age: %d" firstName lastName age let first, _, _ = person printfn "First name: %s" first let _, last, _ = person printfn "Last name: %s" last
Demonstrates using let bindings with tuple pattern matching.
let firstName, lastName, age = person
Destructures the tuple into individual bindings in one operation.
λ dotnet fsi pattern.fsx Name: John Doe, Age: 34 First name: John Last name: Doe
F# let bindings with records
Let bindings work naturally with record types.
type Person = { FirstName: string; LastName: string; Age: int } let createPerson first last age = { FirstName = first; LastName = last; Age = age } let john = createPerson "John" "Doe" 34 let jane = { FirstName = "Jane"; LastName = "Smith"; Age = 28 } let { FirstName = f1; LastName = l1 } = john printfn "%s %s" f1 l1 let { FirstName = f2 } = jane printfn "%s" f2
Shows let bindings used with record types and record patterns.
let { FirstName = f1; LastName = l1 } = john
Destructures the record into individual bindings using pattern matching.
λ dotnet fsi records.fsx John Doe Jane
In this article we've explored the various uses of let bindings in F#. They are a fundamental building block of F# programming, used for variables, functions, and pattern matching.