Basics
Comments Block comments are placed between (* This is block comment *) // And this is line comment XML doc comments come after /// The `let` keyword defines an (immutable) value let result = 1 + 1 = 2 |
|
Strings F# /// Create a string using string concatenation let hello = "Hello" + " World" Use verbatim strings preceded by let verbatimXml = @"<book title=""Paradise Lost"">" We don't even have to escape let tripleXml = """<book title="Paradise Lost">""" Backslash strings indent string contents by stripping leading spaces. let poem = "The lesser world was daubed\n\ By a colorist of modest skill\n\ A master limned you in the finest inks\n\ And with a fresh-cut quill." |
|
Basic Types and Literals Most numeric types have associated suffixes, e.g., let b, i, l = 86uy, 86, 86L // [fsi:val b : byte = 86uy] // [fsi:val i : int = 86] // [fsi:val l : int64 = 86L] Other common examples are 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] See Literals (MSDN) for complete reference. |
|
Tuples and Records A tuple is a grouping of unnamed but ordered values, possibly of different types: // Tuple construction let x = (1, "Hello") // Triple let y = ("one", "two", "three") // Tuple deconstruction / pattern let (a', b') = x The first and second elements of a tuple can be obtained using 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: // Declare a record type type Person = { Name : string; Age : int } // Create a value via record expression let paul = { Name = "Paul"; Age = 28 } // 'Copy and update' record expression let paulsTwin = { paul with Name = "Jim" } Records can be augmented with properties and methods: type Person with member x.Info = (x.Name, x.Age) 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 |
|
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 let rec depth = function | Node(l, _, r) -> 1 + max (depth l) (depth r) | Leaf -> 0 They allow to wrap a type using Single case union types (Designing with types: Single case union types: type CustomerId = CustomerId of int let custId = CustomerId 1 // deconstruct in the param let printCustomerId (CustomerId customerIdInt) = printfn "The CustomerId is %i" customerIdInt // or deconstruct explicitly through let statement let printCustomerId2 custId = let (CustomerId customerIdInt) = custId // deconstruct here printfn "The CustomerId is %i" customerIdInt F# Core has a few built-in discriminated unions for error handling, e.g., Option and Choice. 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: type OrderId = Order of string // Create a DU value let orderId = Order "12" // Use pattern matching to deconstruct single-case DU let (Order id) = orderId |
|
Exceptions Throw an exception using a built-in keyword:
let divideFailwith x y = if y = 0 then failwith "Divisor cannot be zero." else x / y Exception handling is done via let divide x y = try Some (x / y) with :? System.DivideByZeroException -> printfn "Division by zero!" None The 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." Raising an exception is done using the exception MyError of string raise (MyError "my error") |
Functions
Definition The 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)) Infix operator declaration: let (**) x n = Math.Pow(x, n) |
|
Pipe and composition operators Pipe operator let ``square, negate, then print`` x = x |> square |> negate |> print This operator is essential in assisting the F# type checker by providing type information before use: let sumOfLengths (xs : string []) = xs |> Array.map (fun s -> s.Length) |> Array.sum Composition operator let squareNegateThenPrint' = square >> negate >> print |
|
Recursive functions The 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 let rec even x = if x = 0 then true else odd (x - 1) and odd x = if x = 1 then true else even (x - 1) |
Collections
Lists A list is an immutable collection of elements of the same type. // Lists use square brackets and `;` delimiter let list1 = [ "a"; "b" ] // :: (cons operator) is prepending let list2 = "c" :: list1 // @ is concat let list3 = list1 @ list2 // Recursion on list using (::) operator let rec sum list = match list with | [] -> 0 | x :: xs -> x + sum xs |
|
Arrays Arrays are fixed-size, zero-based, mutable collections of consecutive data elements. // Arrays use square brackets with bar let array1 = [| "a"; "b" |] // Indexed access using dot let first = array1.[0] |
|
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 let seq1 = 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
let xs = [ 1..2..9 ]
let ys = [| for i in 0..4 -> 2 * i + 1 |]
let zs = List.init 5 (fun i -> 2 * i + 1) Lists and arrays have comprehensive sets of higher-order functions for manipulation.
let xs' = Array.fold (fun str n -> sprintf "%s,%i" str n) "" [| 0..9 |]
let last xs = List.reduce (fun acc x -> x) xs
let ys' = Array.map (fun x -> x * x) [| 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 let zs' = seq { for i in 0..9 do printfn "Adding %d" i yield i } |
Pattern Matching
Pattern Matching Pattern matching is often facilitated through 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 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 or implicitly via /// Similar to `fib`; using `function` for pattern matching let rec fib' = function | 0 -> 0 | 1 -> 1 | n -> fib' (n - 1) + fib' (n - 2) For more complete reference visit Pattern Matching (MSDN). |
|
Active Patterns 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 Parameterized 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 Partial active patterns share the syntax of parameterized patterns but their active recognizers accept only one argument. |
Scripting
Compiler Directives Load another F# source file into FSI. #load "../lib/StringParsing.fs" Reference a .NET assembly ( #r "../lib/FSharp.Markdown.dll" Include a directory in assembly search paths. #I "../lib" #r "FSharp.Markdown.dll" Other important directives are conditional execution in FSI ( #if INTERACTIVE let path = __SOURCE_DIRECTORY__ + "../lib" #else let path = "../../../lib" #endif |