FSharp

From ScienceZero
Jump to: navigation, search
F# Software Foundation

F# Software Foundation

Reference material

Online F# compilers


Values and variables

When you have significant amount of mutable state you also have code that explains how to do something.
What you want is code that explains what to do and let the compiler figure out how.

Values

let a = 2
This is a value and it is constant within the scope of the value.
You can safely pass values to other threads, computers or to the other side of the world.
Always use values instead of variables when possible.

Mutable values

let mutable b = 2
This is a variable and it can be updated with a new value.
b <- b + 1
now b = 3

Mutable reference cells

The ref type allows you to store mutable data on the heap to avoid two problems with mutable values, that they cannot be returned from functions except as copies and they cannot be captured in inner-functions.

let a = ref 0   // Create a reference cell named a with the value 0
a := 3          // Set the reference cell to 3
let b = !a      // Set b to the value of reference cell a
incr a          // Increment the value of a reference cell a by 1
decr a          // Decrement the value of a reference cell a by 1
let c = a       // This will not create a new reference cell with the value of a, it will create an alias to the same cell
let c = ref !a  // This will create a new reference cell with the same value as a

The F# 4.0 compiler will automatically convert mutable types to reference cells.

Types

The compiler will usually infer types automatically.
let a = 7        - Signed 32 bit integer is the default for numbers without decimal point or any suffix.
let b = "Hello"  - String
let c = 3.1415   - 64 bit floating-point is the default for numbers with a decimal point.
In some cases it is not possible to infer the type or you want to make a point out of it.
let (a : uint64) = xPos
let (point : int*int) = 10,20 // Tuple

Primitive types

The fundamental primitive types that are used in the F# language

Type   Suffix  .NET Type     Size  Range
byte       uy  System.Byte      8  0 to 255 
sbyte       y  System.SByte     8  −128 to 127 
int16       s  System.Int16    16  −32 768 to 32 767 
uint16     us  System.UInt16   16  0 to 65 535 
int, int32     System.Int32    32  −231 to 231 − 1 
uint32      u  System.UInt32   32  0 to 232 − 1 
int64       L  System.Int64    64  −263 to 263 − 1
uint64     UL  System.UInt64   64  0 to 264 − 1 
float      LF  System.Double   64  Floating-point IEEE 64, approximately 15 significant digits. Suffix is used on hex values.              
float32     f  System.Single   32  Floating-point IEEE 32, approximately 7 significant digits. Suffix is used on hex values.              
decimal     M  System.Decimal 128  A fixed-precision floating-point type with precisely 28 digits of precision.
bool           System.Boolean   8  True/false
unit                           32  Indicates the absence of an actual value. This type has only one formal value, which is denoted ().
nativeint                   32/64  A native pointer as a signed integer.
unativeint                  32/64  A native pointer as an unsigned integer.
let a = 0x12345678    Hexadecimal literal constant   32 bit int
let b = 0o12345s      Octal literal constant         16 bit signed int
let c = 0b11110000uy  Binary literal constant         8 bit unsigned byte
let d = 0x00000000LF  Hexadecimal encoding of float  64 bit floating-point
let e = true          Boolean

Option

The option type has two possible values Some('a) or None where 'a can be any type. Sometimes a function does not have a valid value to return, it can then return the option type with value None. The Option type is a discriminated union.

let safeDiv x y = match y with
                  | 0 -> None             // Rules are evaluated in top down order so no other rule can match 0 after this
                  | _ -> Some(x / y)      // _ is a wildcard so this rule matches everything not matched by earlier rules

match safeDiv 10 2 with
| None    -> printfn "Division by zero"
| Some(n) -> printfn "Result: %A" n       // The result of the division is captured in the value n

Option module

Option.bind     - function None -> None | Some x -> f x
Option.map      - function None -> None | Some x -> Some (f x)
Option.fold     - function None -> s | Some x -> f s x
Option.foldBack - function None -> s | Some x -> f x s

String

The string type can be treated as a seq< char >

let a = "This is a string"
let b = a.[2]               // a = "i"
let c = a.[3..8]            // a = "s is a"
let d = a.[5..]             // a = "is a string"

Literal strings

let a = "Line1\nLine2" Normal string with escape characters
let a = @"__/\__"      Verbatim string
let a = """A "B" C"""  String with quotes
let a = "ABCD"B        Byte array
let a = 'c'            Character
let a = "This is \     Multiline string definition that skips whitespace after \
         one long \
         sentence"

Escape characters
\n         Newline      char 10
\r         Return       char 13
\t         Tab          char 9
\b         Backspace    char 8
\\         Backslash    '\'
\NNN       Trigraph     char NNN
\uNNNN     Unicode      char 0xNNNN
\UNNNNNNNN Long unicode char 0xNNNNNNNN

bigint

Type   Suffix  .NET Type        Range
bigint      I  System.Numerics  Arbitrary-sized integer

let a = bigint 2  // Creating a value using conversion
let b = 2I        // Creating a value using suffix

Complex

Type   Suffix  .NET Type        Range
Complex        System.Numerics  float*float

open System.Numerics
let (c : Complex) = new Complex(1.0, 2.0)

Tuple

A tuple is an ordered collection of unnamed types. It is a simple way of returning compound types from a function.

Creating a value that the compiler will infere as type int*int
let point = (100,150)
The tuple can be decomposed by
let x,y = point
or matched against
match point with
| 0,0            -> "Origin"
| x,_ when x < 0 -> "Left side"
| 100,150        -> "Bullseye"
| _              -> "Somewhere else"

Discriminated unions

The discriminated union is a type that can only be one of a set of possible values. Each possible value is called a union case. Since discriminated unions can only be one of a set of values, the compiler can do additional checks to make sure your code is correct. Remember that if you use a wildcard when matching against union cases the compiler can't warn you about incomplete pattern matches.

type Num = Positive|Negative|Zero

let numList = [-1;2;0;3]
let result = numList
             |> List.map (function
                          | 0            -> Zero                   
                          | n when n > 0 -> Positive
                          | _            -> Negative ) 

result will now contain [Negative; Positive; Zero; Positive]
The real advantage over using a string is that the compiler can verify that
the values really exist and will reject typing mistakes like "Positvie".

You can also associate data with a union case.

type mnemonic = | Add | Sub  | And | Or | Not
                | Ldc of int | Beq of int
let program = [Ldc 10; Ldc 20; Add]

Decomposing using pattern matching

Let m = Ldc 93
let (Ldc n = m)
  val n : int = 93

Enumerating discriminated unions using reflection

open Microsoft.FSharp.Reflection

type op = LDC of int | ADD | SUB | AND | OR

let cases = FSharpType.GetUnionCases typeof<op>

for case in cases do
    printf "%s" case.Name
    let fields = case.GetFields()
    if fields.Length > 0 then printf " of"
    for f in fields do printf " %s" f.PropertyType.Name
    printfn ""

Enumerations

An enumeration is just a wrapper over a primitive integral type, making it fast and efficient. Enumerations are defined in the same way as discriminated unions but each case must be given a constant value of the same type.

open System
type Numbers = | One = 1
               | Two = 2
               | Three = 3             
printfn "%A" (Enum.GetNames(typeof<Numbers>))

Records

Records give you a way to organize values by type and name by using fields.

  • Type inference can infer the type of a record.
  • Record fields are immutable by default.
  • Records cannot be subclassed.
  • Records can be used as part of standard pattern matching
  • Records (and discriminated unions) get structural comparison and equality semantics for free.
type Person = { FirstName : string; LastName : string; Age : int }
                member this.FullName = this.FirstName + " " + this.LastName  // How to add methods and properties

let id = { FirstName = "Ivor"; LastName = "Cutler"; Age = 103 }
let id2 = { id with Age = 77 }                                               // How to make a clone of id but with a different Age

Generic types

In the F# language, there are two distinct kinds of type parameters.

  1. Standard generic type parameter, indicated by an apostrophe ('), as in 'T and 'U. They are equivalent to generic type parameters in .NET. Resolved at run time.
  2. Statically resolved and is indicated by a caret symbol, as in ^T and ^U. Statically resolved at compile time, allow you to specify that a type argument must have a particular member or members in order to be used by using constraints.
// Use the inline keyword to let type inference infere several types for the same function
let inline respar x y = (x * y) / (x + y)
respar 2000000000I 4000000000I |> printfn "%A"
respar 2.1 4.9 |> printfn "%A"

F# Collection types

Array

A fixed-size, zero-based, mutable collection of consecutive data elements that are all of the same type.

 ; is the delimiter between items.
.. is used to specify a range of numbers.
[| start of array 
|] end of array
Creating single-dimensional arrays           Resulting array
let a = [||]                                 [||] (Empty array)
let a = [|"a";"b";"c"|]                      [|"a";"b";"c"|]
let a = [|1..6|]                             [|1; 2; 3; 4; 5; 6|] (Counting from 1 to 6)
let a = [|1..2..6|]                          [|1; 3; 5|]          (Counting from 1 to 6 increment 2)
let a : int [] = Array.zeroCreate 3          [|0; 0; 0|]
let a = Array.init 5 (fun i -> i * i)        [|0; 1; 4; 9; 16|]
let a = [|for i = 1 to 3 do yield i * 3|]    [|3; 6; 9|]
Access the third item in the array.
let b = a.[2]       
Slicing arrays by index range                              
let a = [|"a";"b";"c";"d"|]                  Resulting array
let c = a.[1..2]                             [|"b";"c"|]
let d = a.[..2]                              [|"a";"b";"c"|]
let e = a.[1..]                              [|"b";"c";"d"|]
Creating a two-dimensional array
let a:int[,] = Array2D.zeroCreate 4 4

let step =
  let a = [|[|dia2;0.5;dia2|];
            [| 0.5;0.0; 0.5|];
            [|dia2;0.5;dia2|]|]
  Array2D.init 3 3 (fun x y -> a.[x].[y])

List

An ordered, immutable series of elements of the same type. Implemented as a linked list. Lists are fast and memory efficient when items are added or removed from the start. Random access and operations at the end of the list are slow. Lists supports structural equality so can be compared by content.

Creating lists works the same as creating arrays.
let a = []
let a = [1;2;3]
...
Adding one element to the front of a list using the cons operator ::.
This is extremely efficient since the new element just links to the original list.
If you need to repeatedly add to the end of the list you can add to the front and reverse the list after it is complete.
let a = [1;2;3]
let b = 0 :: a      // b = [0;1;2;3]

Decomposing a list into head and tail
let a = [1;2;3]
let h::t = a        // h = 1, t = [2;3]

Joining two lists using the Append operator.
let a = [1;2;3]
let b = [4;5;6]
let c = a @ b      // c = [1;2;3;4;5;6]
Always use the cons operator when possible.

seq

A logical series of elements that are all of one type. Sequences are lazy, meaning that they are evaluated when they are used so a sequence can perform better than a list if not all the elements are used. A sequence does not have to exist in memory, it can be computed on the fly and can be infinite. Sequences supports referece equality and can't be directly compared by content, convert to list before adding to a hashtable or set.

Creating sequences works mostly the same as creating lists.
The sequence expression can be recursive using the yield! operator.
let a = seq {1..3}  // a = seq [1; 2; 3]
...

Map

An immutable dictionary of elements. Elements are accessed by key. Map.add will return a new dictionary including the new key/item pair.

// Build a map of items and their frequency
let histogram =
    Seq.fold (fun acc key ->
        if Map.containsKey key acc
        then Map.add key (acc.[key] + 1) acc
        else Map.add key 1 acc
    ) Map.empty
    >> Seq.sortBy (fun kvp -> ~~~kvp.Value)  // Sort sequence by frequency in reverse order

"Testing shows the presence, not the absence of bugs"
|> histogram |> Seq.iter (printfn "%A")

Set

An immutable set that's based on binary trees, where comparison is the F# structural comparison function.

// Build a set of unique items from the array. Partial function application (currying) is performed.
// This function returns a function that consumes one parameter.
let uniqueItems = Seq.fold (fun acc item -> Set.add item acc) Set.empty     
let a = uniqueItems "Simplicity is prerequisite for reliability."

.NET collection types

Resizeable array

The List type is a mutable array that will dynamically change size. Data access is fast since it is an array.

open System.Collections.Generic 
let words = new List<string>()
words.AddRange [|"add";"sub";"and";"or"|]
printfn "%A" words.[2]

Bit array

A compact mutable array of type bool.

open System.Collections
let ba = BitArray(65536)

printfn "%A" ba.[300]
ba.[300] <- true
printfn "%A" ba.[300]

Dictionary

The Dictionary mutable type contains key-value pairs. It is a fast way of looking up data using a key instead of an index. It is slower than an array but much faster than searching an array for a key.

open System.Collections.Generic
let table = new Dictionary<string, int>()
table.["kibi"] <- 1024      // Alternative syntax "table.Add ("kibi",1024)"
table.["mebi"] <- 1024*1024
table.["gibi"] <- 1024*1024*1024
printfn "A traditional megabyte is %A bytes" table.["mebi"]

HashSet

The HashSet type is a mutable and efficient collection for storing an unordered set (no duplicates) of items.

open System.Collections.Generic
let hs = new HashSet<int>()

let addhs n = match hs.Add n with
              | true  -> printfn "Value %A is unique" n
              | false -> printfn "Value %A is a duplicate" n
addhs 3
addhs 7
addhs 3

Stack

The Stack type is a mutable last-in-first-out collection and is implemented as an array. When elements are added to a Stack(T), the capacity is automatically increased as required by reallocating the internal array. The capacity can be decreased by calling TrimExcess.

If Count is less than the capacity of the stack, Push is an O(1) operation. If the capacity needs to be increased to accommodate the new element, Push becomes an O(n) operation, where n is Count. Pop is an O(1) operation.

open System.Collections.Generic
let s = new Stack<int>()

s.Push 2
s.Push 3
s.Push (s.Pop() * s.Pop())

Queue

The Queue typ is a mutable first-in, first-out collection of objects.

open System.Collections.Generic
let s = new Queue<string>()

s.Enqueue "Hello"
s.Enqueue "World"
printfn "First one is %A" (s.Dequeue())
printfn "Next one will be %A" (s.Peek())
printfn "Here it comes %A" (s.Dequeue())

Pass by reference

let a:int[,] = Array2D.zeroCreate 4 4
let b:int[,] = Array2D.zeroCreate 4 4

for y = 0 to 3 do
    for x = 0 to 3 do
        a.[x,y] <- 1 
        b.[x,y] <- 2 

let swap (left : 'a byref) (right : 'a byref) =
  let temp = left
  left <- right
  right <- temp

swap &a.[1,2] &b.[2,3]

Functions

Everything is a function and returns a value. The last expression at the point of exit is returned to the caller.

This is a function called max that takes two numbers as input and returns the larger of them.
let max a b = if a > b then a else b

The if then else is used as a function to return a or b
if a > b then 
    a // If a is larger than b then this is the last expression that will be executed so a is returned from the function
else
    b // If b is larger or equal to a then this is the last expression that will be executed so b is returned from the function
All functions return a value, if it makes no sense to return data the function should return a value of type unit
let a = ()
If the value returned from a function is not required it can be deleted by using the ignore function.
like this ignore (max 1 3)

Recursive functions

Recursive functions must be defined with the rec keyword.

This recursive function counts down from n to 0
let rec count n =
   printfn "%A" n 
   if n = 0 then () else count (n - 1)
If the last statement in the function is the recursive call then the call can be converted to a jump by the compiler.
This function does not return anything so the type unit is returned with the keyword ().

Mutually recursive functions

Two functions that depend on each other will cause the type inference to fail unless using the and keyword.

let rec exec i = match i with
                 | Ldc num -> s.Push num
                 | Add     -> s.Push (s.Pop() + s.Pop())
                 | Inc     -> run [Ldc 1; Add]
and run p = List.iter (exec) p

Currying

If you supply fewer than the required number of arguments you create a new function that expects the remaining arguments. This is referred to as currying and is characteristic of functional programming languages.

let mul x y = x * y
let mul3 = mul 3     // mul3 is now a new function that takes 1 input and multiplies it by 3
let a = mul3 2       // a is now 6

Closure

Values that are in scope where a function is defined will be captured by the closure of the function and will be accessible to the function. Mutable variables stored on the stack will not be captured, use reference cells instead.

Encapsulation of mutable state

It is considered good practice to encapsulate mutable state.

This will create a reference cell, then return a function that refers to the reference cell
let count = let c = ref 0
            (fun () -> c := !c + 1; !c)

let a = count()


F# 4.0 will convert mutable variables to reference cells when required.
let count =
    let mutable c = 0
    (fun () -> c <- c + 1; c)

Loops

Try to avoid loops when it is simple to do so because most loops require change of state or a manipulation of count/index values that is a common source of bugs.

let arr = [|"One";"Two";"Three"|] // Array to be printed

The only way to escape a while loop is using a mutable variable or an exception
let mutable i = 0                 // Mutable state that is undesirable
while(i < 3) do                   // Comparison of index values that can go wrong
    printfn "%A" arr.[i]
    i <- i + 1                    // Calculation of index values that can go wrong

Classic for loop
for t = 0 to arr.Length - 1 do    // Calculation of index values that can go wrong
    printfn "%A" arr.[t]

This type of loop is much safer but still uses the value t
for t in arr do
    printfn "%A" t

Functional equivalent that uses no mutable state, indexes or values
Array.iter (printfn "%A") arr

Pattern matching

Pattern matching should be used in all cases where a simple if then else is not enough. If a match is not found a match failure exception will be raised so make sure there is a match for all possible input. Rules are checked in order from the top down.

   |  marks the start of a new rule to match.
   _  is a wildcard and matches anything.
  ->  points to the action to be taken if there is a rule match.
  as  pattern as identifier. | _ as x -> x * 2
when  specifies an additional expression that has to be true for a rule to match.
The function keyword creates an anonymous function that pattern matches over its argument
let binDigit = function
               | "0" -> Some 0
               | "1" -> Some 1
               | _   -> None                        
General syntax
let fTest fn = match fn with
               | None    -> sprintf "File not found"
               | Some(n) -> sprintf "File '%A' was found" n
Guards are additional expressions following the when keyword that has to be true for a rule to match.
let testNum n = match n with
                | n when n = 0               -> "Number is ZERO"
                | n when n < 0               -> "Number is negative"
                | n when (n &&& (n - 1)) = 0 -> "Number is a power of two"
                | _                          -> "Number seems unremarkable"
                
printfn "%A" (testNum 3)
Named pattern using as
let rec fib = function
   | 0 | 1 as n -> n
   | n -> fib(n - 1) + fib(n - 2)
Composable pattern matching on a single line
let a = 3
a |> function  2 -> "two" | 3 -> "three" | _ -> "other"

Active patterns

Active patterns are special functions that can be matched against instead of literal constants.

  • Single-case active patterns converts data from one form to another.
  • Partial active pattern optionally converts data from one from to another and returns the Option type.
  • Parameterized active patterns take parameters.
(|  Starts the definition of an active pattern function. The name should start with an uppercase letter.
|)  Ends the definition of an active pattern function.
 _  Wildcard

Single-case active pattern definition which convert from one type to another

let (|Len|) (t:string) = t.Length

match "hie" with
| Len 3           -> "length of 3"
| "hi" | "hello"  -> "greeting"
| Len n           -> sprintf "other string of length %A" n

Complete active pattern definition

let (|Even|Odd|) input = if input % 2 = 0 then Even else Odd

Partial active pattern definition that tests if a number is a power of two

let (|IsPow2|_|) n = match n with
                     | 0 -> None
                     | n when (n &&& (n - 1)) = 0 -> Some () // Return the unit type because we have no value to return
                     | _ -> None

let testNum n = match n with
                | n when n = 0 -> "Number is ZERO"
                | n when n < 0 -> "Number is negative"
                | IsPow2       -> "Number is a power of two"
                | _            -> "Number seems unremarkable"

Processing data in a functional way

The easiest method is to run a function on every item of a collection of items to create a new collection.

let a = [1..5]           // The list to process
let mul2 n = n * 2       // Function that will be run on each item in the list
let b = List.map mul2 a  // Mapping the input list a through the function mul2 to the output list b
                         // The operation is
                         // 1 -> mul2 ->  2
                         // 2 -> mul2 ->  4
                         // 3 -> mul2 ->  6
                         // 4 -> mul2 ->  8
                         // 5 -> mul2 -> 10
Using the fun keyword we can create an anonymous function in place
This is helpful if the function is simple and only is required once.
let b = List.map (fun n -> n * 2) a 


The other method is to use a recursive function.

let mul2 l =
    let rec loop listIn listOut =
        match listIn with
        | []   -> listOut
        | h::t -> loop t (h * 2 :: listOut)

    List.rev (loop l [])

mul2 [1;2;3;4;5] // Gives [2;4;6;8;10]

Using a recursive function inside a normal function makes it easy to hide implementation details but is not required.
| [] matches an empty list and -> listOut returns the result.
| h::t will match any list and decompose it into head and tail where head is the first item of the list and tail is the remainder.
-> loop t (h * 2 :: listOut) will call loop with the tail as the new listIn and append h*2 to the front of listOut as the new listOut.
loop l [] starts the recursive function with the input to mul2 as listIn and an empty list as listOut.
The output list is built in the reverse order and List.rev will reverse the list just before it is returned from mul2.


C 
int n;                          // Create n and initialize it to an undefined value
if a = 3 then n = 1 else n = 0; // Set n to 0 or 1

F#
let n = if a = 3 then 1 else 0  // Create n and initialize it to 0 or 1

Function composition

Combining simple functions to build more complex ones has many advantages.

  • Debugging and verification is simpler ebcause each function gets smaller and easier to understand.
  • Reuse of code is easier because each function becomes less specific.
  • Modification of code becomes simpler because it is clearer where to do the change and what effects it will have.

Pipe-forward operator

The pipe-forward operator and the pipe-backward operator, are not actually composing functions but have a similar use.

let drawbm fileName = drawBitmap(parseData(readData(fileName)))
The trouble with this kind of style is that we read from left to right but the code is performed from right to left

Using the pipeline operator we can pipe the data from left to right
let drawbm fileName = fileName |> readData |> parseData |> drawBitmap

or write it in an aligned column like this
let drawbm fileName = fileName
                      |> readData
                      |> parseData
                      |> drawBitmap

Pipe-backward operator

The main purpose of the <| operator is to change precedence

printfn "%A" 2 + 3
This code will not compile because function parameters are evaluated left to right
the compiler will group the code as (printfn "%A" 2) (+ 3)

The <| operator has a low precedence so 2 + 3 will be performed before <| and the code works correctly
printfn "%A" <| 2 + 3

The problem can also be solved by
printfn "%A" (2 + 3)

Forward composition operator

>> joins two functions together and applies the left one first.

The pipe example requires a value to pipe into the first function
let drawbm fileName = fileName |> readData |> parseData |> drawBitmap

With composition the code can be written in a very clean point-free style
let drawbm = readData >> parseData >> drawBitmap
Using the pipe-forward operator can be messy when it requires a lambda function
Seq.map (fun x -> x |> Seq.take i |> Seq.toList)

In this case the forward composition operator is the right solution
Seq.map (Seq.take i >> Seq.toList)

Backward composition operator

<< joins two functions together and applies the right one first.

Lazy evaluation

Normally code is evaluated as soon as possible and each time it is referenced. With lazy evaluation the code is only evaluated if it is needed. This can make a large difference to performance.

let a = [1..1000000]    // This list will use several megabytes of memory and
                           take time corresponding to the total number of items in the list
let b = seq{1..1000000} // This lazy sequence uses almost no memory and only takes time according 
                           to how many items are actually consumed later in the program

Using the lazy keyword to make lazy values.

let a = lazy [1..1000000]
Now a will not be evaluated until it is required.
let b = a.Value
a is now evaluated fully and the whole list will be created so it is not equivalent to using a sequence
let c = a.Value
This time a will not be evaluated, c will just point to the list that was created the first time a was evaluated

Objects

There are 3 main types of relationships that can be modelled with object-oriented programming.

  • Is a relationships can be modelled through inheritance.
  • Has a relationships are modelled through aggregation (fields and properties).
  • Can do relationship which specifies that type X can do the operations described by type Y. The can do relationship is modeled via an interface, which is a contract that a type can implement to establish that it can perform a certain set of actions. An interface is just a collection of methods and properties. A type can declare that it implements the interface if it provides an implementation for each method or property.

Examples

type Counter() =
  let mutable count = 0
  member this.Next() =
    count <- count + 1
    count


// Simulating the PIC16XXX return stack
type PStack() =
    let mutable arr = [|for i in 1..8 do yield 0|]
    let mutable idx = 0

    member this.pop = idx <- (idx + 1) &&& 7
                      arr.[idx]

    member this.push x = arr.[idx] <- x
                         idx <- (idx - 1) &&& 7

Exceptions

  • Raise exception instead of returning error codes for serious errors.
  • Use the option type to indicate failures that are expected in normal running of the code.
  • Where possible throw existing exceptions from the System namespaces.
  • Do not throw System.Exception when it will escape to user code. This includes avoiding the use of failwith, failwithf, which are handy functions for use in scripting and for code under development, but should be removed from F# library code in favour of throwing a more specific exception type.
  • Do use nullArg, invalidArg and invalidOp as the mechanism to throw ArgumentNullException, ArgumentException and InvalidOperationException when appropriate.

Garbage collection

The garbage collector will automatically free any memory that is no longer referenced. It is still possible to have a memory leak in managed code if you unintentionally keep a reference to something that is not needed.

Resources allocated outside the managed environment must manually be freed.

To manually deal with freeing of resources, use the IDisposable interface and the use keyword.

Attributes

Attributes is used to add metainformation to the code. To define new attributes, create a class that inherits from System.Attribute. You can query attributes at run time by using .NET reflection.

Some common attributes

[<EntryPoint>]
Set the starting point of the application, the function will have the signature string[] -> int
string[] will contain System.Environment.GetCommandLineArgs(), minus the first entry in that array.
[<RequiredQualifiedAccess>]
Require the references to an object to be prefixed with the name of the namespace in which the object is defined.
System.Windows.Forms.ListBox instead of ListBox
[<AutoOpen>]
Open the module when the containing namespace is opened. The [<AutoOpen>] attribute may also be applied to an assembly indicate a namespace or module that is automatically opened when the assembly is referenced.
[<Struct>]
[<Measure>]
Indicates that a type or generic parameter is a unit of measure definition or annotation.
[<Obsolete>]
Mark a construct as deprecated so the compiler will issue a warning when it is referenced
[<ReferenceEquality>]
When applied to an F# record or union type, indicates that the type should use reference equality for its default equality implementation.
[<AbstractClass>]
[< >]
[< >]
[< >]

Comments

//  Normal comment
/// XML documentation comment that can be extracted from the source or used for tooltips
(*  Start of multiline/selective comment
*)  End of multiline/selective comment

Hints and tips

The identity function

id is the identity function and takes a parameter and returns it unchanged.

Seq.countBy id "Histograms are magic because they can also be sorted data in compressed form"

No-operation

Use () as a no-operation if there is no need to return a value. When returning a value use None.

match opcode with
| ADD -> eval (+)
| NOP -> ()

Apostrophe

In mathematics, the prime symbol ( ′ ) is used to generate more variable names for things which are similar, without resorting to subscripts – x′ generally means something related to or derived from x.

In F# the apostrophe ( ' ) is used in a similar way by postfixing identifiers with ( ' ).

let square x = 
  let x' = x * x
  x'


Prefixing the type identifier of a parameter with an apostrophe forces the parameter to be generic.

let ident (x : 'a) = x

Random numbers

open System.Diagnostics

let rnd = let rand = System.Random()
          fun n -> rand.Next(n)

Returning a function makes sure that Random is kept in the closure and not being created each time the function is called.

Parallel computing

Functions run in parallel may start and complete in any order.

Array.Parallel
Parallel.For

Printing text

Format strings are checked by the compiler and is type safe. The formats include the common range of specifiers for padding and alignment used by languages such as C, as well as some other specifiers for computed widths and precisions.

printf   Print formatted text to the console
printfn  Print formatted text + newline to the console
sprintf  Print formatted text to a string
Format specifiers
%[flags][width][.precision][type]
%d, %i Integer
%x, %o Integer in Hex or Octal
%s     String
%f     Floating-point
%c     Character
%b     boolean
%O     Object
%A     Anything, this is useful when you are writing a quick script or you are making changes to types.
       Using the exact format specifier instead of %A will help with type inference in complex programs.    

let n = 5                        Result
printfn "The number is %i " n    The number is 5
A function that prints any type in a simple way.
let inline print t = printfn "%A" t
Prints any type and passes it on so you can insert the function in a pipeline to show what passes through.
let inline show t = print t; t