Learn about variables in F#. This tutorial explains how to create and use variables in F# programming.
last modified May 3, 2025
In this article, we explore how to effectively work with variables in F#. Variables are fundamental building blocks in any programming language.
A variable is a named storage location that holds a value. In F#, variables are immutable by default, meaning their values cannot be changed after initialization. However, mutable variables can be created using the mutable keyword. F# uses type inference to determine variable types, but explicit typing is also supported.
Immutable variables are the default in F#. Once assigned, their value cannot change.
immutable.fsx
let x = 5 let name = “John Doe” let active = true
printfn “%d” x printfn “%s” name printfn “%b” active
The program defines three immutable variables of different types: integer, string, and boolean. We then print their values.
let x = 5
This creates an immutable integer variable x with value 5. The let keyword is used for variable declaration.
λ dotnet fsi immutable.fsx 5 John Doe true
Mutable variables can have their values changed after initialization.
mutable.fsx
let mutable counter = 0 printfn “Initial value: %d” counter
counter <- counter + 1 printfn “After increment: %d” counter
counter <- counter + 1 printfn “After second increment: %d” counter
We create a mutable counter variable, increment it twice, and print its value at each step.
let mutable counter = 0
The mutable keyword makes the variable changeable. Without it, this would be a compilation error.
counter <- counter + 1
The <- operator is used to assign new values to mutable variables.
λ dotnet fsi mutable.fsx Initial value: 0 After increment: 1 After second increment: 2
F# supports explicit type annotations for variables.
typed.fsx
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
We declare three variables with explicit type annotations and print them.
let age: int = 34
The colon followed by the type specifies the variable’s type explicitly.
λ dotnet fsi typed.fsx Roger Roe is 34 years old and 172.5 cm tall
Variables in F# have block scope, visible only within their defining block.
scope.fsx
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
The example demonstrates variable scoping in F#. The inner variable is only accessible within the if block.
let inner = “I’m inside”
This variable is only visible within the if block where it’s defined.
λ dotnet fsi scope.fsx I’m inside I’m outside I’m outside
F# allows shadowing - declaring a new variable with same name in narrower scope.
shadowing.fsx
let x = 10 printfn “Outer x: %d” x
let innerFunction () = let x = 20 printfn “Inner x: %d” x
innerFunction() printfn “Outer x again: %d” x
We demonstrate variable shadowing by creating a new x inside a function.
let x = 20
This creates a new x that shadows the outer x within the function’s scope.
λ dotnet fsi shadowing.fsx Outer x: 10 Inner x: 20 Outer x again: 10
Tuples allow grouping multiple values into a single variable.
tuples.fsx
let person = (“John Doe”, 34, “gardener”) printfn “Full tuple: %A” person
let name, age, occupation = person printfn “Name: %s, Age: %d, Occupation: %s” name age occupation
let first, _, _ = person printfn “Just the name: %s” first
We create a tuple variable and demonstrate different ways to access its values.
let person = (“John Doe”, 34, “gardener”)
This creates a tuple containing three values of different types.
let name, age, occupation = person
This destructures the tuple into individual variables.
λ dotnet fsi tuples.fsx Full tuple: (“John Doe”, 34, “gardener”) Name: John Doe, Age: 34, Occupation: gardener Just the name: John Doe
Reference cells provide an alternative way to handle mutable state.
refcells.fsx
let counter = ref 0 printfn “Initial value: %d” !counter
counter := !counter + 1 printfn “After increment: %d” !counter
counter := !counter + 1 printfn “After second increment: %d” !counter
We create a reference cell, then modify and access its contents.
let counter = ref 0
Creates a reference cell initialized with value 0.
!counter
The ! operator dereferences the cell to access its value.
counter := !counter + 1
The := operator updates the reference cell’s value.
λ dotnet fsi refcells.fsx Initial value: 0 After increment: 1 After second increment: 2
F# follows certain naming conventions for variables.
naming.fsx
let camelCaseVariable = “camelCase”
let PascalCaseVariable = “PascalCase”
let snake_case_variable = “snake_case”
let variable with spaces
= “works with backticks”
printfn “%s” camelCaseVariable
printfn “%s” PascalCaseVariable
printfn “%s” snake_case_variable
printfn “%s” variable with spaces
The example shows different naming styles supported in F#.
let variable with spaces
= “works with backticks”
Backticks allow using spaces and other special characters in variable names.
λ dotnet fsi naming.fsx camelCase PascalCase snake_case works with backticks
In this article we have worked with variables in F#.
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.