From b1210acceecf908a5ebb0ccd557e713cb582baf1 Mon Sep 17 00:00:00 2001 From: Charles Roddie Date: Wed, 18 Oct 2023 22:11:28 +0100 Subject: [PATCH] replace tabs with spaces --- docs/fsharp-cheatsheet.md | 306 +++++++++++++++++++------------------- 1 file changed, 153 insertions(+), 153 deletions(-) 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.