Getting started
Hello World
// Print to console
printfn "Hello, World!"
// With variable
let name = "World"
printfn "Hello, %s!" name
Basic types
let integer = 42
let float = 3.14
let string = "hello"
let boolean = true
let char = 'a'
Type annotations
let x: int = 42
let y: float = 3.14
let z: string = "text"
Comments
// Single line comment
(* Multi-line
comment *)
/// XML documentation comment
Values and functions
Immutable values
let x = 10 // Immutable by default
let mutable y = 20 // Mutable value
y <- 30 // Reassignment
Function definition
let add x y = x + y
let result = add 5 3 // 8
// With type annotations
let add (x: int) (y: int): int = x + y
Lambda expressions
let add = fun x y -> x + y
let square = fun x -> x * x
// Shorthand
let double = (*) 2
Pipe operator
[1; 2; 3; 4]
|> List.map (fun x -> x * 2)
|> List.filter (fun x -> x > 4)
|> List.sum
// Result: 14
Composition
let double x = x * 2
let square x = x * x
// Forward composition
let doubleThenSquare = double >> square
doubleThenSquare 3 // 36
// Backward composition
let squareThenDouble = double << square
squareThenDouble 3 // 18
Currying
let add x y = x + y
let add5 = add 5 // Partial application
add5 10 // 15
Collections
Lists
let list1 = [1; 2; 3; 4]
let list2 = [1..10] // Range
let list3 = [for i in 1..5 -> i * 2]
// Operations
List.head list1 // 1
List.tail list1 // [2; 3; 4]
List.length list1 // 4
1 :: [2; 3] // Cons operator
[1; 2] @ [3; 4] // Append
Arrays
let arr = [|1; 2; 3; 4|]
let arr2 = [|1..10|]
// Access
arr.[0] // 1
arr.[0] <- 10 // Mutation
// Operations
Array.length arr
Array.map (fun x -> x * 2) arr
Sequences
let seq1 = seq { 1..10 }
let seq2 = seq {
for i in 1..5 do
yield i * 2
}
// Lazy evaluation
Seq.take 3 seq1
Seq.filter (fun x -> x % 2 = 0) seq1
Sets and Maps
// Set
let set1 = Set.ofList [1; 2; 3; 3]
Set.add 4 set1
Set.contains 2 set1
// Map (dictionary)
let map1 = Map.ofList [("a", 1); ("b", 2)]
Map.find "a" map1 // 1
Map.add "c" 3 map1
Pattern matching
Basic matching
let describe x =
match x with
| 0 -> "zero"
| 1 -> "one"
| 2 -> "two"
| _ -> "many"
describe 1 // "one"
List patterns
let rec sum list =
match list with
| [] -> 0
| head :: tail -> head + sum tail
// Multiple elements
match list with
| [] -> "empty"
| [x] -> sprintf "one: %d" x
| [x; y] -> sprintf "two: %d, %d" x y
| x :: y :: rest -> sprintf "many starting with %d, %d" x y
Guards
let classify x =
match x with
| n when n < 0 -> "negative"
| 0 -> "zero"
| n when n > 0 -> "positive"
| _ -> "unknown"
Tuple matching
let point = (3, 4)
match point with
| (0, 0) -> "origin"
| (x, 0) -> sprintf "on x-axis at %d" x
| (0, y) -> sprintf "on y-axis at %d" y
| (x, y) -> sprintf "point at (%d, %d)" x y
Record matching
type Person = { Name: string; Age: int }
let greet person =
match person with
| { Name = "Alice" } -> "Hi Alice!"
| { Age = age } when age < 18 -> "Hello, young one!"
| { Name = name; Age = age } -> sprintf "Hello %s (%d)" name age
Active patterns
// Single case
let (|Even|Odd|) n =
if n % 2 = 0 then Even else Odd
match 4 with
| Even -> "even"
| Odd -> "odd"
// Partial active pattern
let (|Integer|_|) str =
match System.Int32.TryParse(str) with
| (true, int) -> Some int
| _ -> None
match "123" with
| Integer i -> printfn "Parsed: %d" i
| _ -> printfn "Not an integer"
Types
Records
type Person = {
Name: string
Age: int
}
let alice = { Name = "Alice"; Age = 30 }
// Copy and update
let older = { alice with Age = 31 }
Discriminated unions
type Shape =
| Circle of radius: float
| Rectangle of width: float * height: float
| Triangle of base_: float * height: float
let area shape =
match shape with
| Circle r -> System.Math.PI * r * r
| Rectangle (w, h) -> w * h
| Triangle (b, h) -> 0.5 * b * h
Option type
let tryDivide x y =
if y = 0 then None
else Some (x / y)
match tryDivide 10 2 with
| Some result -> printfn "Result: %d" result
| None -> printfn "Cannot divide by zero"
Result type
let tryDivide x y =
if y = 0 then Error "Division by zero"
else Ok (x / y)
match tryDivide 10 0 with
| Ok result -> printfn "Result: %d" result
| Error msg -> printfn "Error: %s" msg
Type aliases
type Name = string
type Age = int
type Person = Name * Age
let alice: Person = ("Alice", 30)
Generic types
type Box<'T> = { Value: 'T }
let intBox = { Value = 42 }
let stringBox = { Value = "hello" }
// Generic function
let wrap x = { Value = x }
Object-oriented features
Classes
type Person(name: string, age: int) =
member this.Name = name
member this.Age = age
member this.Greet() =
printfn "Hello, I'm %s" this.Name
let alice = Person("Alice", 30)
alice.Greet()
Properties
type Person(firstName: string, lastName: string) =
member val FirstName = firstName with get, set
member val LastName = lastName with get, set
member this.FullName =
sprintf "%s %s" this.FirstName this.LastName
let person = Person("John", "Doe")
person.FirstName <- "Jane"
Interfaces
type IShape =
abstract member Area: unit -> float
type Circle(radius: float) =
interface IShape with
member this.Area() = System.Math.PI * radius * radius
type Rectangle(width: float, height: float) =
interface IShape with
member this.Area() = width * height
Inheritance
type Animal(name: string) =
member this.Name = name
abstract member MakeSound: unit -> string
default this.MakeSound() = "Some sound"
type Dog(name: string) =
inherit Animal(name)
override this.MakeSound() = "Woof!"
let dog = Dog("Buddy")
dog.MakeSound() // "Woof!"
Async and parallel
Async workflows
let downloadAsync url =
async {
use client = new System.Net.Http.HttpClient()
let! content = client.GetStringAsync(url) |> Async.AwaitTask
return content
}
// Run async
let content = downloadAsync "https://example.com" |> Async.RunSynchronously
Parallel execution
let tasks = [
async { return 1 + 1 }
async { return 2 + 2 }
async { return 3 + 3 }
]
// Run in parallel
let results = tasks |> Async.Parallel |> Async.RunSynchronously
// [|2; 4; 6|]
Task integration
open System.Threading.Tasks
let taskExample() =
task {
let! result = Task.FromResult(42)
return result * 2
}
// Run task
let result = taskExample().Result // 84
Async error handling
let riskyOperation() =
async {
try
let! result = someAsyncOperation()
return Ok result
with
| ex -> return Error ex.Message
}
Computation expressions
Sequence expressions
let squares = seq {
for i in 1..10 do
yield i * i
}
let filtered = seq {
for i in 1..20 do
if i % 2 = 0 then
yield i
}
List comprehensions
let evens = [ for i in 1..10 do if i % 2 = 0 then yield i ]
// [2; 4; 6; 8; 10]
let pairs = [
for i in 1..3 do
for j in 1..3 do
yield (i, j)
]
Query expressions
open System.Linq
let people = [
{ Name = "Alice"; Age = 30 }
{ Name = "Bob"; Age = 25 }
{ Name = "Charlie"; Age = 35 }
]
let adults =
query {
for person in people do
where (person.Age >= 30)
select person.Name
}
Custom computation builders
type MaybeBuilder() =
member this.Bind(x, f) =
match x with
| Some v -> f v
| None -> None
member this.Return(x) = Some x
let maybe = MaybeBuilder()
let result = maybe {
let! x = Some 10
let! y = Some 20
return x + y
} // Some 30
Modules and namespaces
Modules
module Math =
let add x y = x + y
let multiply x y = x * y
// Usage
Math.add 5 3
Nested modules
module Geometry =
module Circle =
let area radius = System.Math.PI * radius * radius
module Rectangle =
let area width height = width * height
Geometry.Circle.area 5.0
Opening modules
open System
let now = DateTime.Now
let path = IO.Path.Combine("a", "b")
Namespaces
namespace MyApp.Utils
module StringHelpers =
let reverse (s: string) =
s.ToCharArray() |> Array.rev |> System.String
Common operations
List operations
List.map (fun x -> x * 2) [1; 2; 3]
List.filter (fun x -> x > 2) [1; 2; 3; 4]
List.fold (+) 0 [1; 2; 3; 4] // 10
List.reduce (+) [1; 2; 3; 4] // 10
List.collect (fun x -> [x; x*2]) [1; 2]
String operations
"hello".Length // 5
"hello".ToUpper() // "HELLO"
"hello".[0] // 'h'
"hello".Substring(1, 3) // "ell"
String.concat ", " ["a"; "b"]
Option operations
Option.map (fun x -> x * 2) (Some 5) // Some 10
Option.bind tryDivide (Some 10)
Option.defaultValue 0 None // 0
Option.isSome (Some 5) // true
Option.isNone None // true
Result operations
Result.map (fun x -> x * 2) (Ok 5)
Result.bind operation (Ok 10)
Result.defaultValue 0 (Error "fail")
// Combine results
let (>>=) result f = Result.bind f result
Type providers
JSON type provider
#r "nuget: FSharp.Data"
open FSharp.Data
type Weather = JsonProvider<"sample.json">
let data = Weather.Load("weather.json")
printfn "Temperature: %f" data.Temperature
CSV type provider
type Stocks = CsvProvider<"stocks.csv">
let data = Stocks.Load("data.csv")
for row in data.Rows do
printfn "%s: %f" row.Company row.Price
SQL type provider
#r "nuget: FSharp.Data.SqlClient"
open FSharp.Data
[<Literal>]
let connectionString = "..."
type GetCustomers = SqlCommandProvider<"
SELECT * FROM Customers
", connectionString>
let customers = GetCustomers.Create(connectionString).Execute()
Units of measure
Defining units
[<Measure>] type m // meters
[<Measure>] type s // seconds
[<Measure>] type kg // kilograms
let distance = 10.0<m>
let time = 2.0<s>
let speed = distance / time // float<m/s>
Dimensionless conversion
[<Measure>] type USD
[<Measure>] type EUR
let dollars = 100.0<USD>
let rate = 0.85<EUR/USD>
let euros = dollars * rate // float<EUR>
// Remove units
let value = float dollars // 100.0
Generic units
let square (x: float<'u>) = x * x // float<'u^2>
let cube (x: float<'u>) = x * x * x // float<'u^3>
square 5.0<m> // 25.0<m^2>
Error handling
Try-with expressions
try
let result = 10 / 0
printfn "%d" result
with
| :? System.DivideByZeroException ->
printfn "Cannot divide by zero"
| ex ->
printfn "Error: %s" ex.Message
Try-finally
try
use file = System.IO.File.OpenRead("file.txt")
// Use file
()
finally
printfn "Cleanup"
Railway-oriented programming
let bind switchFunction =
fun result ->
match result with
| Ok value -> switchFunction value
| Error e -> Error e
let (>>=) result switchFunction =
bind switchFunction result
// Chain operations
validate input
>>= process
>>= save
Scripting and REPL
FSI (F# Interactive)
// Load script
#load "MyScript.fsx"
// Reference assembly
#r "nuget: Newtonsoft.Json"
// Include directory
#I "/path/to/libs"
// Time execution
#time "on"
Script directives
#!/usr/bin/env dotnet fsi
#r "nuget: FSharp.Data"
open FSharp.Data
printfn "Hello from script!"
Testing
Expecto
open Expecto
let tests =
testList "Math tests" [
test "Addition works" {
let result = 2 + 2
Expect.equal result 4 "Should be 4"
}
testCase "Division works" <| fun () ->
Expect.equal (10 / 2) 5 "Should be 5"
]
[<EntryPoint>]
let main args =
runTestsWithArgs defaultConfig args tests
Property-based testing
#r "nuget: FsCheck"
open FsCheck
let reverseTest (list: int list) =
List.rev (List.rev list) = list
Check.Quick reverseTest
// Custom properties
[<Property>]
let ``Addition is commutative`` (a: int) (b: int) =
a + b = b + a
Common patterns
Recursive functions
let rec factorial n =
if n <= 1 then 1
else n * factorial (n - 1)
// Tail-recursive
let factorial n =
let rec loop acc n =
if n <= 1 then acc
else loop (acc * n) (n - 1)
loop 1 n
Memoization
let memoize f =
let cache = System.Collections.Generic.Dictionary<_,_>()
fun x ->
match cache.TryGetValue(x) with
| true, v -> v
| false, _ ->
let v = f x
cache.[x] <- v
v
let rec fib = memoize (fun n ->
if n <= 1 then n
else fib(n-1) + fib(n-2))
Discriminated union helpers
type Result<'T,'E> =
| Ok of 'T
| Error of 'E
module Result =
let map f result =
match result with
| Ok v -> Ok (f v)
| Error e -> Error e
let bind f result =
match result with
| Ok v -> f v
| Error e -> Error e
Gotchas
Indentation matters
// ✅ Correct
let x =
let y = 5
y + 10
// ❌ Wrong - indentation error
let x =
let y = 5
y + 10
Mutable vs immutable
let x = 10
x <- 20 // ⚠️ Error: x is immutable
let mutable y = 10
y <- 20 // ✅ OK
Equality operators
let x = 5 = 5 // ✅ Boolean comparison: true
let x = (5 = 5) // Same as above
let assign x = x // ⚠️ Function, not comparison
List concatenation
[1; 2] @ [3; 4] // ✅ List concatenation
[1; 2] + [3; 4] // ⚠️ Error: + not defined for lists
Function application
printfn "Value: %d" 5 // ✅ Space separates arguments
printfn("Value: %d", 5) // ⚠️ Works but not idiomatic
Semicolons in lists
let list = [1; 2; 3] // ✅ Semicolons separate items
let list = [1, 2, 3] // ⚠️ Creates single tuple item
Type inference limitations
let add x y = x + y // ⚠️ Error: type not known
let add (x: int) y = x + y // ✅ One annotation helps
let add x y : int = x + y // ✅ Return type annotation
Also see
- F# Language Reference (microsoft.com)
- F# for Fun and Profit (fsharpforfunandprofit.com)
- F# Software Foundation (fsharp.org)
- Try F# (try.fsharp.org)
- F# Cheat Sheet (dungpa.github.io)
- Awesome F# (github.com)