Professional Documents
Culture Documents
1
Sequences Tuples and Records let rec depth = function
A sequence is a logical series of elements of the same type. A tuple is a grouping of unnamed but ordered values, possibly | Node(l, _, r) -> 1 + max (depth l) (depth r)
Individual sequence elements are computed only as required, of different types: | Leaf -> 0
so a sequence can provide better performance than a list in // Tuple construction
situations in which not all the elements are used. let x = (1, "Hello") F# Core has a few built-in discriminated unions for error
// Sequences can use yield and contain subsequences handling, e.g., Option and Choice.
let seq1 = // Triple let optionPatternMatch input =
seq { let y = ("one", "two", "three") match input with
// "yield" adds one element | Some i -> printfn "input is an int=%d" i
yield 1 // Tuple deconstruction / pattern | None -> printfn "input is missing"
yield 2 let (a, b) = x
// "yield!" adds a whole subsequence Single-case discriminated unions are often used to create
The first and second elements of a tuple can be obtained using
yield! [5..10] type-safe abstractions with pattern matching support:
fst, snd, or pattern matching:
} type OrderId = Order of string
let c = fst (1, 2)
let d = snd (1, 2)
Higher-order functions on collections // Create a DU value
let print tuple = let orderId = Order "12"
The same list [ 1; 3; 5; 7; 9 ] or array [| 1; 3; 5; 7; 9
|] can be generated in various ways. match tuple with
| (a, b) -> printfn "Pair %A %A" a b // Use pattern matching to deconstruct single-case DU
Using range operator .. let (Order id) = orderId
let xs = [ 1..2..9 ] Records represent simple aggregates of named values,
Using list or array comprehensions
optionally with members: Exceptions
// Declare a record type The failwith function throws an exception of type Exception.
let ys = [| for i in 0..4 -> 2 * i + 1 |] type Person = { Name : string; Age : int }
let divideFailwith x y =
Using init function if y = 0 then
// Create a value via record expression
let zs = List.init 5 (fun i -> 2 * i + 1) failwith "Divisor cannot be zero."
let paul = { Name = "Paul"; Age = 28 }
else x / y
Lists and arrays have comprehensive sets of higher-order
functions for manipulation. // Copy and update record expression
let paulsTwin = { paul with Name = "Jim" } Exception handling is done via try/with expressions.
fold starts from the left of the list (or array) and
foldBack goes in the opposite direction let divide x y =
Records can be augmented with properties and methods: try
let xs = Array.fold (fun str n -> type Person with Some (x / y)
sprintf "%s,%i" str n) "" [| 0..9 |] member x.Info = (x.Name, x.Age) with :? System.DivideByZeroException ->
reduce doesnt require an initial accumulator printfn "Division by zero!"
None
let last xs = List.reduce (fun acc x -> x) xs Records are essentially sealed classes with extra topping:
default immutability, structural equality, and pattern
map transforms every element of the list (or array) matching support. The try/finally expression enables you to execute clean-up
let isPaul person = code even if a block of code throws an exception. Heres an
let ys = Array.map (fun x -> x * x) [| 0..9 |]
match person with example which also defines custom exceptions.
iterate through a list and produce side effects | { Name = "Paul" } -> true exception InnerError of string
let _ = List.iter (printfn "%i") [ 0..9 ] | _ -> false exception OuterError of string
All these operations are also available for sequences. The let handleErrors x y =
added benefits of sequences are laziness and uniform treatment Discriminated Unions try
of all collections implementing IEnumerable<T>. Discriminated unions (DU) provide support for values that try
let zs = can be one of a number of named cases, each possibly with if x = y then raise (InnerError("inner"))
seq { different values and types. else raise (OuterError("outer"))
for i in 0..9 do type Tree<T> = with InnerError(str) ->
printfn "Adding %d" i | Node of Tree<T> * T * Tree<T> printfn "Error1 %s" str
yield i | Leaf finally
} printfn "Always print this."
2
Classes and Inheritance Interfaces and Object Expressions Parameterized active patterns:
This example is a basic class with (1) local let bindings, (2) Declare IVector interface and implement it in Vector. let (|DivisibleBy|_|) by n =
properties, (3) methods, and (4) static members. type IVector = if n % by = 0 then Some DivisibleBy else None
abstract Scale : float -> IVector
type Vector(x : float, y : float) = let fizzBuzz = function
let mag = sqrt(x * x + y * y) // (1) type Vector(x, y) = | DivisibleBy 3 & DivisibleBy 5 -> "FizzBuzz"
member this.X = x // (2) interface IVector with | DivisibleBy 3 -> "Fizz"
member this.Y = y member __.Scale(s) = | DivisibleBy 5 -> "Buzz"
member this.Mag = mag Vector(x * s, y * s) :> IVector | i -> string i
member this.Scale(s) = // (3) member __.X = x
Vector(x * s, y * s) member __.Y = y Partial active patterns share the syntax of parameterized
static member (+) (a : Vector, b : Vector) = // (4) patterns but their active recognizers accept only one argument.
Vector(a.X + b.X, a.Y + b.Y)
Another way of implementing interfaces is to use object Compiler Directives
expressions.
Load another F# source file into FSI.
Call a base class from a derived one. type ICustomer = #load "../lib/StringParsing.fs"
type Animal() = abstract Name : string
member __.Rest() = () abstract Age : int
Reference a .NET assembly (/ symbol is recommended for
let createCustomer name age = Mono compatibility).
type Dog() =
inherit Animal() { new ICustomer with #r "../lib/FSharp.Markdown.dll"
member __.Run() = member __.Name = name
base.Rest() member __.Age = age } Include a directory in assembly search paths.
#I "../lib"
Upcasting is denoted by :> operator. Active Patterns #r "FSharp.Markdown.dll"
Complete active patterns:
let dog = Dog() Other important directives are conditional execution in FSI
let (|Even|Odd|) i =
let animal = dog :> Animal (INTERACTIVE) and querying current directory
if i % 2 = 0 then Even else Odd
(__SOURCE_DIRECTORY__).
Dynamic downcasting (:?>) might throw an let testNumber i = #if INTERACTIVE
InvalidCastException if the cast doesnt succeed at runtime. match i with let path = __SOURCE_DIRECTORY__ + "../lib"
| Even -> printfn "%d is even" i #else
let shouldBeADog = animal :?> Dog | Odd -> printfn "%d is odd" i let path = "../../../lib"
#endif