diff --git a/docs/fsharp-cheatsheet.md b/docs/fsharp-cheatsheet.md
index fd04f90..548b5b8 100644
--- a/docs/fsharp-cheatsheet.md
+++ b/docs/fsharp-cheatsheet.md
@@ -24,7 +24,7 @@ Contents
--------
Block comments are placed between `(*` and `*)`. Line comments start from `//` and continue until the end of the line.
- (* This is block comment *)
+ (* This is block comment *)
// And this is line comment
@@ -70,10 +70,10 @@ Other common examples are `F` or `f` for 32-bit floating-point numbers, `M` or `
let s, f, d, bi = 4.14F, 4.14, 0.7833M, 9999I
- // [fsi:val s : float32 = 4.14f]
- // [fsi:val f : float = 4.14]
- // [fsi:val d : decimal = 0.7833M]
- // [fsi:val bi : System.Numerics.BigInteger = 9999]
+ // [fsi:val s : float32 = 4.14f]
+ // [fsi:val f : float = 4.14]
+ // [fsi:val d : decimal = 0.7833M]
+ // [fsi:val bi : System.Numerics.BigInteger = 9999]
See [Literals (MS Learn)](https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/literals) for complete reference.
@@ -81,77 +81,77 @@ See [Literals (MS Learn)](https://learn.microsoft.com/en-us/dotnet/fsharp/langua
---------
The `let` keyword also defines named functions.
- let negate x = x * -1
- let square x = x * x
- let print x = printfn "The number is: %d" x
+ let negate x = x * -1
+ let square x = x * x
+ let print x = printfn "The number is: %d" x
let squareNegateThenPrint x =
- print (negate (square x))
+ print (negate (square x))
### Pipe and composition operators
Pipe operator `|>` is used to chain functions and arguments together. Double-backtick identifiers are handy to improve readability especially in unit testing:
- let ``square, negate, then print`` x =
- x |> square |> negate |> print
+ let ``square, negate, then print`` x =
+ x |> square |> negate |> print
This operator can assist the F# type checker by providing type information before use:
let sumOfLengths (xs : string []) =
- xs
- |> Array.map (fun s -> s.Length)
- |> Array.sum
+ xs
+ |> Array.map (fun s -> s.Length)
+ |> Array.sum
Composition operator `>>` is used to compose functions:
- let squareNegateThenPrint' =
- square >> negate >> print
+ let squareNegateThenPrint' =
+ square >> negate >> print
### Recursive functions
The `rec` keyword is used together with the `let` keyword to define a recursive function:
- let rec fact x =
- if x < 1 then 1
- else x * fact (x - 1)
+ let rec fact x =
+ if x < 1 then 1
+ else x * fact (x - 1)
*Mutually recursive* functions (those functions which call each other) are indicated by `and` keyword:
- let rec even x =
- if x = 0 then true
- else odd (x - 1)
+ let rec even x =
+ if x = 0 then true
+ else odd (x - 1)
- and odd x =
- if x = 0 then false
- else even (x - 1)
+ and odd x =
+ if x = 0 then false
+ else even (x - 1)
Pattern Matching
----------------
Pattern matching is often facilitated through `match` keyword.
- let rec fib n =
- match n with
- | 0 -> 0
- | 1 -> 1
- | _ -> fib (n - 1) + fib (n - 2)
+ let rec fib n =
+ match n with
+ | 0 -> 0
+ | 1 -> 1
+ | _ -> fib (n - 1) + fib (n - 2)
In order to match sophisticated inputs, one can use `when` to create filters or guards on patterns:
- let sign x =
- match x with
- | 0 -> 0
- | x when x < 0 -> -1
- | x -> 1
+ let sign x =
+ match x with
+ | 0 -> 0
+ | x when x < 0 -> -1
+ | x -> 1
Pattern matching can be done directly on arguments:
- let fst' (x, _) = x
+ let fst' (x, _) = x
or implicitly via `function` keyword:
/// Similar to `fib`; using `function` for pattern matching
- let rec fib' = function
- | 0 -> 0
- | 1 -> 1
- | n -> fib' (n - 1) + fib' (n - 2)
+ let rec fib' = function
+ | 0 -> 0
+ | 1 -> 1
+ | n -> fib' (n - 1) + fib' (n - 2)
For more complete reference visit [Pattern Matching (MS Learn)](https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/pattern-matching).
@@ -177,7 +177,7 @@ A *list* is an immutable collection of elements of the same type.
### Arrays
*Arrays* are fixed-size, zero-based, mutable collections of consecutive data elements.
- // Arrays use square brackets with bar
+ // Arrays use square brackets with bar
let array1 = [| "a"; "b" |]
// Indexed access using dot
let first = array1.[0]
@@ -185,16 +185,16 @@ A *list* is an immutable collection of elements of the same type.
### Sequences
A *sequence* is a logical series of elements of the same type. Individual sequence elements are computed only as required, so a sequence can provide better performance than a list in situations in which not all the elements are used.
- // Sequences can use yield and contain subsequences
+ // Sequences can use yield and contain subsequences
let seq1 =
- seq {
- // "yield" adds one element
- yield 1
- yield 2
-
- // "yield!" adds a whole subsequence
- yield! [5..10]
- }
+ seq {
+ // "yield" adds one element
+ yield 1
+ yield 2
+
+ // "yield!" adds a whole subsequence
+ yield! [5..10]
+ }
### Higher-order functions on collections
The same list `[ 1; 3; 5; 7; 9 ]` or array `[| 1; 3; 5; 7; 9 |]` can be generated in various ways.
@@ -216,8 +216,8 @@ Lists and arrays have comprehensive sets of higher-order functions for manipulat
- `fold` starts from the left of the list (or array) and `foldBack` goes in the opposite direction
let xs' =
- Array.fold (fun str n ->
- sprintf "%s,%i" str n) "" [| 0..9 |]
+ Array.fold (fun str n ->
+ sprintf "%s,%i" str n) "" [| 0..9 |]
- `reduce` doesn't require an initial accumulator
@@ -225,19 +225,19 @@ Lists and arrays have comprehensive sets of higher-order functions for manipulat
- `map` transforms every element of the list (or array)
- let ys' = Array.map (fun x -> x * x) [| 0..9 |]
+ let ys' = Array.map (fun x -> x * x) [| 0..9 |]
- `iter`ate through a list and produce side effects
-
- let _ = List.iter (printfn "%i") [ 0..9 ]
+
+ let _ = List.iter (printfn "%i") [ 0..9 ]
All these operations are also available for sequences. The added benefits of sequences are laziness and uniform treatment of all collections implementing `IEnumerable<'T>`.
- let zs' =
- seq {
- for i in 0..9 do
- printfn "Adding %d" i
- yield i
+ let zs' =
+ seq {
+ for i in 0..9 do
+ printfn "Adding %d" i
+ yield i
}
Tuples and Records
@@ -248,19 +248,19 @@ A *tuple* is a grouping of unnamed but ordered values, possibly of different typ
let x = (1, "Hello")
// Triple
- let y = ("one", "two", "three")
+ let y = ("one", "two", "three")
// Tuple deconstruction / pattern
let (a', b') = x
The first and second elements of a tuple can be obtained using `fst`, `snd`, or pattern matching:
- let c' = fst (1, 2)
- let d' = snd (1, 2)
-
- let print' tuple =
- match tuple with
- | (a, b) -> printfn "Pair %A %A" a b
+ let c' = fst (1, 2)
+ let d' = snd (1, 2)
+
+ let print' tuple =
+ match tuple with
+ | (a, b) -> printfn "Pair %A %A" a b
*Records* represent simple aggregates of named values, optionally with members:
@@ -271,7 +271,7 @@ The first and second elements of a tuple can be obtained using `fst`, `snd`, or
let paul = { Name = "Paul"; Age = 28 }
// 'Copy and update' record expression
- let paulsTwin = { paul with Name = "Jim" }
+ let paulsTwin = { paul with Name = "Jim" }
Records can be augmented with properties and methods:
@@ -281,29 +281,29 @@ Records can be augmented with properties and methods:
Records are essentially sealed classes with extra topping: default immutability, structural equality, and pattern matching support.
let isPaul person =
- match person with
- | { Name = "Paul" } -> true
- | _ -> false
+ match person with
+ | { Name = "Paul" } -> true
+ | _ -> false
Discriminated Unions
--------------------
*Discriminated unions* (DU) provide support for values that can be one of a number of named cases, each possibly with different values and types.
type Tree<'T> =
- | Node of Tree<'T> * 'T * Tree<'T>
- | Leaf
+ | Node of Tree<'T> * 'T * Tree<'T>
+ | Leaf
let rec depth = function
- | Node(l, _, r) -> 1 + max (depth l) (depth r)
- | Leaf -> 0
+ | Node(l, _, r) -> 1 + max (depth l) (depth r)
+ | Leaf -> 0
F# Core has built-in discriminated unions for error handling, e.g., [`option`](https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/options) and [`Result`](https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/results).
- let optionPatternMatch input =
- match input with
- | Some i -> printfn "input is an int=%d" i
- | None -> printfn "input is missing"
+ let optionPatternMatch input =
+ match input with
+ | Some i -> printfn "input is an int=%d" i
+ | None -> printfn "input is missing"
Single-case discriminated unions are often used to create type-safe abstractions with pattern matching support:
@@ -319,92 +319,92 @@ Single-case discriminated unions are often used to create type-safe abstractions
----------
The `failwith` function throws an exception of type `Exception`.
- let divideFailwith x y =
- if y = 0 then
- failwith "Divisor cannot be zero."
- else x / y
+ let divideFailwith x y =
+ if y = 0 then
+ failwith "Divisor cannot be zero."
+ else x / y
Exception handling is done via `try/with` expressions.
- let divide x y =
- try
- Some (x / y)
- with :? System.DivideByZeroException ->
- printfn "Division by zero!"
- None
-
+ let divide x y =
+ try
+ Some (x / y)
+ with :? System.DivideByZeroException ->
+ printfn "Division by zero!"
+ None
+
The `try/finally` expression enables you to execute clean-up code even if a block of code throws an exception. Here's an example which also defines custom exceptions.
- exception InnerError of string
- exception OuterError of string
-
- let handleErrors x y =
- try
- try
- if x = y then raise (InnerError("inner"))
- else raise (OuterError("outer"))
- with InnerError(str) ->
- printfn "Error1 %s" str
- finally
- printfn "Always print this."
+ exception InnerError of string
+ exception OuterError of string
+
+ let handleErrors x y =
+ try
+ try
+ if x = y then raise (InnerError("inner"))
+ else raise (OuterError("outer"))
+ with InnerError(str) ->
+ printfn "Error1 %s" str
+ finally
+ printfn "Always print this."
Classes and Inheritance
-----------------------
This example is a basic class with (1) local let bindings, (2) properties, (3) methods, and (4) static members.
- type Vector(x : float, y : float) =
- let mag = sqrt(x * x + y * y) // (1)
- member _.X = x // (2)
- member _.Y = y
- member _.Mag = mag
- member _.Scale(s) = // (3)
- Vector(x * s, y * s)
- static member (+) (a : Vector, b : Vector) = // (4)
- Vector(a.X + b.X, a.Y + b.Y)
+ type Vector(x : float, y : float) =
+ let mag = sqrt(x * x + y * y) // (1)
+ member _.X = x // (2)
+ member _.Y = y
+ member _.Mag = mag
+ member _.Scale(s) = // (3)
+ Vector(x * s, y * s)
+ static member (+) (a : Vector, b : Vector) = // (4)
+ Vector(a.X + b.X, a.Y + b.Y)
Call a base class from a derived one.
- type Animal() =
- member _.Rest() = ()
-
- type Dog() =
- inherit Animal()
- member _.Run() =
- base.Rest()
+ type Animal() =
+ member _.Rest() = ()
+
+ type Dog() =
+ inherit Animal()
+ member _.Run() =
+ base.Rest()
*Upcasting* is denoted by `:>` operator.
- let dog = Dog()
- let animal = dog :> Animal
+ let dog = Dog()
+ let animal = dog :> Animal
*Dynamic downcasting* (`:?>`) might throw an `InvalidCastException` if the cast doesn't succeed at runtime.
- let shouldBeADog = animal :?> Dog
+ let shouldBeADog = animal :?> Dog
Interfaces and Object Expressions
---------------------------------
Declare `IVector` interface and implement it in `Vector'`.
- type IVector =
- abstract Scale : float -> IVector
-
- type Vector'(x, y) =
- interface IVector with
- member __.Scale(s) =
- Vector'(x * s, y * s) :> IVector
- member _.X = x
- member _.Y = y
+ type IVector =
+ abstract Scale : float -> IVector
+
+ type Vector'(x, y) =
+ interface IVector with
+ member __.Scale(s) =
+ Vector'(x * s, y * s) :> IVector
+ member _.X = x
+ member _.Y = y
Another way of implementing interfaces is to use *object expressions*.
- type ICustomer =
- abstract Name : string
- abstract Age : int
-
- let createCustomer name age =
- { new ICustomer with
- member __.Name = name
- member __.Age = age }
+ type ICustomer =
+ abstract Name : string
+ abstract Age : int
+
+ let createCustomer name age =
+ { new ICustomer with
+ member __.Name = name
+ member __.Age = age }
Active Patterns
---------------
@@ -435,13 +435,13 @@ Another way of implementing interfaces is to use *object expressions*.
*Complete active patterns*:
- let (|Even|Odd|) i =
- if i % 2 = 0 then Even else Odd
-
- let testNumber i =
- match i with
- | Even -> printfn "%d is even" i
- | Odd -> printfn "%d is odd" i
+ let (|Even|Odd|) i =
+ if i % 2 = 0 then Even else Odd
+
+ let testNumber i =
+ match i with
+ | Even -> printfn "%d is even" i
+ | Odd -> printfn "%d is odd" i
let (|Phone|Email|) (s:string) =
if s.Contains '@' then Email $"Email: {s}" else Phone $"Phone: {s}"
@@ -452,14 +452,14 @@ Another way of implementing interfaces is to use *object expressions*.
*Partial active patterns*:
- let (|DivisibleBy|_|) by n =
- if n % by = 0 then Some DivisibleBy else None
-
- let fizzBuzz = function
- | DivisibleBy 3 & DivisibleBy 5 -> "FizzBuzz"
- | DivisibleBy 3 -> "Fizz"
- | DivisibleBy 5 -> "Buzz"
- | i -> string i
+ let (|DivisibleBy|_|) by n =
+ if n % by = 0 then Some DivisibleBy else None
+
+ let fizzBuzz = function
+ | DivisibleBy 3 & DivisibleBy 5 -> "FizzBuzz"
+ | DivisibleBy 3 -> "Fizz"
+ | DivisibleBy 5 -> "Buzz"
+ | i -> string i
*Partial active patterns* share the syntax of parameterized patterns but their active recognizers accept only one argument.