WikiBooks - FSharp

Download as pdf or txt
Download as pdf or txt
You are on page 1of 126

Contents

Preface

1.1

Introduction
Introducing F#

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

2.2

A Brief History of F# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

2.3

Why Learn F#? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Getting Set Up

3.1

Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3.1.1

Setup Procedure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3.1.2

Testing the Install . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3.1.3

Misc. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Mac OSX, Linux and UNIX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3.2.1

Installing interpreter and compiler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3.2.2

MonoDevelop add-in . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3.2.3

Emacs mode and other editors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Basic Concepts

4.1

Major Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

4.1.1

Fundamental Data Types and Type System

. . . . . . . . . . . . . . . . . . . . . . . . .

4.1.2

Type Inference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

4.1.3

Pattern Matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Functional Programming Contrasted with Imperative Programming . . . . . . . . . . . . . . . . .

4.2.1

Immutable Values vs Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

4.2.2

Recursion or Loops? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

4.2.3

Function Composition Rather than Inheritance

. . . . . . . . . . . . . . . . . . . . . . .

4.2.4

Functions as First-Order Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

10

Structure of F# Programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

10

4.2

4.3
5

2.1

3.2

How This Book Came To Be . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Values and Functions

11

5.1

Declaring Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

11

5.1.1

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

11

Declaring Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

11

5.2

Values, Not Variables

ii

CONTENTS
5.2.1

Function Return Values

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

12

5.2.2

How To Read Arrow Notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

12

5.2.3

Partial Function Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

12

5.2.4

Nested Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

13

5.2.5

Generic Functions

13

Pattern Matching Basics


6.0.6

Pattern Matching Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

15

6.0.7

Alternative Pattern Matching Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

15

6.0.8

Binding Variables with Pattern Matching

. . . . . . . . . . . . . . . . . . . . . . . . . .

16

6.0.9

Pay Attention to F# Warnings

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

16
18

7.1

Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

18

7.1.1

Factorial in F# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

18

7.1.2

Greatest Common Divisor (GCD) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

18

Tail Recursion
7.2.1

7.3
7.4

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

How to Write Tail-Recursive Functions

18

. . . . . . . . . . . . . . . . . . . . . . . . . . .

19

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

19

Faster Fib Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

19

Additional Reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

20

Exercises
7.3.1

15

Recursion

7.2

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Higher Order Functions

21

8.0.1

Familiar Higher Order Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

21

8.0.2

The |> Operator

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

22

8.0.3

Anonymous Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

22

8.0.4

Currying and Partial Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

23

Option Types

24

9.1

Using Option Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

24

9.2

Pattern Matching Option Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

24

9.3

Other Functions in the Option Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

24

10 Tuples and Records

26

10.0.1 Dening Tuples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

26

10.0.2 Dening Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

27

11 Lists

29

11.1 Creating Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

29

11.1.1 Using List Literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

29

11.1.2 Using the :: (cons) Operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

29

11.1.3 Using List.init

29

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

11.1.4 Using List Comprehensions


11.2 Pattern Matching Lists

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

29

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

30

CONTENTS

iii

11.2.1 Reverse A List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

30

11.2.2 Filter A List

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

30

11.2.3 Mapping Lists

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

31

11.3 Using the List Module

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

31

11.3.1 List.append and the @ Operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

31

11.3.2 List.choose . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

32

11.3.3 List.fold and List.foldBack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

32

11.3.4 List.nd and List.tryFind . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

32

11.4 Exercises

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

33

11.4.1 Pair and Unpair . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

33

11.4.2 Expand a List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

33

11.4.3 Greatest common divisor on lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

33

11.4.4 Basic Mergesort Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

33

12 Sequences

35

12.1 Dening Sequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

35

12.2 Iterating Through Sequences Manually . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

35

12.3 The Seq Module

36

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

13 Sets and Maps

38

13.1 Sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13.1.1 The Set Module

38

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

38

13.1.2 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

39

13.2 Maps

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

39

13.2.1 The Map Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

39

13.2.2 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

40

13.3 Set and Map Performance

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14 Discriminated Unions

40
41

14.1 Creating Discriminated Unions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

41

14.2 Union basics: an On/O switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

41

14.3 Holding Data In Unions: a dimmer switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

41

14.4 Creating Trees

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

42

14.5 Generalizing Unions For All Datatypes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

42

14.6 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

42

14.6.1 Built-in Union Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

42

14.6.2 Propositional Logic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

43

14.7 Additional Reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

43

15 Mutable Data

44

15.1 mutable Keyword . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


15.1.1 Limitations of Mutable Variables

44

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

44

15.2 Ref cells . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

44

iv

CONTENTS
15.2.1 Aliasing Ref Cells
15.3 Encapsulating Mutable State

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

45

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

45

16 Control Flow

46

16.1 Imperative Programming in a Nutshell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

46

16.2 if/then Decisions

46

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

16.2.1 Working With Conditions

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

46

16.3 for Loops Over Ranges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

47

16.4 for Loops Over Collections and Sequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

47

16.5 while Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

47

17 Arrays

48

17.1 Creating Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

48

17.1.1 Array literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

48

17.1.2 Array comprehensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

48

17.1.3 System.Array Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

48

17.2 Working With Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

48

17.2.1 Array Slicing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

49

17.2.2 Multi-dimensional Arrays

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

49

17.2.3 Using the Array Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

50

17.3 Dierences Between Arrays and Lists

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

51

17.3.1 Representation in Memory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

51

18 Mutable Collections
18.1 List<'T> Class

52
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

18.1.1 Underlying Implementation

52

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

52

18.2 LinkedList<'T> Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

52

18.2.1 Stack<'T> Class

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

53

18.2.2 Queue<'T> Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

53

18.3 HashSet<'T>, and Dictionary<'TKey, 'TValue> Classes . . . . . . . . . . . . . . . . . . . . . . .

54

18.4 Dierences Between .NET BCL and F# Collections . . . . . . . . . . . . . . . . . . . . . . . . .

54

19 Input and Output

55

19.1 Working with the Console

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

55

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

55

19.1.2 With .NET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

56

19.1.1 With F#

19.2 System.IO Namespace

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

56

19.2.1 Files and Directories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

56

19.2.2 Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

56

20 Exception Handling
20.1 Try/With

57

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

57

20.2 Raising Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

57

CONTENTS
20.3 Try/Finally

v
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

58

20.3.1 use Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

58

20.4 Dening New Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

58

20.5 Exception Handling Constructs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

59

21 Operator Overloading
21.1 Using Operators

60
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

60

21.2 Operator Overloading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

60

21.3 Dening New Operators

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

60

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

60

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

60

21.3.1 Inx operators


21.3.2 Prex Operators
22 Classes
22.1 Dening an Object

62
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

62

22.1.1 Implicit Class Construction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

62

22.1.2 Explicit Class Denition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

63

22.1.3 Dierences Between Implicit and Explicit Syntaxes . . . . . . . . . . . . . . . . . . . . .

64

22.1.4 Class Inference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

64

22.2 Class Members . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

65

22.2.1 Instance and Static Members . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

65

22.2.2 Getters and Setters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

66

22.2.3 Adding Members to Records and Unions

. . . . . . . . . . . . . . . . . . . . . . . . . .

66

22.3 Generic classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

66

22.4 Pattern Matching Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

67

23 Inheritance

68

23.1 Subclasses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
23.1.1 Overriding Methods

68

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

68

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

69

23.2 Working With Subclasses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

69

23.1.2 Abstract Classes

23.2.1 Up-casting and Down-casting

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

69

23.2.2 Public, Private, and Protected Members . . . . . . . . . . . . . . . . . . . . . . . . . . .

70

24 Interfaces

71

24.1 Dening Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

71

24.2 Using Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

71

24.2.1 What are interfaces used for?

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

72

24.2.2 Implementing Interfaces with Object Expressions . . . . . . . . . . . . . . . . . . . . . .

72

24.2.3 Implementing Multiple Interfaces

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

72

24.2.4 Interface Hierarchies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

72

24.3 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

73

24.3.1 Generalizing a function to many classes

. . . . . . . . . . . . . . . . . . . . . . . . . . .

73

24.3.2 Using interfaces in generic type denitions . . . . . . . . . . . . . . . . . . . . . . . . . .

73

vi

CONTENTS
24.3.3 Simple dependency injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

25 Events

73
75

25.1 Dening Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

75

25.2 Adding Callbacks to Event Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

75

25.3 Working with EventHandlers Explicitly

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

76

25.3.1 Adding and Removing Event Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . .

76

25.3.2 Dening New Delegate Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

76

25.3.3 Use existing .NET WPF Event and Delegate Types . . . . . . . . . . . . . . . . . . . . .

76

25.4 Passing State To Callbacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

77

25.5 Retrieving State from Callers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

77

25.6 Using the Event Module

78

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

26 Modules and Namespaces

80

26.1 Dening Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


26.1.1 Submodules

80

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

80

26.1.2 Extending Types and Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

81

26.1.3 Module Signatures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

81

26.2 Dening Namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

82

26.2.1 Adding to Namespace from Multiple Files . . . . . . . . . . . . . . . . . . . . . . . . . .

82

26.2.2 Controlling Class and Module Accessibility . . . . . . . . . . . . . . . . . . . . . . . . .

83

27 Units of Measure

84

27.1 Use Cases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


27.1.1 Statically Checked Type Conversions

84

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

84

27.1.2 Decorating Data With Contextual Information . . . . . . . . . . . . . . . . . . . . . . . .

84

27.2 Dening Units

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

85

27.3 Dimensionless Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

85

27.4 Generalizing Units of Measure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

85

27.5 F# PowerPack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

86

27.6 External Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

86

28 Caching

87

28.1 Partial Functions

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

87

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

87

28.3 Lazy Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

88

28.2 Memoization

29 Active Patterns
29.1 Dening Active Patterns

89
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

89

29.1.1 Using Active Patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

89

29.1.2 Parameterizing Active Patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

90

29.1.3 Partial Active Patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

90

29.2 Additional Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

91

CONTENTS

vii

30 Advanced Data Structures

92

30.1 Stacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

92

30.2 Queues

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

93

30.2.1 Naive Queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

93

30.2.2 Queue From Two Stacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

93

30.3 Binary Search Trees

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

94

30.3.1 Red Black Trees . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

94

30.3.2 AVL Trees . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

95

30.3.3 Heaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

96

30.4 Lazy Data Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

97

30.5 Additional Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

97

31 Reection
31.1 Inspecting Types

99
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

31.1.1 Example: Reading Properties


31.1.2 Example: Setting Private Fields

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

99
99

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100

31.2 Microsoft.FSharp.Reection Namespace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100


31.3 Working With Attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
31.3.1 Example: Encapsulating Singleton Design Pattern . . . . . . . . . . . . . . . . . . . . . . 101
32 Computation Expressions
32.1 Monad Primer

102

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

32.2 Dening Computation Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103


32.2.1 Syntax Sugar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
32.2.2 Dissecting Syntax Sugar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
32.3 What are Computation Expressions Used For? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
32.4 Additional Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
33 Async Workows

105

33.1 Dening Async Workows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105


33.1.1 The Async Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
33.1.2 async<'a> Members

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105

33.2 Async Extensions Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106


33.3 Async Examples

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106

33.3.1 Parallel Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106


33.4 Concurrency with Functional Programming

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106

33.4.1 Why Concurrency Matters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107


33.4.2 Problems with Mutable State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
33.4.3 Why Functional Programming Matters . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
34 MailboxProcessor
34.1 Dening MailboxProcessors

109
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109

34.2 MailboxProcessor Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109

viii

CONTENTS
34.3 Two-way Communication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
34.3.1 Encapsulating MailboxProcessors with Objects
34.4 MailboxProcessor Examples

. . . . . . . . . . . . . . . . . . . . . . . 110

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

34.4.1 Prime Number Sieve . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110


35 Lexing and Parsing

111

35.1 Lexing and Parsing from a High-Level View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111


35.2 Extended Example: Parsing SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
35.2.1 Step 1: Dene the Abstract Syntax Tree . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
35.2.2 Step 2: Dene the parser tokens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
35.2.3 Step 3: Dening the lexer rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
35.2.4 Step 4: Dene the parser rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
35.2.5 Step 5: Piecing Everything Together . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
35.2.6 Sample . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
35.3 Text and image sources, contributors, and licenses . . . . . . . . . . . . . . . . . . . . . . . . . . 116
35.3.1 Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
35.3.2 Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
35.3.3 Content license . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

Chapter 1

Preface
1.1 How This Book Came To Be

The most remarkable applications in the world aren't


written in Java; they're written in these weird, obscure
languages. More interestingly, the languages in the highNote to contributors: normally a preface is
est demand -- Erlang, Haskell, Lisp, OCaml -- were all
written by the author of a book. Since this
functional programming languages, a wholly alien probook might have several authors, feel free to
gramming paradigm from my vantage point deep in C#write your own preface.
Land. I decided to supplement my programming wisdom
Written by Awesome Princess: Normally, authors choose by learning one of these obscure functional programming
to write a preface to a book about the book itself. But, just languages.
because I'm an egomaniac, I want to write about myself The choice between one language or another wasn't too
instead. Not because I'm an especially interesting person, hard to make. If I'm going to learn a new language, it
but because my experiences with functional programming needs to satisfy a few conditions: it should be practiare relevant to the creation of this book.
cal enough for personal use, relatively speedy, useful to
So, in 2006, I was becoming bored with my job. The employers, and impress my friends when I tell them I
only kind of software I've ever written has been software learned a weird new language. Haskell was quite scary
that puts a GUI interface on top of a database, and I just to me at the time, and I can't really exploit Erlangs conbecame tired with it. I wanted to nd an interesting pro- currency with the tiny scope of the apps I write for myself. The choice came down to Lisp and OCaml; based
gramming job.
on these comparisons of dierent languages, I decided
Just for fun, I started looking at job openings at dier- that OCamls static-typing, speedy native code, tiny coment high tech companies (Google, eBay, Microsoft, Ama- piled binaries, and established niche in the nancial marzon, etc.). I noticed that all of the boring jobs -- CRUD ket made it a good choice for me.
apps, simple web development -- wanted programmers
with Java, C#, or C++ experience. The interesting jobs -- I learned OCaml and it completely changed my way of
compiler programming, digital circuit verication, mas- thinking. After using the language and keeping up with
sively parallel computing, biometrics -- sought program- OCaml newsgroups, I heard about a .NET port of OCaml
mers with experience in weird and unfamiliar languages. called F#. I gured I already knew the .NET BCL inside
and out, and I was already familiar with OCaml, I could
In particular:
probably learn this language pretty quickly.
I read in Paul Grahams article Beating the Averages In August 2007, I took the time to get familiar with the
that the rst version of Yahoo! Store was written F# language. While I picked up most of it fairly well, one
largely in Lisp
thing I noticed about the language was how completely
inaccessible it was to people trying to learn the language.
I came across job postings for Google looking for
The complete dearth of F# material out there just makes
programmers with Haskell or Python experience in
it impossible for beginners to learn F# as their rst lanaddition to C++.
guage. Even today, November 2008, there are only a
I read in an Erlang FAQ that the Erlang program- handful of publications, but even as a person with many
ming language is the tool of choice for telecommu- years of programming experience, I struggled to follow
along and comprehend the language.
nications providers like T-Mobile.
I've heard for years that Lisp was a niche language For a long time, I wanted to write something that would
actually be useful to F# neophytes, something that would
during the Golden Age of AI research.
contain everything anyone needs to know about the lan I ran across numerous Microsoft job postings in the guage into a single comprehensive resource. This book
area of driver verication looking for OCaml pro- was originally started by a fellow Wikibookian in 2006,
grammers.
1

2
but no one had written any substantial content for it for
nearly 2 years. I found this book and decided that, for the
sake of people wanting to learn F#, I'd compile everything I knew about the language into a format that would
be acceptable for rst-time programmers.
I am happy with the way the book has been progressing.
Ultimately, I'd like people to link to this book as the preferred, denitive F# tutorial on Internet.

CHAPTER 1. PREFACE

Chapter 2

Introduction
2.1 Introducing F#

for the LCF Theorem prover. Lisp continued to be used


for years as the favored language of AI researchers.

The F# programming language is part of Microsofts


family of .NET languages, which includes C#, Visual Basic.NET, JScript.NET, and others. As a .NET language,
F# code compiles down to Common Language Infrastructure (CLI) byte code or Microsoft Intermediate Language
(MSIL) which runs on top of the Common Language
Runtime (CLR). All .NET languages share this common
intermediate state which allows them to easily interoperate with one another and use the .NET Frameworks Base
Class Library (BCL).

ML stands out among other functional programming languages; its polymorphic functions made it a very expressive language, while its strong typing and immutable
data structures made it possible to compile ML into
very ecient machine code. MLs relative success
spawned an entire family of ML-derived languages, including Standard ML, Caml, its most famous dialect
called OCaml which unies functional programming with
object-oriented and imperative styles, and Haskell.
F# was developed in 2005 at Microsoft Research.
many ways, F# is essentially a .Net implementation
OCaml, combining the power and expressive syntax
functional programming with the tens of thousands
classes which make up the .NET class library.

In many ways, its easy to think of F# as a .NET implementation of OCaml, a well-known functional programming language from the ML family of functional programming languages. Some of F#'s notable features include type inference, pattern matching, interactive scripting and debugging, higher order functions, and a welldeveloped object model which allows programmers to
mix object-oriented and functional programming styles
seamlessly.

In
of
of
of

2.3 Why Learn F#?


Functional programming is often regarded as the bestkept secret of scientic modelers, mathematicians, articial intelligence researchers, nancial institutions,
graphic designers, CPU designers, compiler programmers, and telecommunications engineers. Understandably, functional programming languages tend to be used
in settings that perform heavy number crunching, abstract
symbolic processing, or theorem proving. Of course,
while F# is abstract enough to satisfy the needs of some
highly technical niches, its simple and expressive syntax makes it suitable for CRUD apps, web pages, GUIs,
games, and general-purpose programming.

2.2 A Brief History of F#

There are three dominant programming paradigms used


today: functional, imperative, and object-oriented programming. Functional programming is the oldest of the
three, beginning with Information Processing Language
in 1956 and made popular with the appearance of Lisp in
1958. Of course, in the highly competitive world of programming languages in the early decades of computing,
imperative programming established itself as the industry norm and preferred choice of scientic researchers Programming languages are becoming more functional
and businesses with the arrival of Fortran in 1957 and every year. Features such as generic programming, type
inference, list comprehensions, functions as values, and
COBOL in 1959.
While imperative languages became popular with busi- anonymous types, which have traditionally existed as stanesses, functional programming languages continued to ples of functional programming, have quickly become
be developed primarily as highly specialized niche lan- mainstream features of Java, C#, Delphi and even Forguages. For example, the APL programming language, tran. We can expect next-generation programming landeveloped in 1962, was developed to provide a consistent, guages to continue this trend in the future, providing a
mathematical notation for processing arrays. In 1973, hybrid of both functional and imperative approaches that
Robin Milner at the University of Edinburgh developed meet the evolving needs of modern programming.
the ML programming language to develop proof tactics F# is valuable to programmers at any skill level; it com3

4
bines many of the best features of functional and objectoriented programming styles into a uniquely productive
language.

CHAPTER 2. INTRODUCTION

Chapter 3

Getting Set Up
3.1 Windows

additional F# project types.

At the time of this writing, its possible to run F# code


through Visual Studio, through its interactive top-level 3.1.2 Testing the Install
F# Interactive (fsi), and compiling from the command
line. This book will assume that users will compile code Hello World executable
through Visual Studio or F# Interactive by default, unless
specically directed to compile from the command line. Lets create the Hello World standalone application.
Create a text le called hello.fs containing the following
code:

3.1.1

Setup Procedure

(* lename: hello.fs *) let _ = printf Hello world

F# can integrate with existing installations of Visual Studio 2008 and is included with Visual Studio 2010. Alternatively, users can download Visual Studio Express or
Community for free, which will provide an F# pioneer
with everything she needs to get started, including interactive debugging, breakpoints, watches, Intellisense, and
support for F# projects. Make sure all instances of Visual
Studio and Visual Studio Shell are closed before continuing.

Save and close the le and then compile this le:


fsc -o hello.exe hello.fs
Now you can run hello.exe to produce the expected output.
F# Interactive Environment

To get started, users should download and install the latest version of the .NET Framework from Microsoft. Afterwards, download the latest version of F# from the
F# homepage on Microsoft Research, then execute the
installation wizard. Users should also consider downloading and installing the F# PowerPack, which contains
handy extensions to the F# core library.

Open a command-line console (hit the Start button,


click on the Run icon and type cmd and hit ENTER).
Type fsi and hit ENTER. You will see the interactive console:

Microsoft F# Interactive, (c) Microsoft Corporation, All


Rights Reserved F# Version 1.9.6.2, compiling for .NET
Framework Version v2.0.50727 Please send bug reports
After successful installation, users will notice an addito fsbugs@microsoft.com For help type #help;; >
tional folder in their start menu, Microsoft F# 2.0.X.X.
Additionally, users will notice that an entry for F#
Projects has been added to the project types menu in Vi- We can try some basic F# variable assignment (and some
sual Studio. From here, users can create and run new F# basic maths).
projects.
> let x = 5;; val x : int > let y = 20;; val y : int > y + x;;
It is a good idea to add the executable location (e.g. val it : int = 25
c:\fsharp\bin\) to the %PATH% environment variable, so
you can access the compiler and the F# interactive envi- Finally we quit out of the interactive environment
ronment (FSI) from any location.
> #quit;;
As of Visual Studio 2012 the easiest way to get going is to
install Visual Studio 2012 for Web at (even if you want
to do desktop solution). You can then install F# Tools
for Visual Studio Express 2012 for Web from . Once 3.1.3 Misc.
this is done you can create F# projects. Search Nuget for
5

CHAPTER 3. GETTING SET UP

Adding to the PATH Environment Variable


1. Go to the Control Panel and choose System.
2. The System Properties dialog will appear. Select
the Advanced tab and click the Environment Variables....
3. In the System Variables section, select the Path variable from the list and click the Edit... button.
4. In the Edit System Variable text box append a
semicolon (;) followed by the executable path (e.g.
;C:\fsharp\bin\)
5. Click on the OK button
6. Click on the OK button
7. Click on the Apply button
Now any command-line console will check in this location when you type fsc or fsi.

3.2 Mac OSX, Linux and UNIX


F# runs on Mac OSX, Linux and other Unix versions with
the latest Mono. This is supported by the F# community
group called the F# Software Foundation.

3.2.1

Installing interpreter and compiler

The F# Software Foundation give latest instructions on


getting started with F# on Linux and Mac. Once built
and/or installed, you can use the fsharpi command to
use the command-line interpreter, and fsharpc for the
command-line compiler.

3.2.2

MonoDevelop add-in

The F# Software Foundation also give instructions for installing the Monodevelop support for F#. This comes
with project build system, code completion, and syntax
highlighting support.

3.2.3

Emacs mode and other editors

The F# Software Foundation also give instructions for using F# with other editors. An emacs mode for F# is also
available on Github.

Chapter 4

Basic Concepts
Lists, Maps, and Sets, which represent immutable
versions of a stack, hashtable, and set data structures, respectively.

Now that we have a working installation of F# we can


explore the syntax of F# and the basics of functional programming. We'll start o in the Interactive F Sharp environment as this gives us some very valuable type information, which helps get to grips with what is actually going
on in F#. Open F# interactive from the start menu, or
open a command-line prompt and type fsi.

Sequences, which represent a lazy list of items computed on-demand.


Computation expressions, which serve the same purpose as monads in Haskell, allowing programmers to
write continuation-style code in an imperative style.

4.1 Major Features


4.1.1

All of these features will be further enumerated and ex-

Fundamental Data Types and Type plained in later chapters of this book.
System
F# is a statically typed language, meaning that the com-

piler knows the datatype of variables and functions at


In computer programming, every piece of data has a type, compile time. F# is also strongly typed, meaning that
which, predictably, describes the type of data a program- a variable bound to ints cannot be rebound to strings at
mer is working with. In F#, the fundamental data types some later point; an int variable is forever tied to int data.
are:
Unlike C# and VB.Net, F# does not perform implicit
F# is a fully object-oriented language, using an object casts, not even safe conversions (such as converting an int
model based on the .NET Common Language Infrastruc- to a int64). F# requires explicit casts to convert between
ture (CLI). As such, it has a single-inheritance, multiple datatypes, for example:
interface object model, and allows programmers to declare classes, interfaces, and abstract classes. Notably, it > let x = 5;; val x : int = 5 > let y = 6L;; val y : int64 =
has full support for generic class, interface, and function 6L > let z = x + y;; let z = x + y;; ------------^ stdin(5,13):
denitions; however, it lacks some OO features found in error FS0001: The type 'int64' does not match the type
other languages, such as mixins and multiple inheritance. 'int' > let z = (int64 x) + y;; val z : int64 = 11L
F# also provides a unique array of data structures built
The mathematical operators +, -, /, *, and % are overdirectly into the syntax of the language, which include:
loaded to work with dierent datatypes, but they require
arguments on each side of the operator to have the same
Unit, the datatype with only one value, equivalent to datatype. We get an error trying to add an int to an int64,
void in C-style languages.
so we have to cast one of our variables above to the others
datatype before the program will successfully compile.
Tuple types, which are ad hoc data structures that
programmers can use to group related values into a
single object.
4.1.2 Type Inference
Record types, which are similar to tuples, but proUnlike many other strongly typed languages, F# often
vide named elds to access data held by the record
does not require programmers to use type annotations
object.
when declaring functions and variables. Instead, F# at Discriminated unions, which are used to create very tempts to work out the types for you, based on the way
well-dened type hierarchies and hierarchical data that variables are used in code.
structures.

For example, lets take this function:


7

CHAPTER 4. BASIC CONCEPTS

let average a b = (a + b) / 2.0


We have not used any type annotations: that is, we have
not explicitly told the compiler the data type of a and b,
nor have we indicated the type of the functions return
value. If F# is a strongly, statically typed language, how
does the compiler know the datatype of anything beforehand? Thats easy, it uses simple deduction:
The + and / operators are overloaded to work on different datatypes, but it defaults to integer addition
and integer division without any extra information.
(a + b) / 2.0, the value in bold has the type oat.
Since F# doesn't perform implicit casts, and it requires arguments on both sides of a math operator
to have the same datatype, the value (a + b) must
return a oat as well.
The + operator only returns oat when both arguments on each side of the operator are oats, so a
and b must be oats as well.
Finally, since the return value of oat / oat is oat,
the average function must return a oat.

Proposition | And of Proposition * Proposition | Or of


Proposition * Proposition let rec eval x = match x with
| True -> true // syntax: Pattern-to-match -> Result |
Not(prop) -> not (eval prop) | And(prop1, prop2) ->
(eval prop1) && (eval prop2) | Or(prop1, prop2) -> (eval
prop1) || (eval prop2) let shouldBeFalse = And(Not True,
Not True) let shouldBeTrue = Or(True, Not True) let
complexLogic = And(And(True,Or(Not(True),True)),
Or(And(True, Not(True)), Not(True)) ) printfn shouldBeFalse: %b (eval shouldBeFalse) // prints False printfn
shouldBeTrue: %b (eval shouldBeTrue) // prints True
printfn complexLogic: %b (eval complexLogic) //
prints False
The eval method uses pattern matching to recursively traverse and evaluate the abstract syntax tree. The rec keyword marks the function as recursive. Pattern matching
will be explained in more detail in later chapters of this
book.

4.2 Functional Programming Contrasted with Imperative Programming

This process is called type-inference. On most occasions,


F# will be able to work out the types of data on its own
without requiring the programmer to explicitly write out F# is a mixed-paradigm language: it supports imperative,
type annotations. This works just as well for small pro- object-oriented, and functional styles of writing code,
grams as large programs, and it can be a tremendous time- with heaviest emphasis on the latter.
saver.
On those occasions where F# cannot work out the types
4.2.1 Immutable Values vs Variables
correctly, the programmer can provide explicit annotations to guide F# in the right direction. For example, as
The rst mistake a newcomer to functional programming
mentioned above, math operators default to operations on
makes is thinking that the let construct is equivalent to
integers:
assignment. Consider the following code:
> let add x y = x + y;; val add : int -> int -> int
let a = 1 (* a is now 1 *) let a = a + 1 (* in F# this throws
an error: Duplicate denition of value 'a' *)
In absence of other information, F# determines that add
takes two integer and returns another integer. If we
On the surface, this looks exactly like the familiar imperwanted to use oats instead, we'd write:
ative pseudocode:
> let add (x : oat) (y : oat) = x + y;; val add : oat ->
a = 1 // a is 1 a = a + 1 // a is 2
oat -> oat
However, the nature of the F# code is very dierent. Every let construct introduces a new scope, and binds symbols to values in that scope. If execution escapes this in4.1.3 Pattern Matching
troduced scope, the symbols are restored to their original
meanings. This is clearly not identical to variable state
F#'s pattern matching is similar to an if... then or switch mutation with assignment.
construct in other languages, but is much more powerful. Pattern matching allows a programmer to decompose To clarify, let us desugar the F# code:
data structures into their component parts. It matches val- let a = 1 in ((* a stands for 1 here *); (let a = (* a still
ues based on the shape of the data structure, for example: stands for 1 here *) a + 1 in (* a stands for 2 here *)); (*
type Proposition = // type with possible expressions ... a stands for 1 here, again *))
note recursion for all expressions except True | True
// essentially this is dening boolean logic | Not of Indeed the code

4.2. FUNCTIONAL PROGRAMMING CONTRASTED WITH IMPERATIVE PROGRAMMING

let a = 1 in (printfn "%i a; (let a = a + 1 in printfn "%i items.Length - 1 do let item = items.[i] in proc item
a); printfn "%i a)
However, the above code is clearly not written in a functional style. One problem with it is that it traverses an
array of items. For many purposes including enumera121
tion, functional programmers would use a dierent data
Once symbols are bound to values, they cannot be as- structure, a singly linked list. Here is an example of itersigned a new value. The only way to change the meaning ating over this data structure with pattern matching:
of a bound symbol is to shadow it by introducing a new
binding for this symbol (for example, with a let construct, let rec processItems = function | [] -> () // empty list: end
as in let a = a + 1), but this shadowing will only have a recursion | head :: tail -> // split list in rst item (head)
localized eect: it will only aect the newly introduced and rest (tail) proc head; processItems tail // recursively
scope. F# uses so-called 'lexical scoping', which simply enumerate list
means that one can identify the scope of a binding by simply looking at the code. Thus the scope of the let a = a + It is important to note that because the recursive call to
1 binding in (let a = a + 1 in ..) is limited by the paren- processItems appears as the last expression in the functheses. With lexical scoping, there is no way for a piece tion, this is an example of so-called tail recursion. The
of code to change the value of a bound symbol outside of F# compiler recognizes this pattern and compiles proitself, such as in the code that has called it.
cessItems to a loop. The processItems function therefore
Immutability is a great concept. Immutability allows pro- runs in constant space and does not cause stack overows.
prints out

grammers to pass values to functions without worrying


that the function will change the values state in unpredictable ways. Additionally, since value can't be mutated, programmers can process data shared across many
threads without fear that the data will be mutated by another process; as a result, programmers can write multithreaded code without locks, and a whole class of errors
related to race conditions and dead locking can be eliminated.

F# programmers rely on tail recursion to structure their


programs whenever this technique contributes to code
clarity.
A careful reader has noticed that in the above example
proc function was coming from the environment. The
code can be improved and made more general by parameterizing it by this function (making proc a parameter):

let rec processItems proc = function | [] -> () | hd :: tl ->


proc hd; processItems proc tl // recursively enumerate
Functional programmers generally simulate state by passlist
ing extra parameters to functions; objects are mutated
by creating an entirely new instance of an object with the
desired changes and letting the garbage collector throw This processItems function is indeed so useful that it
away the old instances if they are not needed. The re- has made it into the standard library under the name of
source overheads this style implies are dealt with by shar- List.iter.
ing structure. For example, changing the head of a singly- For the sake of completeness it must be mentioned
linked list of 1000 integers can be achieved by allocating that F# includes generic versions of List.iter called
a single new integer, reusing the tail of the original list (of Seq.iter (other List.* functions usually have Seq.* counlength 999).
terparts as well) that works on lists, arrays, and all
For the rare cases when mutation is really needed (for ex- other collections. F# also includes a looping construct
ample, in number-crunching code which is a performance that works for all collections implementing the Sysbottleneck), F# oers reference types and .NET mutable tem.Collections.Generic.IEnumerable:
collections (such as arrays).
for item in collection do process item

4.2.2

Recursion or Loops?

Imperative programming languages tend to iterate 4.2.3


through collections with loops:

Function Composition Rather than


Inheritance

void ProcessItems(Item[] items) { for(int i = 0; i


< items.Length; i++) { Item myItem = items[i]; Traditional OO uses implementation inheritance extensively; in other words, programmers create base classes
proc(myItem); // process myItem } }
with partial implementation, then build up object hierarchies from the base classes while overriding members as
This admits a direct translation to F# (type annotations needed. This style has proven to be remarkably eective
for i and item are omitted because F# can infer them):
since the early 1990s, however this style is not contiguous
let processItems (items : Item []) = for i in 0 .. with functional programming.

10
Functional programming aims to build simple, composable abstractions. Since traditional OO can only make an
objects interface more complex, not simpler, inheritance
is rarely used at all in F#. As a result, F# libraries tend to
have fewer classes and very at object hierarchies, as
opposed to very deep and complex hierarchies found in
equivalent Java or C# applications.

CHAPTER 4. BASIC CONCEPTS

4.3 Structure of F# Programs


A simple, non-trivial F# program has the following parts:
open System (* This is a multi-line comment *) // This
is a single-line comment let rec b = function | 0 ->
0 | 1 -> 1 | n -> b (n - 1) + b (n - 2) let main() =
Console.WriteLine(b 5: {0}", (b 5)) main()

F# tends to rely more on object composition and delegation rather than inheritance to share snippets of impleMost F# code les begin with a number of open statementation across modules.
ments used to import namespaces, allowing programmers to reference classes in namespaces without having
to write fully qualied type declarations. This keyword
4.2.4 Functions as First-Order Types
is functionally equivalent to the using directive in C# and
F# is a functional programming language, meaning that Imports directive in VB.Net. For example, the Console
functions are rst-order data types: they can be declared class is found under the System namespace; without imand used in exactly the same way that any other variable porting the namespace, a programmer would need to access the Console class through its fully qualied name,
can be used.
System.Console.
In an imperative language like Visual Basic there is a funThe body of the F# le usually contains functions to imdamental dierence between variables and functions.
plement the business logic in an application.
Dim myVal as Integer Dim myParam as Integer myParam = 2 Public Function MyFunc(Dim param as Finally, many F# application exhibit this pattern:
Integer) MyFunc = (param * 2) + 7 End Function myVal let main() = (* ... This is the main loop. ... *) main()
= MyFunc(myParam)
(* This is a top-level statement because its not nested in
any other functions. This calls into the main method to
Notice the dierence in syntax between dening and run the main loop. *)
evaluating a function and dening and assigning a variable. In the preceding Visual Basic code we could per- In general, there is no explicit entry point in an F# appliform a number of dierent actions with a variable we cation. Rather, when an F# application is compiled, the
can:
last le passed to the compiler is assumed to be the entry
point, and the compiler executes all top-level statements
create a token (the variable name) and associate it in the le from top to bottom. While its possible to have
any number of top-level statements in the entry point of
with a type
a F# program, well-written F# only has a single top-level
assign it a value
statement which calls the main loop of the application.
interrogate its value
pass it into a function or sub-routine (which is essentially a function that returns no value)
return it from a function
Functional programming makes no distinction between
values and functions, so we can consider functions to be
equal to all other data types. That means that we can:
create a token (the function variable name) and associate it with a type
assign it a value (the actual calculation)
interrogate its value (perform the calculation)
pass a function as a parameter of another function
or sub-routine
return a function as the result of another function

Chapter 5

Values and Functions


Compared to other .NET languages such as C# and
VB.Net, F# has a somewhat terse and minimalistic syntax. To follow along in this tutorial, open F# Interactive
(fsi) or Visual Studio and run the examples.

bind a variable to a value, its stuck with that value forever. For that reason, most F# programmers prefer to
use value rather than variable to describe x, y, and z
above. Behind the scenes, F# actually compiles the variables above as static read-only properties.

5.1 Declaring Variables


The most ubiquitous, familiar keyword in F# is the let
keyword, which allows programmers to declare functions
and variables in their applications.
For example:
let x = 5

5.2 Declaring Functions


There is little distinction between functions and values in
F#. You use the same syntax to write a function as you
use to declare a value:

let add x y = x + y
This declares a variable called x and assigns it the value
5. Naturally, we can write the following:
add is the name of the function, and it takes two parameters, x and y. Notice that each distinct argument in the
let x = 5 let y = 10 let z = x + y
functional declaration is separated by a space. Similarly,
when you execute this function, successive arguments are
z now holds the value 15.
separated by a space:
A complete program looks like this:
let z = add 5 10
let x = 5 let y = 10 let z = x + y printfn x: %i x printfn
y: %i y printfn z: %i z
This assigns z the return value of this function, which in
this case happens to be 15.
The statement printfn prints text out to the console winNaturally, we can pass the return value of functions didow. As you might have guessed, the code above prints
rectly into other functions, for example:
out the values of x, y, and z. This program results in the
let add x y = x + y let sub x y = x - y let printThreefollowing:
Numbers num1 num2 num3 = printfn num1: %i num1
x: 5 y: 10 z: 15
printfn num2: %i num2 printfn num3: %i num3
printThreeNumbers 5 (add 10 7) (sub 20 8)
Note to F# Interactive users: all statements
in F# Interactive are terminated by ;; (two
semicolons). To run the program above in fsi,
copy and paste the text above into the fsi window, type ;;, then hit enter.

5.1.1

This program outputs:


num1: 5 num2: 17 num3: 12
Notice that I have to surround the calls to add and sub
functions with parentheses; this tells F# to treat the value
in parentheses as a single argument.

Values, Not Variables

Otherwise, if we wrote printThreeNumbers 5 add 10 7


sub 20 8, its not only incredibly dicult to read, but it
In F#, variable is a misnomer. In reality, all vari- actually passes 7 parameters to the function, which is obables in F# are immutable; in other words, once you viously incorrect.
11

12

5.2.1

CHAPTER 5. VALUES AND FUNCTIONS

Function Return Values

Data types are read from left to right. Before muddying


Unlike many other languages, F# functions do not have
the waters with a more accurate description of how F#
an explicit keyword to return a value. Instead, the return
functions are built, consider the basic concept of Arrow
value of a function is simply the value of the last statement
Notation: starting from the left, our function takes two int
executed in the function. For example:
inputs and returns a string. A function only has one return
let sign num = if num > 0 then positive elif num < 0 type, which is represented by the rightmost data type in
then negative else zero
chained arrow notation.
We can read the following data types as follows:
This function takes an integer parameter and returns a
int -> string
string. As you can imagine, the F# function above is
equivalent to the following C# code:
takes one int input, returns a string
string Sign(int num) { if (num > 0) return positive";
else if (num < 0) return negative"; else return zero"; }
oat -> oat -> oat
Just like C#, F# is a strongly typed language. A function
can only return one datatype; for example, the following
F# code will not compile:

takes two oat inputs, returns another oat

let sign num = if num > 0 then positive elif num < 0 int -> string -> oat
then negative else 0
takes an int and a string input, returns a oat
If you run this code in fsi, you get the following error
message:
This description is a good introductory way to understand
> let sign num = if num > 0 then positive elif num < 0 Arrow Notation for a beginner--and if you are new to F#
feel free to stop here until you get your feet wet. For those
then negative else 0;; else 0;; ---------^
stdin(7,10): error FS0001: This expression was expected who feel comfortable with this concept as described, the
actual way in which F# is implementing these calls is via
to have type string but here has type int
currying the function.
The error message is quite explicit: F# has determined
that this function returns a string, but the last line of the
function returns an int, which is an error.
5.2.3 Partial Function Application
Interestingly, every function in F# has a return value; of
course, programmers don't always write functions that return useful values. F# has a special datatype called unit,
which has just one possible value: (). Functions return
unit when they don't need to return any value to the programmer. For example, a function that prints a string to
the console obviously doesn't have a return value:
let helloWorld = printfn hello world

While the above description of Arrow Notation is intuitive, it is not entirely accurate due to the fact that F#
implicitly curries functions. This means that a function
only ever has a single argument and a single return type,
quite at odds with the previous description of Arrow Notation above where in the second and third example two
arguments are passed to a function. In reality, a function
in F# only ever has a single argument and a single return
type. How can this be? Consider this type:

This function takes no parameters and returns (). You oat -> oat -> oat
can think of unit as the equivalent to void in C-style lansince a function of this type is implicitly curried by F#,
guages.
there is a two step process to resolve the function when
called with two arguments

5.2.2

How To Read Arrow Notation

All functions and values in F# have a data type. Open F#


Interactive and type the following:
> let addAndMakeString x y = (x + y).ToString();;
F# reports the data type using chained arrow notation as
follows:
val addAndMakeString : x:int -> y:int -> string

1. a function is called with the rst argument that returns a function that takes a oat and returns a
oat. To help clarify currying, lets call this function
funX (note that this naming is just for illustration
purposes--the function that gets created by the runtime is anonymous).
2. the second function ('funX' from step 1 above) is
called with the second argument, returning a oat

5.2. DECLARING FUNCTIONS


So, if you provide two oats, the result appears as if the
function takes two arguments, though this is not actually
how the runtime behaves. The concept of currying will
probably strike a developer not steeped in functional concepts as very strange and non-intuitive--even needlessly
redundant and inecient, so before attempting a further
explanation, consider the benets of curried functions via
an example:

13

is actually (when taking into consideration the implicit


currying):
// curried version pseudo-code f: int -> (int -> int)
In other words, f is a function that takes an int and returns
a function that takes an int and returns an int. Moreover,

let addTwoNumbers x y = x + y

f: int -> int -> int -> int

this type has the signature of

is a simplied shorthand for

int -> int -> int

// curried version pseudo-code f: int -> (int -> (int -> int))

then this function:


let add5ToNumber = addTwoNumbers 5

or, in very dicult to decode English: f is a function that


takes an int and returns a function that takes an int that returns a function that takes an int and returns an int. Yikes!

with the type signature of (int -> int). Note that the body
of add5ToNumber calls addTwoNumbers with only one
argument--not two. It returns a function that takes an
5.2.4 Nested Functions
int and returns an int. In other words, add5toNumber
partially applies the addTwoNumbers function.
F# allows programmers to nest functions inside other
> let z = add5ToNumber 6;; val z : int = 11
functions. Nested functions have a number of applications, such as hiding the complexity of inner loops:
This partial application of a function with multiple argument exemplies the power of curried functions. It allows
deferred application of the function, allowing for more
modular development and code re-use--we can re-use the
addTwoNumbers function to create a new function via
partial application. From this, you can glean the power
of function currying: it is always breaking down function
application to the smallest possible elements, facilitating
greater chances for code-reuse and modularity.

let sumOfDivisors n = let rec loop current max acc = if


current > max then acc else if n % current = 0 then loop
(current + 1) max (acc + current) else loop (current +
1) max acc let start = 2 let max = n / 2 (* largest factor,
apart from n, cannot be > n / 2 *) let minSum = 1 +
n (* 1 and n are already factors of n *) loop start max
minSum printfn "%d (sumOfDivisors 10) (* prints 18,
because the sum of 10s divisors is 1 + 2 + 5 + 10 = 18
*)

Take another example, illustrating the use of partially applied functions as a bookkeeping technique. Note the The outer function sumOfDivisors makes a call to the intype signature of holdOn is a function (int -> int) since ner function loop. Programmers can have an arbitrary
it is the partial application of addTwoNumbers
level of nested functions as need requires.
> let holdOn = addTwoNumbers 7;; val holdOn : (int ->
int) > let okDone = holdOn 8;; val okDone : int = 15

5.2.5 Generic Functions

Here we dene a new function holdOn on the y just to


keep track of the rst value to add. Then later we apply this new 'temp' function holdOn with another value
which returns an int. Partially applied functions--enabled
by currying--is a very powerful means of controlling complexity in F#. In short, the reason for the indirection resulting from currying function calls aords partial function application and all the benets it supplies. In other
words, the goal of partial function application is enabled
by implicit currying.

In programming, a generic function is a function that


returns an indeterminate type t without sacricing type
safety. A generic type is dierent from a concrete type
such as an int or a string; a generic type represents a type
to be specied later. Generic functions are useful because
they can be generalized over many dierent types.

So while the Arrow Notation is a good shorthand for understanding the type signature of a function, it does so
at the price of oversimplication, for a function with the
type signature of

F# derives type information of variables from the way


variables are used in an application, but F# can't constrain
the value x to any particular concrete type, so F# generalizes x to the generic type 'a:

f : int -> int -> int

'a -> int

Lets examine the following function:


let giveMeAThree x = 3

14

CHAPTER 5. VALUES AND FUNCTIONS


this function takes a generic type 'a and returns
an int.

used with type int.

The error message is very explicit: The add function takes


When you call a generic function, the compiler substitutes two int parameters, but throwAwayFirstInput 5 this is a
a functions generic types with the data types of the values string has the return type string, so we have a type mispassed to the function. As a demonstration, lets use the match.
following function:
Later chapters will demonstrate how to use generics in
creative and interesting ways.
let throwAwayFirstInput x y = y
Which has the type 'a -> 'b -> 'b, meaning that the function takes a generic 'a and a generic 'b and returns a 'b.
Here are some sample inputs and outputs in F# interactive:
> let throwAwayFirstInput x y = y;; val throwAwayFirstInput : 'a -> 'b -> 'b > throwAwayFirstInput 5
value";; val it : string = value > throwAwayFirstInput
thrownAway 10.0;; val it : oat = 10.0 > throwAwayFirstInput 5 30;; val it : int = 30
throwAwayFirstInput 5 value calls the function with an
int and a string, which substitutes int for 'a and string for
'b. This changes the data type of throwAwayFirstInput to
int -> string -> string.
throwAwayFirstInput thrownAway 10.0 calls the function with a string and a oat, so the functions data type
changes to string -> oat -> oat.
throwAwayFirstInput 5 30 just happens to call the function with two ints, so the functions data type is incidentally int -> int -> int.
Generic functions are strongly typed. For example:
let throwAwayFirstInput x y = y let add x y = x + y let z
= add 10 (throwAwayFirstInput this is a string 5)
The generic function throwAwayFirstInput is dened
again, then the add function is dened and it has the type
int -> int -> int, meaning that this function must be called
with two int parameters.
Then throwAwayFirstInput is called, as a parameter to
add, with two parameters on itself, the rst one of type
string and the second of type int. This call to throwAwayFirstInput ends up having the type string -> int -> int.
Since this function has the return type int, the code works
as expected:
> add 10 (throwAwayFirstInput this is a string 5);; val
it : int = 15
However, we get an error when we reverse the order of
the parameters to throwAwayFirstInput:
> add 10 (throwAwayFirstInput 5 this is a string);; add
10 (throwAwayFirstInput 5 this is a string);; -----------------------------^^^^^^^^^^^^^^^^^^^ stdin(13,31):
error FS0001: This expression has type string but is here

Chapter 6

Pattern Matching Basics


Pattern matching is used for control ow; it allows programmers to look at a value, test it against a series of
conditions, and perform certain computations depending
on whether that condition is met. While pattern matching
is conceptually similar to a series of if ... then statements
in other languages, F#'s pattern matching is much more
exible and powerful.

6.0.6

6.0.7 Alternative Pattern Matching Syntax


Pattern matching is such a fundamental feature that F#
has a shorthand syntax for writing pattern matching functions using the function keyword:
let something = function | test1 -> value1 | test2 -> value2
| test3 -> value3
It may not be obvious, but a function dened in this way
actually takes a single input. Heres a trivial example of
the alternative syntax:

Pattern Matching Syntax

In high level terms, pattern matching resembles this:


match expr with | pat1 -> result1 | pat2 -> result2 | pat3
when expr2 -> result3 | _ -> defaultResult

let getPrice = function | banana -> 0.79 | watermelon


-> 3.49 | tofu -> 1.09 | _ -> nan (* nan is a special
value meaning not a number *)

Although it doesn't appear as if the function takes any


parameters, it actually has the type string -> oat, so it
Each | denes a condition, the -> means if the condition
takes a single string parameter and returns a oat. You
is true, return this value.... The _ is the default pattern,
call this function in exactly the same way that you'd call
meaning that it matches anything, sort of like a wildcard.
any other function:
Using a real example, its easy to calculate the nth Fi> getPrice tofu";; val it : oat = 1.09 > getPrice
bonacci number using pattern matching syntax:
banana";; val it : oat = 0.79 > getPrice apple";; val it :
let rec b n = match n with | 0 -> 0 | 1 -> 1 | _ -> b (n - oat = nan
1) + b (n - 2)
We can experiment with this function in fsi:
b 1;; val it : int = 1 > b 2;; val it : int = 1 > b 5;; val it
: int = 5 > b 10;; val it : int = 55
Comparison To Other Languages
Its possible to chain together multiple conditions which
return the same value. For example:
> let greeting name = match name with | Steve |
Kristina | Matt -> Hello!" | Carlos | Maria ->
Hola!" | Worf -> nuqneH!" | Pierre | Monique
-> Bonjour!" | _ -> DOES NOT COMPUTE!";; val
greeting : string -> string > greeting Monique";; val it
: string = Bonjour!" > greeting Pierre";; val it : string
= Bonjour!" > greeting Kristina";; val it : string =
Hello!" > greeting Sakura";; val it : string = DOES
NOT COMPUTE!"

F#'s pattern matching syntax is subtly dierent from


switch statement structures in imperative languages,
because each case in a pattern has a return value. For
example, the b function is equivalent to the following
C#:
int b(int n) { switch(n) { case 0: return 0; case 1: return
1; default: return b(n - 1) + b(n - 2); } }
Like all functions, pattern matches can only have one return type.

15

16

6.0.8

CHAPTER 6. PATTERN MATCHING BASICS

Binding Variables
Matching

with

Pattern case from ever being matched), or writing a pattern which


doesn't match all possible inputs. F# is smart enough to
notify the programmer of these types of errors.

Pattern matching is not a fancy syntax for a switch struc- Example With Incomplete Pattern Matches
ture found in other languages, because it does not necessarily match against values, it matches against the shape > let getCityFromZipcode zip = match zip with | 68528
-> Lincoln, Nebraska | 90210 -> Beverly Hills,
of data.
California";; match zip with ----------^^^^ stdin(12,11):
F# can automatically bind values to identiers if they warning FS0025: Incomplete pattern matches on this
match certain patterns. This can be especially useful expression. For example, the value '0' will not be
when using the alternative pattern matching syntax, for matched val getCityFromZipcode : int -> string
example:
let rec factorial = function | 0 | 1 -> 1 | n -> n * factorial While this code is valid, F# informs the programmer of
(n - 1)
the possible error. F# warns us for a reason:
> getCityFromZipcode 68528;; val it : string = LinThe variable n is a pattern. If the factorial function is coln, Nebraska > getCityFromZipcode 32566;; Micalled with a 5, the 0 and 1 patterns will fail, but the last crosoft.FSharp.Core.MatchFailureException: Exception
pattern will match and bind the value to the identier n. of type 'Microsoft.FSharp.Core.MatchFailureException'
was thrown. at FSI_0018.getCityFromZipcode(Int32
Note to beginners: variable binding in patzip) at <StartupCode$FSI_0020>.$FSI_0020._main()
tern matching often looks strange to beginstopped due to error
ners, however it is probably the most powerful
and useful feature of F#. Variable binding is
F# will throw an exception if a pattern isn't matched. The
used to decompose data structures into compoobvious solution to this problem is to write patterns which
nent parts and allow programmers to examine
are complete.
each part; however, data structure decomposiOn occasions when a function genuinely has a limited
tion is too advanced for most F# beginners, and
range of inputs, its best to adopt this style:
the concept is dicult to express using simple
types like ints and strings. This book will dislet apartmentPrices numberOfRooms = match numcuss how to decompose data structures using
berOfRooms with | 1 -> 500.0 | 2 -> 650.0 | 3 -> 700.0
pattern matching in later chapters.
| _ -> failwith Only 1-, 2-, and 3- bedroom apartments
available at this complex
Using Guards within Patterns
This function now matches any possible input, and will
Occasionally, its not enough to match an input against a fail with an explanatory informative error message on inparticular value; we can add lters, or guards, to patterns valid inputs (this makes sense, because who would rent a
using the when keyword:
negative 42 bedroom apartment?).
let sign = function | 0 -> 0 | x when x < 0 -> 1 | x when Example With Unmatched Patterns
x > 0 -> 1
> let greeting name = match name with | Steve ->
Hello!" | Carlos -> Hola!" | _ -> DOES NOT
The function above returns the sign of a number: 1 for COMPUTE!" | Pierre -> Bonjour";; | Pierre
negative numbers, +1 for positive numbers, and '0' for 0: -> Bonjour";; ------^^^^^^^^^ stdin(22,7): warning
> sign 55;; val it : int = 1 > sign 108;; val it : int = 1 FS0026: This rule will never be matched. val greeting :
string -> string
> sign 0;; val it : int = 0
Variable binding is useful because its often required to Since the pattern _ matches anything, and since F# evaluates patterns from top to bottom, its not possible for the
implement guards.
code to ever reach the pattern Pierre.

6.0.9

Pay Attention to F# Warnings

Here is a demonstration of this code in fsi:

> greeting Steve";; val it : string = Hello!" > greeting


Note that F#'s pattern matching works from top to bot- Ino";; val it : string = DOES NOT COMPUTE!"
tom: it tests a value against each pattern, and returns the > greeting Pierre";; val it : string = DOES NOT
value of the rst pattern which matches. It is possible for COMPUTE!"
programmers to make mistakes, such as placing a general
case above a specic (which would prevent the specic

17
The rst two lines return the correct output, because
we've dened a pattern for Steve and nothing for Ino.
However, the third line is wrong. We have an entry for
Pierre, but F# never reaches it. The best solution to this
problem is to deliberately arrange the order of conditions
from most specic to most general.
Note to beginners: The code above contains an
error, but it will not throw an exception. These
are the worst kinds of errors to have, much
worse than an error which throws an exception
and crashes an app, because this error puts our
program in an invalid state and silently continues on its way. An error like this might occur early in a programs life cycle, but may not
show its eects for a long time (it could take
minutes, days, or weeks before someone notices the buggy behavior). Ideally, we want
buggy behavior to be as close to its source as
possible, so if a program enters an invalid state,
it should throw an exception immediately.

Chapter 7

Recursion
A recursive function is a function which calls itself. Interestingly, in contrast to many other languages, functions
in F# are not recursive by default. A programmer needs
to explicitly mark a function as recursive using the rec
keyword:
let rec someFunction = ...

The greatest common divisor, or GCD function, calculates the largest integer number which evenly divides two
other integers. For example, largest number that evenly
divides 259 and 111 is 37, denoted GCD(259, 111) = 37.
Euclid discovered a remarkably simple recursive algorithm for calculating the GCD of two numbers:
{
x
if y = 0
gcd(x, y) =
gcd(y, remainder(x, y)) if x >= y and y > 0

7.1 Examples
7.1.1

7.1.2 Greatest Common Divisor (GCD)

To calculate this by hand, we'd write:


gcd(259, 111) = gcd(111, 259 % 111) = gcd(111, 37) =
gcd(37, 111 % 37) = gcd(37, 0) = 37

Factorial in F#

In F#, we can use the % (modulus) operator to calculate


The factorial of a non-negative integer n, denoted by n!, the remainder of two numbers, so naturally we can dene
is the product of all positive integers less than or equal to the GCD function in F# as follows:
n. For example, 6! = 6 * 5 * 4 * 3 * 2 * 1 = 720.
open System let rec gcd x y = if y = 0 then x else gcd y
In mathematics, the factorial is dened as follows:
(x % y) Console.WriteLine(gcd 259 111) // prints 37
{
1
if n = 0
f act(n) =
n f act(n 1) if n > 0

7.2 Tail Recursion

Lets say we have a function A which, at some point, calls


Naturally, we'd calculate a factorial by hand using the fol- function B. When B nishes executing, the CPU must
lowing:
continue executing A from the point where it left o. To
fact(6) = = 6 * fact(6 - 1) = 6 * 5 * fact(5 - 1) = 6 * 5 * remember where to return, the function A passes a re4 * fact(4 - 1) = 6 * 5 * 4 * 3 * fact(3 - 1) = 6 * 5 * 4 * turn address as an extra argument to B on the stack; B
3 * 2 * fact(2 - 1) = 6 * 5 * 4 * 3 * 2 * 1 * fact(1 - 1) = jumps back to the return address when it nishes executing. This means calling a function, even one that doesn't
6 * 5 * 4 * 3 * 2 * 1 * 1 = 720
take any parameters, consumes stack space, and its exIn F#, the factorial function can be written concisely as tremely easy for a recursive function to consume all of
follows:
the available memory on the stack.
let rec fact x = if x < 1 then 1 else x * fact (x - 1)
A tail recursive function is a special case of recursion in
Heres a complete program:
open System let rec fact x = if x < 1 then 1 else
x * fact (x - 1) (* // can also be written using pattern matching syntax: let rec fact = function | n when n
< 1 -> 1 | n -> n * fact (n - 1) *) Console.WriteLine(fact 6)

which the last instruction executed in the method is the


recursive call. F# and many other functional languages
can optimize tail recursive functions; since no extra work
is performed after the recursive call, there is no need for
the function to remember where it came from, and hence
no reason to allocate additional memory on the stack.
F# optimizes tail-recursive functions by telling the CLR

18

7.3. EXERCISES

19

to drop the current stack frame before executing the target > let rec slowMultiply a b = if b > 1 then a + slowMultiply
function. As a result, tail-recursive functions can recurse a (b - 1) else a;; val slowMultiply : int -> int -> int >
indenitely without consuming stack space.
slowMultiply 3 9;; val it : int = 27 > slowMultiply 2
14;; val it : int = 28 > slowMultiply 1 100000;; Process
Heres non-tail recursive function:
is terminated due to StackOverowException. Session
> let rec count n = if n = 1000000 then printfn done termination detected. Press Enter to restart.
else if n % 1000 = 0 then printfn n: %i n count (n +
1) (* recursive call *) () (* <-- This function is not tail
recursive because it performs extra work (by returning Its possible to re-write most recursive functions into their
unit) after the recursive call is invoked. *);; val count tail-recursive forms using an accumulating parameter:
: int -> unit > count 0;; n: 0 n: 1000 n: 2000 n: 3000 > let slowMultiply a b = let rec loop acc counter = if
... n: 58000 n: 59000 Session termination detected. counter > 1 then loop (acc + a) (counter - 1) (* tail
Press Enter to restart. Process is terminated due to recursive *) else acc loop a b;; val slowMultiply : int
StackOverowException.
-> int -> int > slowMultiply 3 9;; val it : int = 27 >
slowMultiply 2 14;; val it : int = 28 > slowMultiply 1
Lets see what happens if we make the function properly 100000;; val it : int = 100000
tail-recursive:
> let rec count n = if n = 1000000 then printfn done The accumulator parameter in the inner loop holds the
state of our function throughout each recursive iteration.
else if n % 1000 = 0 then printfn n: %i n count (n +
1) (* recursive call *);; val count : int -> unit > count 0;;
n: 0 n: 1000 n: 2000 n: 3000 n: 4000 ... n: 995000 n:
996000 n: 997000 n: 998000 n: 999000 done
7.3 Exercises
If there was no check for n = 1000000, the function would Solutions.
run indenitely. Its important to ensure that all recursive
function have a base case to ensure they terminate eventually.

7.3.1 Faster Fib Function

7.2.1

The following function calculates the nth number in the

How to Write Tail-Recursive Func- Fibonacci sequence:


tions

let rec b = function | n when n=0I -> 0I | n when n=1I


Lets imagine that, for our own amusement, we wanted to -> 1I | n -> b(n - 1I) + b(n - 2I)
implement a multiplication function in terms of the more
fundamental function of addition. For example, we know
Note: The function above has the type val b
that 6 * 4 is the same as 6 + 6 + 6 + 6, or more generally
: bigint -> bigint. Previously, we've been uswe can dene multiplication recursively as M(a, b) = a +
ing the int or System.Int32 type to represent
M(a, b - 1), b > 1. In F#, we'd write this function as:
numbers, but this type has a maximum value
let rec slowMultiply a b = if b > 1 then a + slowMultiply
of 2,147,483,647. The type bigint is used for
a (b - 1) else a
arbitrary size integers such as integers with billions of digits. The maximum value of bigint is
It may not be immediately obvious, but this function is
constrained only by the available memory on a
not tail recursive. It might be more obvious if we rewrote
users machine, but for most practical computthe function as follows:
ing purposes we can say this type is boundless.
let rec slowMultiply a b = if b > 1 then let intermediate
= slowMultiply a (b - 1) (* recursion *) let result = a The function above is neither tail-recursive nor partic+ intermediate (* <-- additional operations *) result else a ularly ecient with a computational complexity O(2n ).
The tail-recursive form of this function has a computaThe reason it is not tail recursive is because after the re- tional complexity of O(n). Re-write the function above
cursive call to slowMultiply, the result of the recursion so that its tail recursive.
has to added to a. Remember tail recursion needs the last You can verify the correctness of your function using the
operation to be the recursion.
following:
Since the slowMultiply function isn't tail recursive, it b(0I) = 0 b(1I) = 1 b(2I) = 1 b(3I) = 2
throws a StackOverFlowException for inputs which re- b(4I) = 3 b(5I) = 5 b(10I) = 55 b(100I) =
sult in very deep recursion:
354224848179261915075

20

7.4 Additional Reading


Understanding Tail Recursion
How can I implement a tail-recursive append?

CHAPTER 7. RECURSION

Chapter 8

Higher Order Functions


A higher-order function is a function that takes another passFive has the return type 'a because we don't know the
function as a parameter, or a function that returns another return type of f 5 in advance.
function as a value, or a function which does both.
open System let square x = x * x let cube x = x * x *
x let sign x = if x > 0 then positive else if x < 0 then
negative else zero let passFive f = (f 5) printfn "%A
8.0.1 Familiar Higher Order Functions
(passFive square) // 25 printfn "%A (passFive cube) //
To put higher order functions in perspective, if you've 125 printfn "%A (passFive sign) // positive
ever taken a rst-semester course on calculus, you're undoubtedly familiar with two functions: the limit function These functions have the following types:
and the derivative function.
val square : int -> int val cube : int -> int val sign : int ->
The limit function is dened as follows:
string val passFive : (int -> 'a) -> 'a
Unlike many other languages, F# makes no distinction
between functions and values. We pass functions to other
functions in the exact same way that we pass ints, strings,
The limit function, lim, takes another function f(x) as a and other values.
parameter, and it returns a value L to represent the limit.
lim f (x) = L

xp

Similarly, the derivative function is dened as follows:


f (a + h) f (a)
= f (x)
h0
h

deriv(f (x)) = lim

Creating a Map Function


A map function converts one type of data to another type
of data. A simple map function in F# looks like this:

let map item converter = converter item


The derivative function, deriv, takes a function f(x) as a
parameter, and it returns a completely dierent function
This has the type val map : 'a -> ('a -> 'b) -> 'b. In other
f'(x) as a result.
words, map takes a two parameters: an item 'a, and a
In this respect, we can correctly assume the limit and
function that takes an 'a and returns a 'b; map returns a 'b.
derivative functions are higher-order functions. If we
have a good understanding of higher-order functions in Lets examine the following code:
mathematics, then we can apply the same principles in open System let map x f = f x let square x = x * x
F# code.
let cubeAndConvertToString x = let temp = x * x * x
In F#, we can pass a function to another function just as temp.ToString() let answer x = if x = true then yes
if it was a literal value, and we call it just like we call any else no let rst = map 5 square let second = map 5
other function. For example, heres a very trivial function: cubeAndConvertToString let third = map true answer
let passFive f = (f 5)
These functions have the following signatures:
In F# notation, passFive has the following type:
val passFive : (int -> 'a) -> 'a

val map : 'a -> ('a -> 'b) -> 'b val square : int -> int val
cubeAndConvertToString : int -> string val answer :
bool -> string val rst : int val second : string val third :
string

In other words, passFive takes a function f, where f must


take an int and return any generic type 'a. Our function The rst function passes a datatype int and a function with
21

22

CHAPTER 8. HIGHER ORDER FUNCTIONS

the signature (int -> int); this means the placeholders 'a The advantage of doing composition using the >> operand 'b in the map function both become ints.
ator is that the functions in the composition are listed in
The second function passes a datatype int and a function the order in which they are called.
(int -> string), and map predictably returns a string.

let gof = f >> g

The third function passes a datatype bool and a function


(bool -> string), and map returns a string just as we ex- This will rst apply f and then apply g on the result.
pect.
Since our generic code is typesafe, we would get an error
8.0.2
if we wrote:

The |> Operator

let fourth = map true square

The pipeline operator, |>, is one of the most important


operators in F#. The denition of the pipeline operator is
Because the true constrains our function to a type (bool remarkably simple:
-> 'b), but the square function has the type (int -> int), so let inline (|>) x f = f x
its obviously incorrect.
Lets take 3 functions:
The Composition Function (<< operator)

let square x = x * x let add x y = x + y let toString x =


x.ToString()

In algebra, the composition function is dened as compose(f, g, x) = f(g(x)), denoted f o g. In F#, the compo- Lets also say we had a complicated function which
sition function is dened as follows:
squared a number, added ve to it, and converted it to
a string? Normally, we'd write this:
let inline (<<) f g x = f (g x)
let complexFunction x = toString (add 5 (square x))
Which has the somewhat cumbersome signature val << :
('b -> 'c) -> ('a -> 'b) -> 'a -> 'c.
We can improve the readability of this function somewhat
using the pipeline operator:
If I had two functions:
let complexFunction x = x |> square |> add 5 |> toString
f(x) = x^2
g(x) = -x/2 + 5
And I wanted to model f o g, I could write:
open System let f x = x*x let g x = -x/2.0 + 5.0 let
fog = f << g Console.WriteLine(fog 0.0) // 25 Console.WriteLine(fog 1.0) // 20.25 Console.WriteLine(fog
2.0) // 16 Console.WriteLine(fog 3.0) // 12.25 Console.WriteLine(fog 4.0) // 9 Console.WriteLine(fog 5.0)
// 6.25

x is piped to the square function, which is piped to add 5


method, and nally to the toString method.

8.0.3 Anonymous Functions


Until now, all functions shown in this book have been
named. For example, the function above is named add.
F# allows programmers to declare nameless, or anonymous functions using the fun keyword.

let complexFunction = 2 (* 2 *) |> ( fun x -> x


+ 5) (* 2 + 5 = 7 *) |> ( fun x -> x * x) (* 7 * 7 =
Note that fog doesn't return a value, it returns another
49 *) |> ( fun x -> x.ToString() ) (* 49.ToString = 49 *)
function whose signature is (oat -> oat).
Of course, theres no reason why the compose function
Anonymous functions are convenient and nd a use in a
needs to be limited to numbers; since its generic, it can
surprising number of places.
work with any datatype, such as int arrays, tuples, strings,
and so on.
There also exists the >> operator, which similarly per- A Timer Function
forms function composition, but in reverse order. It is
open System let duration f = let timer = new
dened as follows:
System.Diagnostics.Stopwatch()
timer.Start()
let
let inline (>>) f g x = g (f x)
returnValue = f() printfn Elapsed Time:
%i
timer.ElapsedMilliseconds returnValue let rec b =
This operators signature is as follows: val >> : ('a -> 'b) function | 0 -> 0 | 1 -> 1 | n -> b (n - 1) + b (n - 2)
-> ('b -> 'c) -> 'a -> 'c.
let main() = printfn b 5: %i (duration ( fun() -> b 5

23
)) printfn b 30: %i (duration ( fun() -> b 30 )) main() the rest of its arguments, with all occurrences of x being
replaced by the argument 5.
The duration function has the type val duration : (unit -> Currying is built on the principle that each argument ac'a) -> 'a. This program prints:
tually returns a separate function, which is why calling a
function with only part of its parameters returns another
Elapsed Time: 1 b 5: 5 Elapsed Time: 5 b 30: 832040
function. The familiar F# syntax that we've seen so far,
let add x y = x + y, is actually a kind of syntatic sugar for
Note: the actual duration to execute these functhe explicit currying style shown above.
tions will vary from machine to machine.

8.0.4

Currying and Partial Functions

Two Pattern Matching Syntaxes

You may have wondered why there are two pattern


A fascinating feature in F# is called currying, which matching syntaxes:
means that F# does not require programmers to provide
all of the arguments when calling a function. For exam- Both snippets of code are identical, but why does the
shortcut syntax allow programmers to omit the food paple, lets say we have a function:
rameter in the function denition? The answer is related
let add x y = x + y
to currying: behind the scenes, the F# compiler converts
the function keyword into the following construct:
add takes two integers and returns another integer. In F# let getPrice2 = (fun x -> match x with | banana -> 0.79
notation, this is written as val add : int -> int -> int
| watermelon -> 3.49 | tofu -> 1.09 | _ -> nan)
We can dene another function as follows:
In other words, F# treats the function keyword as an
anonymous function that takes one parameter and returns
one value. The getPrice2 function actually returns an
The addFive calls the add function with one its parameanonymous function; arguments passed to getPrice2 are
ters, so what is the return value of this function? Thats
actually applied and evaluated by the anonymous function
easy: addFive returns another function which is waiting
instead.
for the rest of its arguments. In this case, addFive returns a function that takes an int and returns another int,
denoted in F# notation as val addFive : (int -> int).
let addFive = add 5

You call addFive just in the same way that you call other
functions:
open System let add x y = x + y let addFive = add 5
Console.WriteLine(addFive 12) // prints 17

How Currying Works


The function let add x y = x + y has the type val add
: int -> int -> int. F# uses the slightly unconventional
arrow notation to denote function signatures for a reason:
arrows notation is intrinsically connected to currying and
anonymous functions. Currying works because, behind
the scenes, F# converts function parameters to a style that
looks like this:
let add = (fun x -> (fun y -> x + y) )
The type int -> int -> int is semantically equivalent to (int
-> (int -> int)).
When you call add with no arguments, it returns fun x ->
fun y -> x + y (or equivalently fun x y -> x + y), another
function waiting for the rest of its arguments. Likewise,
when you supply one argument to the function above, say
5, it returns fun y -> 5 + y, another function waiting for

Chapter 9

Option Types
An option type can hold two possible values: Some(x) the CLR System.Nullable<T> representation in IL due
or None. Option types are frequently used to represent to dierences in semantics.
optional values in calculations, or to indicate whether a
particular computation has succeeded or failed.

9.2 Pattern
Types

9.1 Using Option Types

Matching

Option

Pattern matching option types is as easy as creating them:


Lets say we have a function that divides two integers. the same syntax used to declare an option type is used to
match option types:
Normally, we'd write the function as follows:
> let isFortyTwo = function | Some(42) -> true | Some(_)
| None -> false;; val isFortyTwo : int option -> bool > isFortyTwo (Some(43));; val it : bool = false > isFortyTwo
This function works just ne, but its not safe: its possible
(Some(42));; val it : bool = true > isFortyTwo None;; val
to pass an invalid value into this function which results in
it : bool = false
a runtime error. Here is a demonstration in fsi:
let div x y = x / y

> let div x y = x / y;; val div : int -> int -> int
> div 10 5;; val it : int = 2 > div 10 0;; System.DivideByZeroException: Attempted to divide by
zero. at <StartupCode$FSI_0035>.$FSI_0035._main()
stopped due to error

9.3 Other Functions in the Option


Module

div 10 5 executes just ne, but div 10 0 throws a division val get : 'a option -> 'a
by zero exception.
Returns the value of a Some option.
Using option types, we can return Some(value) on a successful calculation, or None if the calculation fails:
val isNone : 'a option -> bool
> let safediv x y = match y with | 0 -> None | _ ->
Some(x/y);; val safediv : int -> int -> int option > safediv
Returns true for a None option, false otherwise.
10 5;; val it : int option = Some 2 > safediv 10 0;; val it :
int option = None
val isSome : 'a option -> bool
Notice an important dierence between our div and safediv functions:

Returns true for a Some option, false otherwise.

val div : int -> int -> int val safediv : int -> int -> int option
val map : ('a -> 'b) -> 'a option -> 'b option
div returns an int, while safediv returns an int option.
Since our safediv function returns a dierent data type,
it informs clients of our function that the application has
entered an invalid state.

Given None, returns None. Given Some(x), returns Some(f x), where f is the given mapping
function.

Option types are conceptually similar to nullable types in


languages like C#, however F# option types do not use val iter : ('a -> unit) -> 'a option -> unit
24

9.3. OTHER FUNCTIONS IN THE OPTION MODULE


Applies the given function to the value of a
Some option, does nothing otherwise.

25

Chapter 10

Tuples and Records


10.0.1

Dening Tuples

ber of arguments used to dene a tuple. For example, an


int * string tuple is made up of two parts, so it has an arity
of 2, a string * string * oat has an arity of 3, and so on.

A tuple is dened as a comma separated collection of values. For example, (10, hello) is a 2-tuple with the type
(int * string). Tuples are extremely useful for creating ad Pattern Matching Tuples
hoc data structures which group together related values.
Pattern matching on tuples is easy, because the same synlet average (a, b) = (a + b) / 2.0
tax used to declare tuple types is also used to match tuples.
This function has the type oat * oat -> oat, it takes a
Example 1
oat * oat tuple and returns another oat.

> let average (a, b) = let sum = a + b sum / 2.0;; val Lets say that we have a function greeting that prints out a
average : oat * oat -> oat > average (10.0, 20.0);; val custom greeting based on the specied name and/or language.
it : oat = 15.0
let greeting (name, language) = match (name, lanNotice that a tuple is considered a single argument. As a guage) with | (Steve, _) -> Howdy, Steve | (name,
English) -> Hello, " + name | (name, _) when
result, tuples can be used to return multiple values:
language.StartsWith(Span) -> Hola, " + name | (_,
Example 1 - a function which multiplies a 3-tuple by a French) -> Bonjour!" | _ -> DOES NOT COMscalar value to return another 3-tuple.
PUTE
> let scalarMultiply (s : oat) (a, b, c) = (a * s, b * s, c
* s);; val scalarMultiply : oat -> oat * oat * oat -> This function has type string * string -> string, meaning
oat * oat * oat > scalarMultiply 5.0 (6.0, 10.0, 20.0);;
that it takes a 2-tuple and returns a string. We can test
val it : oat * oat * oat = (30.0, 50.0, 100.0)
this function in fsi:
> greeting (Steve, English);; val it : string =
Example 2 - a function which reverses the input of what- Howdy, Steve > greeting (Pierre, French);; val it
ever is passed into the function.
: string = Bonjour!" > greeting (Maria, Spanish);;
> let swap (a, b) = (b, a);; val swap : 'a * 'b -> 'b * 'a > val it : string = Hola, Maria > greeting (Rocko,
swap (Web, 2.0);; val it : oat * string = (2.0, Web) Esperanto);; val it : string = DOES NOT COMPUTE
> swap (20, 30);; val it : int * int = (30, 20)
Example 2
Example 3 - a function which divides two numbers and We can conveniently match against the shape of a tuple
returns the remainder simultaneously.
using the alternative pattern matching syntax:
> let divrem x y = match y with | 0 -> None | _ -> Some(x > let getLocation = function | (0, 0) -> origin | (0, y)
/ y, x % y);; val divrem : int -> int -> (int * int) option -> on the y-axis at y=" + y.ToString() | (x, 0) -> on
> divrem 100 20;; (* 100 / 20 = 5 remainder 0 *) val it the x-axis at x=" + x.ToString() | (x, y) -> at x=" +
: (int * int) option = Some (5, 0) > divrem 6 4;; (* 6 / x.ToString() + ", y=" + y.ToString() ;; val getLocation :
4 = 1 remainder 2 *) val it : (int * int) option = Some int * int -> string > getLocation (0, 0);; val it : string =
(1, 2) > divrem 7 0;; (* 7 / 0 throws a DivisionByZero origin > getLocation (0, 1);; val it : string = on the
exception *) val it : (int * int) option = None
y-axis at y=1 > getLocation (5, 10);; val it : string
= at x=5, y=10 > getLocation (7, 0);; val it : string =
Every tuple has a property called arity, which is the num- on the x-axis at x=7
26

27
> System.Int32.TryParse(3);; val it : bool * int = (true,
3) > System.Math.DivRem(10, 7);; val it : int * int = (1,
3)
fst and snd
F# has two built-in functions, fst and snd, which return
the rst and second items in a 2-tuple. These functions 10.0.2 Dening Records
are dened as follows:
A record is similar to a tuple, except it contains named
let fst (a, b) = a let snd (a, b) = b
elds. A record is dened using the syntax:
They have the following types:

type recordName = { [ eldName : dataType ] + }

val fst : 'a * 'b -> 'a val snd : 'a * 'b -> 'b
Here are a few examples in FSI:

+ means the element must occur one or more


times.

> fst (1, 10);; val it : int = 1 > snd (1, 10);; val it : int =
10 > fst (hello, world);; val it : string = hello > snd Heres a simple record:
(hello, world);; val it : string = world > fst (Web, type website = { Title : string; Url : string }
2.0);; val it : string = Web > snd (50, 100);; val it : int
= 100
Unlike a tuple, a record is explicitly dened as its own
type using the type keyword, and record elds are dened
as a semicolon-separated list. (In many ways, a record can
be thought of as a simple class.)
Assigning Multiple Variables Simultaneously
A website record is created by specifying the records
Tuples can be used to assign multiple values simultane- elds as follows:
ously. The syntax for doing so is:
> let homepage = { Title = Google"; Url =
let val1, val2, ... valN = (expr1, expr2, ... exprN)
"http://www.google.com" };; val homepage : website
In other words, you assign a comma-separated list of N
values to an N-tuple. Heres an example in FSI:

Note that F# determines a records type by the name and


> let x, y = (1, 2);; val y : int val x : int > x;; val it : int = type of its elds, not the order that elds are used. For
example, while the record above is dened with Title rst
1 > y;; val it : int = 2
and Url second, its perfectly legitimate to write:
TiThe number of values being assigned must match the ar- > { Url = "http://www.microsoft.com/";
tle
=
Microsoft
Corporation
};;
val
it
:
webity of tuple returned from the function, otherwise F# will
site = {Title = Microsoft Corporation"; Url =
raise an exception:
"http://www.microsoft.com/";}
> let x, y = (1, 2, 3);; let x, y = (1, 2, 3);; -----------^^^^^^^^ stdin(18,13): error FS0001: Type mismatch.
Expecting a 'a * 'b but given a 'a * 'b * 'c. The tuples Its easy to access a records properties using dot notation:
have diering lengths of 2 and 3.
> let homepage = { Title = Wikibooks"; Url =
"http://www.wikibooks.org/" };; val homepage : website
> homepage.Title;; val it : string = Wikibooks > homepage.Url;; val it : string = "http://www.wikibooks.org/"
Tuples and the .NET Framework
From a point of view F#, all methods in the .NET Base
Class Library take a single argument, which is a tuple of Cloning Records
varying types and arity. For example:
Some methods, such as the System.Math.DivRem shown Records are immutable types, which means that instances
above, and others such as System.Int32.TryParse return of records cannot be modied. However, records can be
multiple through output variables. F# allows program- cloned conveniently using the clone syntax:
mers to omit an output variable; using this calling con- type coords = { X : oat; Y : oat } let setX item newX
vention, F# will return results of a function as a tuple, for = { item with X = newX }
example:

28

CHAPTER 10. TUPLES AND RECORDS

The method setX has the type coords -> oat -> coords. (0.000000, 0.000000) is in quadrant Origin (1.000000,
The with keyword creates a clone of item and set its X 1.000000) is in quadrant I (1.000000, 1.000000) is in
property to newX.
quadrant II (1.000000, 1.000000) is in quadrant III
> let start = { X = 1.0; Y = 2.0 };; val start : coords > let (1.000000, 1.000000) is in quadrant IV
nish = setX start 15.5;; val nish : coords > start;; val it
: coords = {X = 1.0; Y = 2.0;} > nish;; val it : coords =
{X = 15.5; Y = 2.0;}
Notice that the setX creates a copy of the record, it doesn't
actually mutate the original record instance.
Heres a more complete program:
type TransactionItem = { Name : string; ID : int;
ProcessedText : string; IsProcessed : bool } let getItem
name id = { Name = name; ID = id; ProcessedText
= null; IsProcessed = false } let processItem item = {
item with ProcessedText = Done"; IsProcessed = true
} let printItem msg item = printfn "%s: %A msg item
let main() = let preProcessedItem = getItem Steve 5
let postProcessedItem = processItem preProcessedItem
printItem preProcessed preProcessedItem printItem
postProcessed postProcessedItem main()
This program processes an instance of the TransactionItem class and prints the results. This program outputs
the following:
preProcessed: {Name = Steve"; ID = 5; ProcessedText
= null; IsProcessed = false;} postProcessed: {Name =
Steve"; ID = 5; ProcessedText = Done"; IsProcessed
= true;}

Pattern Matching Records


We can pattern match on records just as easily as tuples:
open System type coords = { X : oat; Y : oat } let
getQuadrant = function | { X = 0.0; Y = 0.0 } -> Origin
| item when item.X >= 0.0 && item.Y >= 0.0 -> I |
item when item.X <= 0.0 && item.Y >= 0.0 -> II |
item when item.X <= 0.0 && item.Y <= 0.0 -> III |
item when item.X >= 0.0 && item.Y <= 0.0 -> IV
let testCoords (x, y) = let item = { X = x; Y = y }
printfn "(%f, %f) is in quadrant %s x y (getQuadrant
item) let main() = testCoords(0.0, 0.0) testCoords(1.0,
1.0) testCoords(1.0, 1.0) testCoords(1.0, 1.0)
testCoords(1.0, 1.0) Console.ReadKey(true) |> ignore
main()
Note that pattern cases are dened with the same syntax
used to create a record (as shown in the rst case), or using
guards (as shown in the remaining cases). Unfortunately,
programmers cannot use the clone syntax in pattern cases,
so a case such as | { item with X = 0 } -> y-axis will not
compile.
The program above outputs:

Chapter 11

Lists
A list is an ordered collection of related values, and is
roughly equivalent to a linked list data structure used
in many other languages. F# provides a module, Microsoft.FSharp.Collections.List, for common operations
on lists; this module is imported automatically by F#, so
the List module is already accessible from every F# application.

11.1 Creating Lists


11.1.1

This operator does not actually mutate lists, it creates an


entirely new list with the prepended element in the front.
Heres an example in fsi:
> let x = 1 :: 2 :: 3 :: 4 :: [];; val x : int list > let y = 12 ::
x;; val y : int list > x;; val it : int list = [1; 2; 3; 4] > y;;
val it : int list = [12; 1; 2; 3; 4]
Consing creates a new list, but it reuses nodes from the
old list, so consing a list is an extremely ecient O(1)
operation.

Using List Literals


11.1.3 Using List.init

There are a variety of ways to create lists in F#, the most


straightforward method being a semicolon-delimited se- The List module contains a useful method, List.init,
quence of values. Heres a list of numbers in fsi:
which has the type
> let numbers = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10];; val numbers
: int list > numbers;; val it : int list = [1; 2; 3; 4; 5; 6; 7;
val init : int -> (int -> 'T) -> 'T list
8; 9; 10]
The rst argument is the desired length of the new list,
and the second argument is an initializer function which
> [1; 2; 3; 4; cat"; 6; 7];; -------------^^^^^^ generates items in the list. List.init is used as follows:
stdin(120,14): error FS0001: This expression has > List.init 5 (fun index -> index * 3);; val it : int list =
type string but is here used with type int.
[0; 3; 6; 9; 12] > List.init 5 (fun index -> (index, index
Notice that all values in a list must have the same type:

* index, index * index * index));; val it : (int * int * int)


list = [(0, 0, 0); (1, 1, 1); (2, 4, 8); (3, 9, 27); (4, 16, 64)]

11.1.2

Using the :: (cons) Operator

F# calls the initializer function 5 times with the index of


It is very common to build lists up by prepending or con- each item in the list, starting at index 0.
sing a value to an existing list using the :: operator:
> 1 :: 2 :: 3 :: [];; val it : int list = [1; 2; 3]

Note: the [] is an empty list. By itself, it has the


type 'T list; since its used with ints, it has the
type int list.

11.1.4 Using List Comprehensions


List comprehensions refers to special syntactic constructs
in some languages used for generating lists. F# has an
expressive list comprehension syntax, which comes in two
forms, ranges and generators.

The :: operator prepends items to a list, returning a new Ranges have the constructs [start .. end] and [start .. step
list. It is a right-associative operator with the following .. end]. For example:
type:
> [1 .. 10];; val it : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
> [1 .. 2 .. 10];; val it : int list = [1; 3; 5; 7; 9] > ['a' ..
val inline (::) : 'T -> 'T list -> 'T list
's];; val it : char list = ['a'; 'b'; 'c'; 'd'; 'e'; 'f'; 'g'; 'h'; 'i'; 'j';
29

30
'k'; 'l'; 'm'; 'n'; 'o'; 'p'; 'q'; 'r'; 's]

CHAPTER 11. LISTS

11.2 Pattern Matching Lists

Generators have the construct [for x in collection do ... You use the same syntax to match against lists that you
yield expr], and they are much more exible than ranges. use to create lists. Heres a simple program:
For example:
let rec sum total = function | [] -> total | hd :: tl -> sum
> [ for a in 1 .. 10 do yield (a * a) ];; val it : int list = [1; (hd + total) tl let main() = let numbers = [ 1 .. 5 ] let
4; 9; 16; 25; 36; 49; 64; 81; 100] > [ for a in 1 .. 3 do for sumOfNumbers = sum 0 numbers printfn sumOfNumb in 3 .. 7 do yield (a, b) ];; val it : (int * int) list = [(1, bers: %i sumOfNumbers main()
3); (1, 4); (1, 5); (1, 6); (1, 7); (2, 3); (2, 4); (2, 5); (2,
6); (2, 7); (3, 3); (3, 4); (3, 5); (3, 6); (3, 7)] > [ for a in The sum method has the type val sum : int -> int list 1 .. 100 do if a % 3 = 0 && a % 5 = 0 then yield a];; val > int. It recursively enumerates through the list, adding
it : int list = [15; 30; 45; 60; 75; 90]
each item in the list to the value total. Step by step, the
function works as follows:
Its possible to loop over any collection, not just numbers.
This example loops over a char list:

11.2.1 Reverse A List

> let x = [ 'a' .. 'f' ];; val x : char list > [for a in x do yield
[a; a; a] ];; val it : char list list = [['a'; 'a'; 'a']; ['b'; 'b'; 'b']; Frequently, we use recursion and pattern matching to gen['c'; 'c'; 'c']; ['d'; 'd'; 'd']; ['e'; 'e'; 'e']; ['f'; 'f'; 'f']]
erate new lists from existing lists. A simple example is
reversing a list:
Note that the yield keyword pushes a single value into let reverse l = let rec loop acc = function | [] -> acc | hd
a list. Another keyword, yield!, pushes a collection of :: tl -> loop (hd :: acc) tl loop [] l
values into the list. The yield! keyword is used as follows:
> [for a in 1 .. 5 do yield! [ a .. a + 3 ] ];; val it : int list =
[1; 2; 3; 4; 2; 3; 4; 5; 3; 4; 5; 6; 4; 5; 6; 7; 5; 6; 7; 8]
Its possible to mix the yield and yield! keywords:
> [for a in 1 .. 5 do match a with | 3 -> yield! ["hello";
world"] | _ -> yield a.ToString() ];; val it : string list =
["1"; 2"; hello"; world"; 4"; 5"]

Note to beginners: the pattern seen above is


very common. Often, when we iterate through
lists, we want to build up a new list. To do this
recursively, we use an accumulating parameter (which is called acc above) which holds our
new list as we generate it. Its also very common to use a nested function, usually named
innerXXXXX or loop, to hide the implementation details of the function from clients (in
other words, clients should not have to pass in
their own accumulating parameter).
reverse has the type val reverse : 'a list -> 'a list. You'd
use this function as follows:

Alternative List Comprehension Syntax

> reverse [1 .. 5];; val it : int list = [5; 4; 3; 2; 1]

The samples above use the yield keyword explicitly, how- This simple function works because items are always
ever F# provides a slightly dierent arrow-based syntax prepended to the accumulating parameter acc, resulting
in series of recursive calls as follows:
for list comprehensions:
> [ for a in 1 .. 5 -> a * a];; val it : int list = [1; 4; 9; 16; List.rev is a built-in function for reversing a list:
25] > [ for a in 1 .. 5 do for b in 1 .. 3 -> a, b];; val it : > List.rev [1 .. 5];; val it : int list = [5; 4; 3; 2; 1]
(int * int) list = [(1, 1); (1, 2); (1, 3); (2, 1); (2, 2); (2, 3);
(3, 1); (3, 2); (3, 3); (4, 1); (4, 2); (4, 3); (5, 1); (5, 2);
(5, 3)] > [ for a in 1 .. 5 ->> [1 .. 3] ];; val it : int list =
[1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3]
11.2.2 Filter A List
-> and ->> are equivalent to the yield and yield! operators respectively. While its still common to see list comprehensions expressed using -> and ->>, those constructs
will not be emphasized in this book since they have been
deprecated in favor of yield and yield!.

Oftentimes, we want to lter a list for certain values. We


can write a lter function as follows:
open System let rec lter predicate = function | [] -> []
| hd :: tl -> match predicate hd with | true -> hd::lter
predicate tl | false -> lter predicate tl let main() = let

11.3. USING THE LIST MODULE

31

lteredNumbers = [1 .. 10] |> lter (fun x -> x % 2 = 0) list = [2; 4; 6; 8; 10]


printfn lteredNumbers: %A lteredNumbers main()
List.map maps a list from one type to another:
The lter method has the type val lter : ('a -> bool) -> > [1 .. 10] |> List.map (fun x -> (x * x).ToString());; val
'a list -> 'a list. The program above outputs:
it : string list = ["1"; 4"; 9"; 16"; 25"; 36"; 49";
lteredNumbers: [2; 4; 6; 8; 10]
64"; 81"; 100"]
We can make the lter above tail-recursive with a slight
modication:
let lter predicate l = let rec loop acc = function | []
-> acc | hd :: tl -> match predicate hd with | true ->
loop (hd :: acc) tl | false -> loop (acc) tl List.rev (loop [] l) 11.3.1
Note: Since accumulating parameters often
build up lists in reverse order, its very common to see List.rev called immediately before
returning a list from a function to put it in correct order.

11.2.3

List.append and the @ Operator

List.append has the type:


val append : 'T list -> 'T list -> 'T list
As you can imagine, the append functions appends one
list to another. The @ operator is an inx operator which
performs the same function:

Mapping Lists

let rst = [1; 2; 3;] let second = [4; 5; 6;] let combined1
= rst @ second (* returns [1; 2; 3; 4; 5; 6] *) let
We can write a function which maps a list to another list: combined2 = List.append rst second (* returns [1; 2; 3;
open System let rec map converter = function | [] -> [] 4; 5; 6] *)
| hd :: tl -> converter hd::map converter tl let main()
= let mappedNumbers = [1 .. 10] |> map ( fun x -> Since lists are immutable, appending two lists together
(x * x).ToString() ) printfn mappedNumbers: %A requires copying all of the elements of the lists to create
mappedNumbers main()
a brand new list. However, since lists are immutable, its
only necessary to copy the elements of the rst list; the
map has the type val map : ('a -> 'b) -> 'a list -> 'b list. second list does not need to be copied. Represented in
memory, appending two lists can be diagrammed as folThe program above outputs:
lows:
mappedNumbers: ["1"; 4"; 9"; 16"; 25"; 36"; 49";
We start with the following:
64"; 81"; 100"]
rst = 1 :: 2 :: 3 :: [] second = 4 :: 5 :: 6 :: []
A tail-recursive map function can be written as:
let map converter l = let rec loop acc = function | [] -> Appending the two lists, rst @ second, results in the folacc | hd :: tl -> loop (converter hd :: acc) tl List.rev (loop lowing:
[] l)
rst = 1 :: 2 :: 3 :: [] \______ ______/ \/ combined = 1 ::
2 :: 3 :: second (copied)
Like the example above, we use the accumulating-param- In other words, F# prepends a copy of rst to second to
and-reverse pattern to make the function tail recursive.
create the combined list. This hypothesis can be veried
using the following in fsi:
> let rst = [1; 2; 3;] let second = [4; 5; 6;] let combined
= rst @ second let secondHalf = List.tail (List.tail
(List.tail combined));; val rst : int list val second : int
Although a reverse, lter, and map method were imple- list val combined : int list val secondHalf : int list >
mented above, its much more convenient to use F#'s built- System.Object.ReferenceEquals(second, secondHalf);;
val it : bool = true
in functions:

11.3 Using the List Module

List.rev reverses a list:


> List.rev [1 .. 5];; val it : int list = [5; 4; 3; 2; 1]
List.lter lters a list:

The two lists second and secondHalf are literally the same
object in memory, meaning F# reused the nodes from
second when constructing the new list combined.

Appending two lists, list1 and list2 has a space and time
> [1 .. 10] |> List.lter (fun x -> x % 2 = 0);; val it : int complexity of O(list1.Length).

32

11.3.2

CHAPTER 11. LISTS

List.choose

f and a list [1; 2; 3; 4; 5], the fold methods transform our


lists in the following ways:

List.choose has the following denition:


val choose : ('T -> 'U option) -> 'T list -> 'U list

fold: f (f (f (f (f (f seed 1) 2) 3) 4) 5
foldBack: f 1 (f 2 (f 3(f 4(f 5 seed))))

The choose method is clever because it lters and maps a


list simultaneously:
There are several other functions in the List module re> [1 .. 10] |> List.choose (fun x -> match x % 2 with | 0 lated to folding:
-> Some(x, x*x, x*x*x) | _ -> None);; val it : (int * int
* int) list = [(2, 4, 8); (4, 16, 64); (6, 36, 216); (8, 64,
512); (10, 100, 1000)]

fold2 and foldBack2: folds two lists together simultaneously.

choose lters for items that return Some and maps them
to another value in a single step.

reduce and reduceBack: same as fold and foldBack,


except it uses the rst (or last) element in the list as
the seed value.

11.3.3

List.fold and List.foldBack

scan and scanBack: similar to fold and foldBack,


except it returns all of the intermediate values as a
list rather than the nal accumulated value.

List.fold and List.foldBack have the following denitions:

Fold functions can be surprisingly useful:


val fold : ('State -> 'T -> 'State) -> 'State -> 'T list ->
'State val foldBack : ('T -> 'State -> 'State) -> 'T list -> Summing the numbers 1 - 100
'State -> 'State
let x = [ 1 .. 100 ] |> List.fold ( + ) 0 (* returns 5050 *)
A fold operation applies a function to each element in
a list, aggregates the result of the function in an accumulator variable, and returns the accumulator as the result
of the fold operation. This description makes fold operations sound more complicated, but the implementation is
actually very simple:
(* List.fold implementation *) let rec fold (f : 'State ->
'T -> 'State) (seed : 'State) = function | [] -> seed | hd :: tl
-> fold f (f seed hd) tl (* List.foldBack implementation
*) let rec foldBack (f : 'T -> 'State -> 'State) (items : 'T
list) (seed : 'State) = match items with | [] -> seed | hd ::
tl -> f hd (foldBack f tl seed)

In F#, mathematical operators are no dierent from functions. As shown above, we can actually pass the addition
operator to the fold function, because the + operator has
the denition int -> int -> int.
Computing a factorial
let factorial n = [ 1I .. n ] |> List.fold ( * ) 1I let x =
factorial 13I (* returns 6227020800I *)
Computing population standard deviation

let stddev (input : oat list) = let sampleSize = oat


input.Length let mean = (input |> List.fold ( + ) 0.0) /
sampleSize let dierenceOfSquares = input |> List.fold
fold applies a function to each element in the from left ( fun sum item -> sum + Math.Pow(item - mean, 2.0)
to right, while foldBack applies a function to each ele- ) 0.0 let variance = dierenceOfSquares / sampleSize
ment from right to left. Lets examine the fold functions Math.Sqrt(variance) let x = stddev [ 5.0; 6.0; 8.0; 9.0 ]
(* returns 1.58113883 *)
in more technical detail using the following example:
let input = [ 2; 4; 6; 8; 10 ] let f accumulator input =
accumulator * input let seed = 1 let output = List.fold f
seed input

11.3.4 List.nd and List.tryFind

The value of output is 3840. This table demonstrates how List.nd and List.trynd have the following types:
output was calculated:
val nd : ('T -> bool) -> 'T list -> 'T val tryFind : ('T ->
List.fold passes an accumulator with an item from the list bool) -> 'T list -> 'T option
into a function. The output of the function is passed as
the accumulator for the next item.
The nd and tryFind methods return the rst item in the
As shown above, the fold function processes the list from
the rst item to the last item in the list, or left to right. As
you can imagine, List.foldBack works the same way, but
it operates on lists from right to left. Given a fold function

list for which the search function returns true. They only
dier as follows: if no items are found that meet the
search function, nd throws a KeyNotFoundException,
while trynd returns None.

11.4. EXERCISES

33

The two functions are used as follows:

5]; [4; 5]; [5] ] expand [ monkey"; kitty"; bunny";


> let cities = ["Bellevue"; Omaha"; Lincoln"; Papil- rat ] = [ ["monkey"; kitty"; bunny"; rat"]; ["kitty";
lion"; Fremont"];; val cities : string list = ["Bellevue"; bunny"; rat"]; ["bunny"; rat"]; ["rat"] ]
Omaha"; Lincoln"; Papillion"; Fremont"] > let
ndStringContaining text (items : string list) = items
|> List.nd(fun item -> item.Contains(text));; val nd11.4.3 Greatest common divisor on lists
StringContaining : string -> string list -> string > let
ndStringContaining2 text (items : string list) = items
The task is to calculate the greatest common divisor of a
|> List.tryFind(fun item -> item.Contains(text));; val
list of integers.
ndStringContaining2 : string -> string list -> string
The rst step is to write a function which should have the
option > ndStringContaining Papi cities;; val it : string
following type:
= Papillion > ndStringContaining Hastings cities;;
val gcd : int -> int -> int
System.Collections.Generic.KeyNotFoundException:
The given key was not present in the dictionary. at Microsoft.FSharp.Collections.ListModule.nd[T](FastFunc`2 The gcd function should take two integers and return their
predicate,
FSharpList`1
list)
at
<Startup- greatest common divisor. Hint: Use Eulers algorithm
Code$FSI_0007>.$FSI_0007.main@() stopped due
to error > ndStringContaining2 Hastings cities;; val it gcd 15 25 = 5
: string option = None
The second step is to use the gcd function to calculate the
greatest common divisor of an int list.

11.4 Exercises

The gcdl function should work like this:

Solutions.

11.4.1

val gcdl : int list -> int

gcdl [15; 75; 20] = 5

Pair and Unpair

Write two functions with the following denitions:


val pair : 'a list -> ('a * 'a) list val unpair : ('a * 'a) list ->
'a list

If the list is empty the result shall be 0.

11.4.4 Basic Mergesort Algorithm

The goal of this exercise is to implement the mergesort


algorithm to sort a list in F#. When I talk about sorting, it
The pair function should convert a list into a list of pairs means sorting in an ascending order. If you're not familiar
as follows:
with the mergesort algorithm, it works like this:
pair [ 1 .. 10 ] = [(1, 2); (3, 4); (5, 6); (7, 8); (9, 10)] pair
[ one"; two"; three"; four"; ve ] = [(one, two);
(three, four)]

Split: Divide the list in two equally large parts

The unpair function should convert a list of pairs back


into a traditional list as follows:

Merge: Sort while merging (hence the name)

Sort: Sort those parts

unpair [(1, 2); (3, 4); (5, 6)] = [1; 2; 3; 4; 5; 6] unpair Note that the algorithm works recursively. It will rst split
[(one, two); (three, four)] = ["one"; two"; the list. The next step is to sort both parts. To do that, they
three"; four"]
will be split again and so on. This will basically continue
until the original list is scrambled up completely. Then
recursion will do its magic and assemble the list from the
bottom. This might seem confusing at rst, but you will
11.4.2 Expand a List
learn a lot about how recursion works, when theres more
than one function involved
Write a function with the following type denition:
The split function
val expand : 'a list -> 'a list list
val split : 'a list -> 'a list * 'a list
The expand function should expand a list as follows:
expand [ 1 .. 5 ] = [ [1; 2; 3; 4; 5]; [2; 3; 4; 5]; [3; 4; The split function will work like this.

34
split [2; 8; 5; 3] = ( [5; 2], [8; 3] )
The splits will be returned as a tuple. The split function
doesn't need to sort the lists though.
The merge function
The next step is merging. We now want to merge the splits
together into a sorted list assuming that both splits themselves are already sorted. The merge function will take a
tuple of two already sorted lists and recursively create a
sorted list:
val merge : 'a list * 'a list -> 'a list
Example:
merge ( [2; 5], [3; 8] ) = [2; 3; 5; 8]
It is important to notice that the merge function will only
work if both splits are already sorted. It will make its implementation a lot easier. Assuming both splits are sorted,
we can just look at the rst element of both splits and only
compare which one of them is smaller. To ensure this is
the case we will write one last function.
The msort function
You can think of it as the function organising the correct
execution of the algorithm. It uses the previously implemented functions, so its able to take a random list and
return the sorted list.
val msort : 'a list -> 'a list
How to implement msort:
If the list is empty or if the list has only one element, we
don't need to do anything to it and can immediately return
it, because we don't need to sort it.
If thats not the case, we need to apply our algorithm to it.
First split the list into a tuple of two, then merge the tuple
while recursively sorting both arguments of the tuple

CHAPTER 11. LISTS

Chapter 12

Sequences
Sequences, commonly called sequence expressions, are
similar to lists: both data structures are used to represent
an ordered collection of values. However, unlike lists, elements in a sequence are computed as they are needed (or
lazily), rather than computed all at once. This gives sequences some interesting properties, such as the capacity
to represent innite data structures.

quence are created as they are needed.


As a result, sequences are able to represent a data structure with an arbitrary number of elements:
> seq { 1I .. 1000000000000I };; val it : seq<bigint> =
seq [1I; 2I; 3I; 4I; ...]

The sequence above represents a list with one trillion elements in it. That does not mean the sequence actually
contains one trillion elements, but it can potentially hold
12.1 Dening Sequences
one trillion elements. By comparison, it would not be
possible to create a list [ 1I .. 1000000000000I ] since
Sequences are dened using the syntax:
the .NET runtime would attempt to create all one trillion
seq { expr }
elements up front, which would certainly consume all of
the available memory on a system before the operation
Similar to lists, sequences can be constructed using ranges completed.
and comprehensions:
Additionally, sequences can represent an innite number
> seq { 1 .. 10 };; (* seq ranges *) val it : seq<int> = seq of elements:
[1; 2; 3; 4; ...] > seq { 1 .. 2 .. 10 };; (* seq ranges *) val
it : seq<int> = seq [1; 3; 5; 7; ...] > seq {10 .. 1 .. 0};;
(* descending *) val it : seq<int> = seq [10; 9; 8; 7; ...]
> seq { for a in 1 .. 10 do yield a, a*a, a*a*a };; (* seq
comprehensions *) val it : seq<int * int * int> = seq [(1,
1, 1); (2, 4, 8); (3, 9, 27); (4, 16, 64); ...]

> let allEvens = let rec loop x = seq { yield x; yield! loop
(x + 2) } loop 0;; > for a in (Seq.take 5 allEvens) do
printfn "%i a;; 0 2 4 6 8 val it : unit = ()

Notice the denition of allEvens does not terminate. The


function Seq.take returns the rst n elements of elements
of the sequence. If we attempted to loop through all of
Sequences have an interesting property which sets them the elements, fsi would print indenitely.
apart from lists: elements in the sequence are lazily evaluated, meaning that F# does not compute values in a seNote: sequences are implemented as state maquence until the values are actually needed. This is in
chines by the F# compiler. In reality, they mancontrast to lists, where F# computes the value of all eleage state internally and hold only the last genments in a list on declaration. As a demonstration, comerated item in memory at a time. Memory uspare the following:
age is constant for creating and traversing sequences of any length.
> let intList = [ for a in 1 .. 10 do printfn intList: %i
a yield a ] let intSeq = seq { for a in 1 .. 10 do printfn
intSeq: %i a yield a };; val intList : int list val intSeq :
seq<int> intList: 1 intList: 2 intList: 3 intList: 4 intList: 12.2 Iterating Through Sequences
5 intList: 6 intList: 7 intList: 8 intList: 9 intList: 10 >
Manually
Seq.nth 3 intSeq;; intSeq: 1 intSeq: 2 intSeq: 3 intSeq:
4 val it : int = 4 > Seq.nth 7 intSeq;; intSeq: 1 intSeq: 2
intSeq: 3 intSeq: 4 intSeq: 5 intSeq: 6 intSeq: 7 intSeq: The .NET Base Class Library (BCL) contains two interfaces in the System.Collections.Generic namespace:
8 val it : int = 8
type IEnumerable<'a> = interface (* Returns an enuThe list is created on declaration, but elements in the se- merator that iterates through a collection *) member
35

36

CHAPTER 12. SEQUENCES

GetEnumerator<'a> : unit -> IEnumerator<'a> end type


IEnumerator<'a> = interface (* Advances to the next
element in the sequences. Returns true if the enumerator
was successfully advanced to the next element; false if
the enumerator has passed the end of the collection. *)
member MoveNext : unit -> bool (* Gets the current
element in the collection. *) member Current : 'a (* Sets
the enumerator to its initial position, which is before the
rst element in the collection. *) member Reset : unit ->
unit end
The seq type is dened as follows:
type
seq<'a>
=
tem.Collections.Generic.IEnumerable<'a>

Sys-

val choose : ('T -> 'U option) -> seq<'T> -> seq<'U>
Filters and maps a sequence to another sequence.
> let thisworks = seq { for nm in [ Some(James); None;
Some(John) ] |> Seq.choose id -> nm.Length } val it :
seq<int> = seq [5; 4]
val distinct : seq<'T> -> seq<'T>
Returns a sequence that lters out duplicate entries.

> let dist = Seq.distinct (seq[1;2;2;6;3;2]) val it : seq<int>


As you can see, seq is not a unique F# type, = seq [1; 2; 6; 3]
but rather another name for the built-in System.Collections.Generic.IEnumerable interface. Since val exists : ('T -> bool) -> seq<'T> -> bool
seq/IEnumerable is a native .NET type, it was designed
to be used in a more imperative style, which can be
Determines if an element exists in a sequence.
demonstrated as follows:
open System open System.Collections let evens = seq
{ 0 .. 2 .. 10 } (* returns IEnumerable<int> *) let
main() = let evensEnumerator = evens.GetEnumerator()
(* returns IEnumerator<int> *) while evensEnumerator.MoveNext() do printfn evensEnumerator.Current:
%i evensEnumerator.Current Console.ReadKey(true)
|> ignore main()
This program outputs:

> let equalsTwo x = x=2 > let exist = Seq.exists


equalsTwo (seq{3..9}) val equalsTwo : int -> bool val it
: bool = false
val lter : ('T -> bool) -> seq<'T> -> seq<'T>
Builds a new sequence consisting of elements
ltered from the input sequence.

evensEnumerator.Current: 0 evensEnumerator.Current: > Seq.lter (fun x-> x%2 = 0) (seq{0..9}) val it :


2 evensEnumerator.Current:
4 evensEnumera- seq<int> = seq [0; 2; 4; 6; ...]
tor.Current: 6 evensEnumerator.Current: 8 evensEnumerator.Current: 10
val fold : ('State -> 'T -> 'State) -> 'State -> seq<'T>
Behind the scenes, .NET converts every for loop over a -> 'State
collection into an explicit while loop. In other words, the
following two pieces of code compile down to the same
Repeatedly applies a function to each element
bytecode:
in the sequence from left to right.
All collections which can be used with the for keyword implement the IEnumerable<'a> interface, a con- > let sumSeq sequence1 = Seq.fold (fun acc elem -> acc
cept which will be discussed later in this book.
+ elem) 0 sequence1 Seq.init 10 (fun index -> index *
index) |> sumSeq |> printfn The sum of the elements is
%d. > The sum of the elements is 285. val sumSeq :
seq<int> -> int
12.3 The Seq Module
Similar to the List modules, the Seq module contains a
number of useful functions for operating on sequences:
val append : seq<'T> -> seq<'T> -> seq<'T>

Note: sequences can only be read in a forwardonly manner, so there is no corresponding foldBack function as found in the List and Array
modules.

Appends one sequence onto another sequence.


val initInnite : (int -> 'T) -> seq<'T>
> let test = Seq.append (seq{1..3}) (seq{4..7});; val it :
seq<int> = seq [1; 2; 3; 4; ...]

Generates a sequence consisting of an innite


number of elements.

12.3. THE SEQ MODULE

37

> Seq.initInnite (fun x -> x*x) val it : seq<int> = seq > let test = 1 |> Seq.unfold (fun x -> if x <= 5 then
[0; 1; 4; 9; ...]
Some(sprintf x: %i x, x + 1) else None );; val test :
seq<string> > Seq.iter (fun x -> printfn "%s x) test;; x:
1 x: 2 x: 3 x: 4 x: 5
val map : ('T -> 'U) -> seq<'T> -> seq<'U>
Maps a sequence of type 'a to type 'b.
> Seq.map (fun x->x*x+2) (seq[3;5;4;3]) val it :
seq<int> = seq [11; 27; 18; 11]
val item : int -> seq<'T> -> 'T
Returns the nth value of a sequence.
> Seq.item 3 (seq {for n in 2..9 do yield n}) val it : int = 5
val take : int -> seq<'T> -> seq<'T>
Returns a new sequence consisting of the rst
n elements of the input sequence.
> Seq.take 3 (seq{1..6}) val it : seq<int> = seq [1; 2; 3]
val takeWhile : ('T -> bool) -> seq<'T> -> seq<'T>
Return a sequence that, when iterated, yields
elements of the underlying sequence while the
given predicate returns true, and returns no further elements.
> let sequenciaMenorqDez = Seq.takeWhile (fun elem
-> elem < 10) (seq {for i in 0..20 do yield i+1}) val sequenciaMenorqDez : seq<int> > sequenciaMenorqDez;;
val it : seq<int> = seq [1; 2; 3; 4; ...]
val unfold : ('State -> ('T * 'State) option) -> 'State
seed -> seq<'T>
The opposite of fold: this function generates a
sequence as long as the generator function returns Some.
> let bs = (0I, 1I) |> Seq.unfold (fun (a, b) ->
Some(a, (b, a + b) ) );; val bs : seq<bigint> > Seq.iter
(fun x -> printf "%O " x) (Seq.take 20 bs);; 0 1 1 2 3
5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181
The generator function in unfold expects a return type
of ('T * 'State) option. The rst value of the tuple is inserted as an element into the sequence, the second value
of the tuple is passed as the accumulator. The bs function is clever for its brevity, but its hard to understand
if you've never seen an unfold function. The following
demonstrates unfold in a more straightforward way:

Often, its preferable to generate sequences using seq


comprehensions rather than the unfold.

Chapter 13

Sets and Maps


In addition to lists and sequences, F# provides two related
Return a new set with an element added to the
immutable data structures called sets and maps. Unlike
set. No exception is raised if the set already
lists, sets and maps are unordered data structures, meancontains the given element.
ing that these collections do not preserve the order of elements as they are inserted, nor do they permit duplicates. val compare : Set<'a> -> Set<'a> -> int
Compare two sets. Places sets into a total order.

13.1 Sets

A set in F# is just a container for items. Sets do not pre- val count : Set<'a> -> int
serve the order in which items are inserted, nor do they
allow duplicate entries to be inserted into the collection.
Return the number of elements in the set.
Same as size.
Sets can be created in a variety of ways:
Adding an item to an empty set The Set module contains a useful function Set.empty which returns an empty val dierence : Set<'a> -> Set<'a> -> Set<'a>
set to start with.
Return a new set with the elements of the secConveniently, all instances of sets have an Add function
ond set removed from the rst.
with the type val Add : 'a -> Set<'a>. In other words, our
Add returns a set containing our new item, which makes
it easy to add items together in this fashion:
> let a = Set.ofSeq [ 1 .. 10 ] let b = Set.ofSeq [ 5 .. 15 ];;
> Set.empty.Add(1).Add(2).Add(7);; val it : Set<int> = val a : Set<int> val b : Set<int> > Set.dierence a b;; val
it : Set<int> = set [1; 2; 3; 4] > a - b;; (* The '-' operator
set [1; 2; 7]
is equivalent to Set.dierence *) val it : Set<int> = set
[1; 2; 3; 4]
Converting lists and sequences into sets Additionally,
the we can use Set.ofList and Set.ofSeq to convert an enval exists : ('a -> bool) -> Set<'a> -> bool
tire collection into a set:
> Set.ofList ["Mercury"; Venus"; Earth"; Mars";
Jupiter"; Saturn"; Uranus"; Neptune"];; val it :
Set<string> = set ["Earth"; Jupiter"; Mars"; Mercury"; ...]

Test if any element of the collection satises


the given predicate.
val lter : ('a -> bool) -> Set<'a> -> Set<'a>

The example above demonstrates the unordered nature of


sets.

13.1.1

Return a new collection containing only the elements of the collection for which the given
predicate returns true.

The Set Module


val intersect : Set<'a> -> Set<'a> -> Set<'a>

The Microsoft.FSharp.Collections.Set module contains a


variety of useful methods for working with sets.

Compute the intersection, or overlap, of the


two sets.

val add : 'a -> Set<'a> -> Set<'a>


38

13.2. MAPS

39

> let a = Set.ofSeq [ 1 .. 10 ] let b = Set.ofSeq [ 5 .. 15 13.1.2 Examples


];; val a : Set<int> val b : Set<int> > Set.iter (fun x ->
open System let shakespeare = O Romeo, Romeo!
printf "%O " x) (Set.intersect a b);; 5 6 7 8 9 10
wherefore art thou Romeo?" let shakespeareArray =
shakespeare.Split([| ' '; ','; '!'; '?' |], StringSplitOpval map : ('a -> 'b) -> Set<'a> -> Set<'b>
tions.RemoveEmptyEntries) let shakespeareSet = shakespeareArray |> Set.ofSeq let main() = printfn shakeReturn a new collection containing the results
speare: %A shakespeare let printCollection msg coll =
of applying the given function to each element
printfn "%s:" msg Seq.iteri (fun index item -> printfn
of the input set.
" %i: %O index item) coll printCollection shakespeareArray shakespeareArray printCollection shakeval contains: 'a -> Set<'a> -> bool
speareSet shakespeareSet Console.ReadKey(true) |> ignore main()
shakespeare: O Romeo, Romeo! wherefore art thou
Evaluates to true if the given element is in the
Romeo?" shakespeareArray: 0: O 1: Romeo 2: Romeo
given set.
3: wherefore 4: art 5: thou 6: Romeo shakespeareSet: 0:
O 1: Romeo 2: art 3: thou 4: wherefore
val remove : 'a -> Set<'a> -> Set<'a>
Return a new set with the given element removed. No exception is raised if the set doesn't
contain the given element.
val count: Set<'a> -> int
Return the number of elements in the set.
val isSubset : Set<'a> -> Set<'a> -> bool
Evaluates to true if all elements of the rst
set are in the second.
val isProperSubset : Set<'a> -> Set<'a> -> bool
Evaluates to true if all elements of the rst
set are in the second, and there is at least one
element in the second set which is not in the
rst.

13.2 Maps
A map is a special kind of set: it associates keys with
values. A map is created in a similar way to sets:
> let holidays = Map.empty. (* Start with empty Map *)
Add(Christmas, Dec. 25). Add(Halloween, Oct.
31). Add(Darwin Day, Feb. 12). Add(World Vegan Day, Nov. 1);; val holidays : Map<string,string>
> let monkeys = [ Squirrel Monkey, Simia sciureus";
Marmoset, Callithrix jacchus"; Macaque, Macaca
mulatta"; Gibbon, Hylobates lar"; Gorilla, Gorilla
gorilla"; Humans, Homo sapiens"; Chimpanzee,
Pan troglodytes ] |> Map.ofList;; (* Convert list to
Map *) val monkeys : Map<string,string>
You can use the .[key] to access elements in the map:
> holidays.["Christmas"];; val it : string = Dec. 25
> monkeys.["Marmoset"];; val it : string = Callithrix
jacchus

> let a = Set.ofSeq [ 1 .. 10 ] let b = Set.ofSeq [ 5 ..


15 ] let c = Set.ofSeq [ 2; 4; 5; 9 ];; val a : Set<int>
val b : Set<int> val c : Set<int> > Set.isSubset c a;; (*
All elements of 'c' exist in 'a' *) val it : bool = true > 13.2.1 The Map Module
Set.isSubset c b;; (* Not all of the elements of 'c' exist in
The Microsoft.FSharp.Collections.Map module handles
'b' *);; val it : bool = false
map operations.
val union : Set<'a> -> Set<'a> -> Set<'a>

val add :
'key -> 'a -> Map<'key,'a> ->
Map<'key,'a>

Compute the union of the two sets.

Return a new map with the binding added to


the given map.

> let a = Set.ofSeq [ 1 .. 10 ] let b = Set.ofSeq [ 5 .. 15


];; val a : Set<int> val b : Set<int> > Set.iter (fun x -> val empty<'key,'a> : Map<'key,'a>
printf "%O " x) (Set.union a b);; 1 2 3 4 5 6 7 8 9 10 11
12 13 14 15 val it : unit = () > Set.iter (fun x -> printf
Returns an empty map.
"%O " x) (a + b);; (* '+' computes union *) 1 2 3 4 5 6 7
val exists : ('key -> 'a -> bool) -> Map<'key,'a> ->
8 9 10 11 12 13 14 15
bool

40

CHAPTER 13. SETS AND MAPS


Return true if the given predicate returns true
for one of the bindings in the map.

13.3 Set and Map Performance

F# sets and maps are implemented as immutable AVL


val lter : ('key -> 'a -> bool) -> Map<'key,'a> -> trees, an ecient data structure which forms a selfMap<'key,'a>
balancing binary tree. AVL trees are well-known for their
eciency, in which they can search, insert, and delete elements in the tree in O(log n) time, where n is the number
Build a new map containing only the bindings
of elements in the tree.
for which the given predicate returns true.
val nd : 'key -> Map<'key,'a> -> 'a
Lookup an element in the map, raising
KeyNotFoundException if no binding exists in
the map.
val containsKey: 'key -> Map<'key,'a> -> bool
Test if an element is in the domain of the map.
val remove : 'key -> Map<'key,'a> -> Map<'key,'a>
Remove an element from the domain of the
map. No exception is raised if the element is
not present.
val trynd : 'key -> Map<'key,'a> -> 'a option
Lookup an element in the map, returning a
Some value if the element is in the domain of
the map and None if not.

13.2.2

Examples

open System let capitals = [(Australia, Canberra);


(Canada, Ottawa); (China, Beijing); (Denmark, Copenhagen); (Egypt, Cairo); (Finland,
Helsinki); (France, Paris); (Germany, Berlin);
(India, New Delhi); (Japan, Tokyo); (Mexico, Mexico City); (Russia, Moscow); (Slovenia, Ljubljana); (Spain, Madrid); (Sweden,
Stockholm); (Taiwan, Taipei); (USA, Washington D.C.)] |> Map.ofList let rec main() = Console.Write(Find a capital by country (type 'q' to
quit): ") match Console.ReadLine() with | q -> Console.WriteLine(Bye bye) | country -> match capitals.TryFind(country) with | Some(capital) -> Console.WriteLine(The capital of {0} is {1}\n, country, capital) | None -> Console.WriteLine(Country not
found.\n) main() (* loop again *) main()
Find a capital by country (type 'q' to quit): Egypt The capital of Egypt is Cairo Find a capital by country (type 'q' to
quit): Slovenia The capital of Slovenia is Ljubljana Find a
capital by country (type 'q' to quit): Latveria Country not
found. Find a capital by country (type 'q' to quit): USA
The capital of USA is Washington D.C. Find a capital by
country (type 'q' to quit): q Bye bye

Chapter 14

Discriminated Unions
Discriminated unions, also called tagged unions, represent a nite, well-dened set of choices. Discriminated
unions are often the tool of choice for building up more
complicated data structures including linked lists and a
wide range of trees.

14.1 Creating
Unions

Discriminated

x: On y: O
Notice that we create an instance of switchstate simply
by using the name of its cases; this is because, in a literal
sense, the cases of a union are constructors. As you may
have guessed, since we use the same syntax for constructing objects as for matching them, we can pattern match
on unions in the following way:

type switchstate = | On | O let toggle = function (*


pattern matching input *) | On -> O | O -> On let
main() = let x = On let y = O let z = toggle y printfn
Discriminated unions are dened using the following syn- x: %A x printfn y: %A y printfn z: %A z printfn
toggle z: %A (toggle z) main()
tax:
type unionName = | Case1 | Case2 of datatype | ...

The function toggle has the type val toggle : switchstate


-> switchstate.

By convention, union names start with a lowercase charThis program has the following output:
acter, and union cases are written in PascalCase.
x: On y: O z: On toggle z: O

14.2 Union basics:


switch

an On/O
14.3 Holding Data In Unions: a
dimmer switch

Lets say we have a light switch. For most of us, a light


switch has two possible states: the light switch can be ON,
or it can be OFF. We can use an F# union to model our The example above is kept deliberately simple. In fact,
in many ways, the discriminated union dened above
light switchs state as follows:
doesn't appear much dierent from an enum value. Howtype switchstate = | On | O
ever, lets say we wanted change our light switch model
into a model of a dimmer switch, or in other words a light
We've dened a union called switchstate which has two switch that allow users to adjust a lightbulbs power outcases, On and O. You can create and use instances of put from 0% to 100% power.
switchstate as follows:
We can modify our union above to accommodate three
type switchstate = | On | O let x = On (* creates an possible states: On, O, and an adjustable value between
instance of switchstate *) let y = O (* creates another 0 and 100:
instance of switchstate *) let main() = printfn x: %A x type switchstate = | On | O | Adjustable of oat
printfn y: %A y main()
We've added a new case, Adjustable of oat. This case
is fundamentally the same as the others, except it takes a
type switchstate = On | O val x : switchstate val y : single oat value in its constructor:
switchstate
open System type switchstate = | On | O | Adjustable
This program has the following types:

It outputs the following:

of oat let toggle = function | On -> O | O -> On


| Adjustable(brightness) -> (* Matches any switchstate
41

42

CHAPTER 14. DISCRIMINATED UNIONS

of type Adjustable. Binds the value passed into the


constructor to the variable 'brightness. Toggles dimness
around the halfway point. *) let pivot = 0.5 if brightness <= pivot then Adjustable(brightness + pivot) else
Adjustable(brightness - pivot) let main() = let x = On
let y = O let z = Adjustable(0.25) (* takes a oat in
constructor *) printfn x: %A x printfn y: %A y
printfn z: %A z printfn toggle z: %A (toggle z)
Console.ReadKey(true) |> ignore main()

14.5 Generalizing Unions For All


Datatypes

14.4 Creating Trees

We can use the union dene above to dene a binary tree


of any data type:

Note that our binary tree above only operates on integers.


Its possible to construct unions which are generalized to
operate on all possible data types. We can modify the
denition of our union to the following:

type 'a tree = | Leaf of 'a | Node of 'a tree * 'a tree (*
The syntax above is OCaml style. Its common to see
This program outputs:
unions dened using the ".NET style as follows which
x: On y: O z: Adjustable 0.25 toggle z: Adjustable 0.75 surrounds the type parameter with <'s and >'s after
the type name: type tree<'a> = | Leaf of 'a | Node of
tree<'a> * tree<'a> *)

Discriminated unions can easily model a wide variety of open System type 'a tree = | Leaf of 'a | Node of 'a tree
* 'a tree let rstTree = Node( Leaf 1, Node( Leaf 2,
trees and hierarchical data structures.
Node( Node( Leaf 4, Leaf 5 ), Leaf 3 ) ) ) let secondTree
For example, lets consider the following binary tree:
= Node( Node( Node( Leaf Red, Leaf Orange ),
/\ / \ 1 /\ / \ 2 /\ / \ /\ 3 / \ 4 5
Node( Leaf Yellow, Leaf Green ) ), Node( Leaf
Each node of our tree contains exactly two branches, and Blue, Leaf Violet ) ) let prettyPrint tree = let rec
each branch can either be a integer or another tree. We loop depth tree = let spacer = new String(' ', depth)
match tree with | Leaf(value) -> printfn "%s |- %A
can represent this tree as follows:
spacer value | Node(tree1, tree2) -> printfn "%s |"
type tree = | Leaf of int | Node of tree * tree
spacer loop (depth + 1) tree1 loop (depth + 1) tree2
loop 0 tree let main() = printfn rstTree:" prettyPrint
We can create an instance of the tree above using the fol- rstTree printfn secondTree:" prettyPrint secondTree
Console.ReadKey(true) |> ignore main()
lowing code:
open System type tree = | Leaf of int | Node of tree
* tree let simpleTree = Node( Leaf 1, Node( Leaf 2,
Node( Node( Leaf 4, Leaf 5 ), Leaf 3 ) ) ) let main()
= printfn "%A simpleTree Console.ReadKey(true) |>
ignore main()

The functions above have the following types:


type 'a tree = | Leaf of 'a | Node of 'a tree * 'a tree
val rstTree : int tree val secondTree : string tree val
prettyPrint : 'a tree -> unit

This program outputs the following:

This program outputs:


Node (Leaf 1,Node (Leaf 2,Node (Node (Leaf 4,Leaf rstTree: | |- 1 | |- 2 | | |- 4 |- 5 |- 3 secondTree: | | | |- Red
5),Leaf 3)))
|- Orange | |- Yellow |- Green | |- Blue |- Violet
Very often, we want to recursively enumerate through
all of the nodes in a tree structure. For example, if we
wanted to count the total number of Leaf nodes in our
tree, we can use:
open System type tree = | Leaf of int | Node of tree
* tree let simpleTree = Node (Leaf 1, Node (Leaf 2,
Node (Node (Leaf 4, Leaf 5), Leaf 3))) let countLeaves
tree = let rec loop sum = function | Leaf(_) -> sum +
1 | Node(tree1, tree2) -> sum + (loop 0 tree1) + (loop
0 tree2) loop 0 tree let main() = printfn countLeaves
simpleTree:
%i (countLeaves simpleTree) Console.ReadKey(true) |> ignore main()
This program outputs:
countLeaves simpleTree: 5

14.6 Examples
14.6.1 Built-in Union Types
F# has several built-in types derived from discriminated
unions, some of which have already been introduced in
this tutorial. These types include:
type 'a list = | Cons of 'a * 'a list | Nil type 'a option = |
Some of 'a | None

14.7. ADDITIONAL READING

14.6.2

Propositional Logic

The ML family of languages, which includes F# and its


parent language OCaml, were originally designed for the
development of automated theorem provers. Union types
allow F# programmers to represent propositional logic remarkably concisely. To keep things simple, lets limit our
propositions to four possible cases:
type proposition = | True | Not of proposition | And
of proposition * proposition | Or of proposition *
proposition
Lets say we had a series of propositions and wanted to
determine whether they evaluate to true or false. We can
easily write an eval function by recursively enumerating
through a propositional statement as follows:
let rec eval = function | True -> true | Not(prop) ->
not (eval(prop)) | And(prop1, prop2) -> eval(prop1)
&& eval(prop2) | Or(prop1, prop2) -> eval(prop1) ||
eval(prop2)
The eval function has the type val eval : proposition ->
bool.
Here is a full program using the eval function:
open System type proposition = | True | Not of proposition | And of proposition * proposition | Or of proposition
* proposition let prop1 = (* ~t || ~~t *) Or( Not True,
Not (Not True) ) let prop2 = (* ~(t && ~t) || ( (t || t)
|| ~t) *) Or( Not( And( True, Not True ) ), Or( Or(
True, True ), Not True ) ) let prop3 = (* ~~~~~~~t *)
Not(Not(Not(Not(Not(Not(Not True)))))) let rec eval =
function | True -> true | Not(prop) -> not (eval(prop))
| And(prop1, prop2) -> eval(prop1) && eval(prop2) |
Or(prop1, prop2) -> eval(prop1) || eval(prop2) let main()
= let testProp name prop = printfn "%s: %b name
(eval prop) testProp prop1 prop1 testProp prop2
prop2 testProp prop3 prop3 Console.ReadKey(true) |>
ignore main()
This program outputs the following:
prop1: true prop2: true prop3: false

14.7 Additional Reading


Theorem Proving Examples (OCaml)

43

Chapter 15

Mutable Data
All of the data types and values in F# seen so far have
been immutable, meaning the values cannot be reassigned
another value after they've been declared. However, F#
allows programmers to create variables in the true sense
of the word: variables whose values can change throughout the lifetime of the application.

10:00:33";} {ID = 3; IsProcessed = true; ProcessedText


= Processed 10:00:34";} {ID = 4; IsProcessed = true;
ProcessedText = Processed 10:00:35";}

15.1.1 Limitations of Mutable Variables

Mutable variables are somewhat limited: mutables are inaccessible outside of the scope of the function where they
15.1 mutable Keyword
are dened. Specically, this means its not possible to
reference a mutable in a subfunction of another function.
The simplest mutable variables in F# are declared using Heres a demonstration in fsi:
the mutable keyword. Here is a sample using fsi:
> let testMutable() = let mutable msg = hello printfn
> let mutable x = 5;; val mutable x : int > x;; val it : int = "%s msg let setMsg() = msg <- world setMsg() printfn
"%s msg;; msg <- world --------^^^^^^^^^^^^^^^
5 > x <- 10;; val it : unit = () > x;; val it : int = 10
stdin(18,9): error FS0191: The mutable variable 'msg'
is used in an invalid way. Mutable variables may not be
As shown above, the <- operator is used to assign a muta- captured by closures. Consider eliminating this use of
ble variable a new value. Notice that variable assignment mutation or using a heap-allocated mutable reference
actually returns unit as a value.
cell via 'ref' and '!'.
The mutable keyword is frequently used with record types
to create mutable records:
open System type transactionItem = { ID : int; mutable IsProcessed : bool; mutable ProcessedText :
string; } let getItem id = { ID = id; IsProcessed =
false; ProcessedText = null; } let processItems (items
: transactionItem list) = items |> List.iter(fun item ->
item.IsProcessed <- true item.ProcessedText <- sprintf
Processed %s (DateTime.Now.ToString("hh:mm:ss"))
Threading.Thread.Sleep(1000) (* Putting thread to sleep
for 1 second to simulate processing overhead. *) )
let printItems (items : transactionItem list) = items |>
List.iter (fun x -> printfn "%A x) let main() = let items
= List.init 5 getItem printfn Before Process:" printItems
items printfn After process:" processItems items printItems items Console.ReadKey(true) |> ignore main()
Before Process: {ID = 0; IsProcessed = false; ProcessedText = null;} {ID = 1; IsProcessed = false; ProcessedText = null;} {ID = 2; IsProcessed = false; ProcessedText = null;} {ID = 3; IsProcessed = false; ProcessedText = null;} {ID = 4; IsProcessed = false; ProcessedText = null;} After process: {ID = 0; IsProcessed = true;
ProcessedText = Processed 10:00:31";} {ID = 1; IsProcessed = true; ProcessedText = Processed 10:00:32";}
{ID = 2; IsProcessed = true; ProcessedText = Processed

15.2 Ref cells


Ref cells get around some of the limitations of mutables.
In fact, ref cells are very simple datatypes which wrap up
a mutable eld in a record type. Ref cells are dened by
F# as follows:
type 'a ref = { mutable contents : 'a }
The F# library contains several built-in functions and operators for working with ref cells:
let ref v = { contents = v } (* val ref : 'a -> 'a ref *) let
(!) r = r.contents (* val (!) : 'a ref -> 'a *) let (:=) r v =
r.contents <- v (* val (:=) : 'a ref -> 'a -> unit *)
The ref function is used to create a ref cell, the ! operator is used to read the contents of a ref cell, and the :=
operator is used to assign a ref cell a new value. Here is
a sample in fsi:
> let x = ref hello";; val x : string ref > x;; (* returns ref
instance *) val it : string ref = {contents = hello";} >

44

15.3. ENCAPSULATING MUTABLE STATE


!x;; (* returns x.contents *) val it : string = hello > x :=
world";; (* updates x.contents with a new value *) val it
: unit = () > !x;; (* returns x.contents *) val it : string =
world

45
cell1 := 10, this changes the value in memory to the following:
Memory ____ |____| cell1 |____| / |_10_| - cell2 |____| \
|____| cell3

By assigning cell1.contents a new value, the variables


Since ref cells are allocated on the heap, they can be cell2 and cell3 were changed as well. This can be demonshared across multiple functions:
strated using fsi as follows:
open System let withSideEects x = x := assigned from > let cell1 = ref 7;; val cell1 : int ref > let cell2 = cell1;;
withSideEects function let refTest() = let msg = ref val cell2 : int ref > let cell3 = cell2;; val cell3 : int ref >
hello printfn "%s !msg let setMsg() = msg := world !cell1;; val it : int = 7 > !cell2;; val it : int = 7 > !cell3;; val
setMsg() printfn "%s !msg withSideEects msg printfn it : int = 7 > cell1 := 10;; val it : unit = () > !cell1;; val it :
"%s !msg let main() = refTest() Console.ReadKey(true) int = 10 > !cell2;; val it : int = 10 > !cell3;; val it : int = 10
|> ignore main()
The withSideEects function has the type val withSideEects : string ref -> unit.

15.3 Encapsulating Mutable State

This program outputs the following:


F# discourages the practice of passing mutable data between functions. Functions that rely on mutation should
The withSideEects function is named as such because generally hide its implementation details behind a private
it has a side-eect, meaning it can change the state of a function, such as the following example in FSI:
variable in other functions. Ref Cells should be treated
like re. Use it cautiously when it is absolutely necessary > let incr = let counter = ref 0 fun () -> counter :=
but avoid it in general. If you nd yourself using Ref Cells !counter + 1 !counter;; val incr : (unit -> int) > incr();;
while translating code from C/C++, then ignore eciency val it : int = 1 > incr();; val it : int = 2 > incr();; val it :
for a while and see if you can get away without Ref Cells int = 3
or at worst using mutable. You would often stumble upon
a more elegant and more maintanable algorithm
hello world assigned from withSideEects function

15.2.1

Aliasing Ref Cells

Note: While imperative programming uses


aliasing extensively, this practice has a number
of problems. In particular it makes programs
hard to follow since the state of any variable
can be modied at any point elsewhere in an
application. Additionally, multithreaded applications sharing mutable state are dicult to
reason about since one thread can potentially
change the state of a variable in another thread,
which can result in a number of subtle errors
related to race conditions and dead locks.
A ref cell is very similar to a C or C++ pointer. Its possible to point to two or more ref cells to the same memory
address; changes at that memory address will change the
state of all ref cells pointing to it. Conceptually, this process looks like this:
Lets say we have 3 ref cells looking at the same address
in memory:
Memory ____ |____| cell1 |____| / |__7_| - cell2 |____| \
|____| cell3
cell1, cell2, and cell3 are all pointing to the same address
in memory. The .contents property of each cell is 7. Lets
say, at some point in our program, we execute the code

Chapter 16

Control Flow
In all programming languages, control ow refers to the
decisions made in code that aect the order in which
statements are executed in an application. F#'s imperative control ow elements are similar to those encountered in other languages.

16.1 Imperative Programming in a


Nutshell

Like all F# blocks, the scope of an if statement extends


to any code indented under it. For example:
open System let printMessage condition = if condition
then printfn condition = true: inside the 'if'" printfn outside the 'if' block let main() = printMessage true printfn
"--------" printMessage false Console.ReadKey(true) |>
ignore main()
This program prints:

Most programmers coming from a C#, Java, or C++


background are familiar with an imperative style of programming which uses loops, mutable data, and functions
with side-eects in applications. While F# primarily encourages the use of a functional programming style, it has
constructs which allow programmers to write code in a
more imperative style as well. Imperative programming
can be useful in the following situations:

condition = true: inside the 'if' outside the 'if' block ------- outside the 'if' block

16.2.1 Working With Conditions


F# has three boolean operators:

The && and || operators are short-circuited, meaning the


CLR will perform the minimum evaluation necessary to
determine whether the condition will succeed or fail. For
Interacting with many objects in the .NET Frame- example, if the left side of an && evaluates to false, then
work, most of which are inherently imperative.
there is no need to evaluate the right side; likewise, if the
left side of a || evaluates to true, then there is no need to
Interacting with components that depend heavily on evaluate the right side of the expression.
side-eects, such as GUIs, I/O, and sockets.
Here is a demonstration of short-circuiting in F#:
Scripting and prototyping snippets of code.
open System let alwaysTrue() = printfn Always true

true let alwaysFalse() = printfn Always false false


let main() = let testCases = ["alwaysTrue && al Optimizing blocks of code where an imperative ver- waysFalse, fun() -> alwaysTrue() && alwaysFalse();
sion of an algorithm is more ecient than the func- alwaysFalse && alwaysTrue, fun() -> alwaysFalse()
&& alwaysTrue(); alwaysTrue || alwaysFalse, fun()
tional version.
-> alwaysTrue() || alwaysFalse(); alwaysFalse || alwaysTrue, fun() -> alwaysFalse() || alwaysTrue();]
testCases |> List.iter (fun (msg, res) -> printfn "%s: %b
16.2 if/then Decisions
msg (res()) printfn "-------") Console.ReadKey(true) |>
ignore main()
F#'s if/then/elif/else construct has already been seen earlier in this book, but to introduce it more formally, the
The alwaysTrue and alwaysFalse methods return true and
if/then construct has the following syntaxes:
false respectively, but they also have a side-eect of print(* simple if *) if expr then expr (* binary if *) if expr ing a message to the console whenever the functions are
then expr else expr (* multiple else branches *) if expr evaluated.
then expr elif expr then expr elif expr then expr ... else
This program outputs the following:
expr
Always true Always false alwaysTrue && alwaysFalse:
Initializing complex data structures.

46

16.5. WHILE LOOPS

47

false ------- Always false alwaysFalse && alwaysTrue:


false ------- Always true alwaysTrue || alwaysFalse: true ------ Always false Always true alwaysFalse || alwaysTrue:
true -------

3.99; Tempeh, 3, 2.69; Rice milk, 1, 2.95;];; val


shoppingList : (string * int * oat) list > for (food,
quantity, price) in shoppingList do printfn food: %s,
quantity: %i, price: %g food quantity price;; food:
As you can see above, the expression alwaysTrue && Tofu, quantity: 2, price: 1.99 food: Seitan, quantity: 2,
alwaysFalse evaluates both sides of the expression. al- price: 3.99 food: Tempeh, quantity: 3, price: 2.69 food:
waysFalse && alwaysTrue only evaluates the left side of Rice milk, quantity: 1, price: 2.95
the expression; since the left side returns false, its unnecessary to evaluate the right side.

16.5 while Loops


16.3 for Loops Over Ranges

As the name suggests, while loops will repeat a block of


code indenitely while a particular condition is true. The
for loops are traditionally used to iterate over a well- syntax of a while loop is dened as follows:
dened integer range. The syntax of a for loop is dened
while expr do ... // loop body
as:
for var = start-expr to end-expr do ... // loop body

We use a while loop when we don't know how many times


to execute a block of code. For example, lets say we
Heres a trivial program which prints out the numbers 1 - wanted the user to guess a password to a secret area; the
10:
user could get the password right on the rst try, or the
user could try millions of passwords, we just don't know.
let main() = for i = 1 to 10 do printfn i: %i i main()
Here is a short program that requires a user to guess a
password correctly in at most 3 attempts:
This program outputs:
open System let main() = let password = monkey
i: 1 i: 2 i: 3 i: 4 i: 5 i: 6 i: 7 i: 8 i: 9 i: 10
let mutable guess = String.Empty let mutable attempts
This code takes input from the user to compute an aver- = 0 while password <> guess && attempts < 3 do
age:
Console.Write(Whats the password? ") attempts <open System let main() = Console.WriteLine(This attempts + 1 guess <- Console.ReadLine() if password
program averages numbers input by the user.) Con- = guess then Console.WriteLine(You got the password
sole.Write(How many numbers do you want to add? right!") else Console.WriteLine(You didn't guess the
") let mutable sum = 0 let numbersToAdd = Con- password) Console.ReadKey(true) |> ignore main()
sole.ReadLine() |> int for i = 1 to numbersToAdd
do Console.Write(Input #{0}: ", i) let input = Con- This program outputs the following:
sole.ReadLine() |> int sum <- sum + input let average
Whats the password? kitty Whats the password? mon= sum / numbersToAdd Console.WriteLine(Average:
key You got the password right!
{0}", average) main()
This program outputs:
This program averages numbers input by the user. How
many numbers do you want to add? 3 Input #1: 100 Input
#2: 90 Input #3: 50 Average: 80

16.4 for Loops Over Collections


and Sequences
Its often convenient to iterate over collections of items
using the syntax:
for pattern in expr do ... // loop body
For example, we can print out a shopping list in fsi:
> let shoppingList = ["Tofu, 2, 1.99; Seitan, 2,

Chapter 17

Arrays
Arrays are a ubiquitous, familiar data structure used to val create : int -> 'T value -> 'T []
represent a group of related, ordered values. Unlike F#
data structures, arrays are mutable, meaning the values in
Creates an array with arraySize elements. Inian array can be changed after the array has been created.
tializes each element in the array with value.
> Array.create 5 Juliet";; val it : string [] = [|"Juliet";
Juliet"; Juliet"; Juliet"; Juliet"|]

17.1 Creating Arrays

Arrays are conceptually similar to lists. Naturally, arrays


val init : int arraySize -> (int index -> 'T) initializer
can be created using many of the same techniques as lists:
-> 'T []

17.1.1

Array literals

Creates an array with arraySize elements. Initializes each element in the array with the initializer function.

> [| 1; 2; 3; 4; 5 |];; val it : int array = [|1; 2; 3; 4; 5|]

17.1.2

> Array.init 5 (fun index -> sprintf index: %i index);;


val it : string [] = [|"index: 0"; index: 1"; index: 2";
index: 3"; index: 4"|]

Array comprehensions

F# supports array comprehensions using ranges and generators in the same style and format as list comprehensions:

17.2 Working With Arrays

> [| 1 .. 10 |];; val it : int array = [|1; 2; 3; 4; 5; 6; 7; 8; 9;


10|] > [| 1 .. 3 .. 10 |];; val it : int array = [|1; 4; 7; 10|] >
[| for a in 1 .. 5 do yield (a, a*a, a*a*a) |];; val it : (int * Elements in an array are accessed by their index, or posiint * int) array = [|(1, 1, 1); (2, 4, 8); (3, 9, 27); (4, 16, tion in an array. Array indexes always start at 0 and end
at array.length - 1. For example, lets take the following
64); (5, 25, 125)|]
array:

17.1.3

System.Array Methods

let names = [| Juliet"; Monique"; Rachelle"; Tara";


Sophia |] (* Indexes: 0 1 2 3 4 *)

There are several methods in the System.Array module This list contains 5 items. The rst index is 0, and the last
index is 4.
for creating arrays:
val zeroCreate : int arraySize -> 'T []
Creates an array with arraySize elements. Each
element in the array holds the default value for
the particular data type (0 for numbers, false
for bools, null for reference types).

We can access items in the array using the .[index] operator, also called indexer notation. Here is the same array
in fsi:
> let names = [| Juliet"; Monique"; Rachelle"; Tara";
Sophia |];; val names : string array > names.[2];; val it
: string = Rachelle > names.[0];; val it : string = Juliet

> let (x : int array) = Array.zeroCreate 5;; val x : int


Instances of arrays have a Length property which returns
array > x;; val it : int array = [|0; 0; 0; 0; 0|]
the number of elements in the array:
48

17.2. WORKING WITH ARRAYS

49

> names.Length;; val it : int = 5 > for i = 0 to Rectangular Arrays


names.Length - 1 do printfn "%s (names.[i]);; Juliet
A rectangular array, which may be called a grid or a maMonique Rachelle Tara Sophia
trix, is an array of arrays, where all of the inner arrays
have the same length. Here is a simple 2x3 rectangular
Arrays are mutable data structures, meaning we can asarray in fsi:
sign elements in an array new values at any point in our
> Array2D.zeroCreate<int> 2 3;; val it : int [,] = [|[|0; 0;
program:
0|]; [|0; 0; 0|]|]
> names;; val it : string array = [|"Juliet"; Monique";
Rachelle"; Tara"; Sophia"|] > names.[4] <- Kristen";;
val it : unit = () > names;; val it : string array = [|"Juliet"; This array has 2 rows, and each row has 3 columns. To
access elements in this array, you use the .[row,col] opMonique"; Rachelle"; Tara"; Kristen"|]
erator:
If you try to access an element outside the range of an > let grid = Array2D.init<string> 3 3 (fun row col ->
sprintf row: %i, col: %i row col);; val grid : string [,]
array, you'll get an exception:
> grid;; val it : string [,] = [|[|"row: 0, col: 0"; row: 0,
> names.[1];; System.IndexOutOfRangeException:
col: 1"; row: 0, col: 2"|]; [|"row: 1, col: 0"; row: 1, col:
Index was outside the bounds of the array. at <Startup1"; row: 1, col: 2"|]; [|"row: 2, col: 0"; row: 2, col: 1";
Code$FSI_0029>.$FSI_0029._main() stopped due to
row: 2, col: 2"|]|] > grid.[0, 1];; val it : string = row: 0,
error > names.[5];; System.IndexOutOfRangeException:
col: 1 > grid.[1, 2];; val it : string = row: 1, col: 2
Index was outside the bounds of the array. at <StartupCode$FSI_0030>.$FSI_0030._main() stopped due to
Heres a simple program to demonstrate how to use and
error
iterate through multidimensional arrays:

17.2.1

Array Slicing

F# supports a few useful operators which allow programmers to return slices or subarrays of an array using the
.[start..nish] operator, where one of the start and nish
arguments may be omitted.
> let names = [|"0: Juliet"; 1: Monique"; 2: Rachelle";
3: Tara"; 4: Sophia"|];; val names : string array >
names.[1..3];; (* Grabs items between index 1 and 3 *)
val it : string [] = [|"1: Monique"; 2: Rachelle"; 3:
Tara"|] > names.[2..];; (* Grabs items between index 2
and last element *) val it : string [] = [|"2: Rachelle";
3: Tara"; 4: Sophia"|] > names.[..3];; (* Grabs items
between rst element and index 3 *) val it : string [] =
[|"0: Juliet"; 1: Monique"; 2: Rachelle"; 3: Tara"|]
Note that array slices generate a new array, rather than
altering the existing array. This requires allocating new
memory and copying elements from our source array into
our target array. If performance is a high priority, it is
generally more ecient to look at parts of an array using
a few index adjustments.

open System let printGrid grid = let maxY = (Array2D.length1 grid) - 1 let maxX = (Array2D.length2
grid) - 1 for row in 0 .. maxY do for col in 0 .. maxX
do if grid.[row, col] = true then Console.Write("*
") else Console.Write("_ ") Console.WriteLine() let
toggleGrid (grid : bool[,]) = Console.WriteLine()
Console.WriteLine(Toggle grid:") let row = Console.Write(Row: ") Console.ReadLine() |> int let col
= Console.Write(Col: ") Console.ReadLine() |> int
grid.[row, col] <- (not grid.[row, col]) let main() =
Console.WriteLine(Create a grid:") let rows = Console.Write(Rows: ") Console.ReadLine() |> int let
cols = Console.Write(Cols: ") Console.ReadLine()
|> int let grid = Array2D.zeroCreate<bool> rows
cols printGrid grid let mutable go = true while go do
toggleGrid grid printGrid grid Console.Write(Keep
playing (y/n)? ") go <- Console.ReadLine() = y
Console.WriteLine(Thanks for playing) main()
This program outputs the following:
Create a grid: Rows: 2 Cols: 3 _ _ _ _ _ _ Toggle grid:
Row: 0 Col: 1 _ * _ _ _ _ Keep playing (y/n)? y Toggle
grid: Row: 1 Col: 1 _ * _ _ * _ Keep playing (y/n)? y
Toggle grid: Row: 1 Col: 2 _ * _ _ * * Keep playing
(y/n)? n Thanks for playing

Multi-dimensional Arrays

In additional to the Array2D module, F# has an Array3D


module to support 3-dimensional arrays as well.

A multi-dimensional array is literally an array of arrays. Conceptually, its not any harder to work with these
types of arrays than single-dimensional arrays (as shown
above). Multi-dimensional arrays come in two forms:
rectangular and jagged arrays.

Note Its possible to create arrays with


more than 3 dimensions using the System.Array.CreateInstance method, but its generally recommended to avoid creating arrays
with huge numbers of elements or dimensions,

17.2.2

50

CHAPTER 17. ARRAYS


since it can quickly consume all of the available memory on a machine. For comparison,
an int is 4 bytes, and a 1000x1000x1000 int
array would consume about 3.7 GB of memory, more than the memory available on 99%
of desktop PCs.

Jagged arrays

val choose : ('T item -> 'U option) -> 'T[] input ->
'U[]
Filters and maps an array, returning a new array consisting of all elements which returned
Some.
val copy : 'T[] input -> 'T[]

A jagged array is an array of arrays, except each row in


Returns a copy of the input array.
the array does not necessary need to have the same number of elements:
val ll : 'T[] input -> int start -> int end -> 'T value
> [| for a in 1 .. 5 do yield [| 1 .. a |] |];; val it : int array -> unit
array = [|[|1|]; [|1; 2|]; [|1; 2; 3|]; [|1; 2; 3; 4|]; [|1; 2; 3; 4;
5|]|]
Assigns value to all the elements between the
start and end indexes.
You use the .[index] operator to access items in the array.
Since each element contains another array, its common val lter : ('T -> bool) -> 'T[] -> 'T[]
to see code that resembles .[row].[col]:
Returns a new array consisting of items ltered
> let jagged = [| for a in 1 .. 5 do yield [| 1 .. a |] |] for arr
out of the input array.
in jagged do for col in arr do printf "%i " col printfn "";;
val jagged : int array array 1 1 2 1 2 3 1 2 3 4 1 2 3 4 5
> jagged.[2].[2];; val it : int = 3 > jagged.[4].[0];; val it : val fold : ('State -> 'T -> 'State) -> 'State -> 'T[] input
-> 'State
int = 1
Note: Notice that the data type of a rectangular array is 'a[,], but the data type of a jagged
array is 'a array array. This results because
a rectangular array stores data at, whereas
a jagged array is literally an array of pointers to arrays. Since these two types of arrays are stored dierently in memory, F# treats
'a[,] and 'a array array as two dierent, noninterchangeable data types. As a result, rectangular and jagged arrays have slightly dierent
syntax for element access.
Rectangular arrays are stored in a slightly more
ecient manner and generally perform better
than jagged arrays, although there may not be
a perceptible dierence in most applications.
However, its worth noting performance dierences between the two in benchmark tests.

17.2.3

Using the Array Module

Accumulates left to right over an array.


val foldBack : ('T -> 'State -> 'State) -> 'T[] input ->
'State -> 'State
Accumulates right to left over an array.
val iter : ('T -> unit) -> 'T[] input -> unit
Applies a function to all of the elements of the
input array.
val length : 'T[] -> int
Returns the number of items in an array.
val map : ('T -> 'U) -> 'T[] -> 'U[]
Applies a mapping function to each element in
the input array to return a new array.

There are two array modules, System.Array and


Microsoft.FSharp.Collections.Array, developed by the val rev : 'T[] input -> 'T[]
.NET BCL designers and the creators of F# respectively.
Returns a new array with the items in reverse
Many of the methods and functions in the F# Array modorder.
ule are similar to those in the List module.
val append : 'T[] rst -> 'T[] second -> 'T[]
Returns a new array with elements consisting
of the rst array followed by the second array.

val sort : 'T[] -> 'T[]


Sorts a copy of an array.

17.3. DIFFERENCES BETWEEN ARRAYS AND LISTS


val sortInPlace : 'T[] -> unit
Sorts an array in place. Note that the sortInPlace method returns unit, indicating the sortInPlace mutates the original array.
val sortBy : ('T -> 'T -> int) -> 'T[] -> 'T[]
Sorts a copy of an array based on the sorting
function.
val sub : 'T[] -> int start -> int end -> 'T[]
Returns a sub array based on the given start and
end indexes.

17.3 Dierences Between Arrays


and Lists
Lists

Immutable, allows new lists to share nodes


with other lists.

List literals.

Pattern matching.

Supports mapping and folding.

51

17.3.1 Representation in Memory


Items in an array are represented in memory as adjacent
values in memory. For example, lets say we create the
following int array:
[| 15; 5; 21; 0; 9 |]
Represented in memory, our array resembles something
like this:
Memory Location: | 100 | 104 | 108 | 112 | 116 Value: |
15 | 5 | 21 | 0 | 9 Index: | 0 | 1 | 2 | 3 | 4
Each int occupies 4 bytes of memory. Since our array
contains 5 elements, the operating systems allocates 20
bytes of memory to hold this array (4 bytes * 5 elements
= 20 bytes). The rst rst element in the array occupies
memory 100-103, the second element occupies 104-107,
and so on.
We know that each element in the array is identied by
its index or position in the array. Literally, the index is
an oset: since the array starts at memory location 100,
and each element in the array occupies a xed amount of
memory, the operating system can know the exact address
of each element in memory using the formula:
Start memory address of element at index n
= StartPosition of array + (n * length of data
type)
End memory address of element at index n =
Start memory address + length of data type

In laymens terms, this means we can access the nth element of any array in constant time, or in O(1) operations.

Linear lookup time.


This is in contrast to lists, where accessing the nth element

No random access to elements, just requires O(n) operations.


forward-only traversal.
With the understanding that elements in an array occupy
adjacent memory locations, we can deduce two properties
of arrays:

Arrays

Array literals.

Constant lookup time.

Good spacial locality of reference ensures


ecient lookup time.
Indexes indicate the position of each element relative to others, making arrays ideal for
random access.
Supports mapping and folding.
Mutability prevents arrays from sharing
nodes with other elements in an array.
Not resizable.

1. Creating arrays requires programmers to specify the


size of the array upfront, otherwise the operating
system won't know how many adjacent memory locations to allocate.
2. Arrays are not resizable, because memory locations
before the rst element or beyond the last element
may hold data used by other applications. An array is only resized by allocating a new block of
memory and copying all of the elements from the
old array into the new array.

Chapter 18

Mutable Collections
The .NET BCL comes with its own suite of
mutable collections which are found in the
System.Collections.Generic namespace. These builtin collections are very similar to their immutable
counterparts in F#.

18.1 List<'T> Class


The List<'T> class represents a strongly typed list of objects that can be accessed by index. Conceptually, this
makes the List<'T> class similar to arrays. However, unlike arrays, Lists can be resized and don't need to have
their size specied on declaration.
.NET lists are created using the new keyword and calling
the lists constructor as follows:
> open System.Collections.Generic;; > let myList
= new List<string>();; val myList : List<string>
> myList.Add(hello);; val it :
unit = () >
myList.Add(world);; val it : unit = () > myList.[0];; val
it : string = hello > myList |> Seq.iteri (fun index item
-> printfn "%i: %s index myList.[index]);; 0: hello 1:
world

sized:
open System open System.Collections.Generic let items
= new List<string>() let printList (l : List<_>) =
printfn l.Count: %i, l.Capacity: %i l.Count l.Capacity
printfn Items:" l |> Seq.iteri (fun index item -> printfn
" l.[%i]: %s index l.[index]) printfn "-----------" let
main() = printList items items.Add(monkey) printList items items.Add(kitty) items.Add(bunny) printList items items.Add(doggy) items.Add(octopussy)
items.Add(ducky) printList items printfn Removing
entry for \"doggy\"\n--------\n items.Remove(doggy)
|> ignore printList items printfn Removing item at index
3\n--------\n items.RemoveAt(3) printList items Console.ReadKey(true) |> ignore main()
l.Count: 0, l.Capacity: 0 Items: ----------- l.Count: 1,
l.Capacity: 4 Items: l.[0]: monkey ----------- l.Count:
3, l.Capacity: 4 Items: l.[0]: monkey l.[1]: kitty l.[2]:
bunny ----------- l.Count: 6, l.Capacity: 8 Items: l.[0]:
monkey l.[1]: kitty l.[2]: bunny l.[3]: doggy l.[4]: octopussy l.[5]: ducky ----------- Removing entry for doggy
-------- l.Count: 5, l.Capacity: 8 Items: l.[0]: monkey
l.[1]: kitty l.[2]: bunny l.[3]: octopussy l.[4]: ducky ---------- Removing item at index 3 -------- l.Count: 4,
l.Capacity: 8 Items: l.[0]: monkey l.[1]: kitty l.[2]:
bunny l.[3]: ducky -----------

Its easy to tell that .NET lists are mutable because their If you know the maximum size of the list beforehand,
Add methods return unit rather than returning another it is possible to avoid the performance hit by calling the
List<'T>(size : int) constructor instead. The following
list.
sample demonstrates how to add 1000 items to a list without resizing the internal array:

18.1.1

Underlying Implementation

> let myList = new List<int>(1000);; val myList :


List<int> > myList.Count, myList.Capacity;; val it : int
Behind the scenes, the List<'T> class is just a fancy wrap- * int = (0, 1000) > seq { 1 .. 1000 } |> Seq.iter (fun x
per for an array. When a List<'T> is constructed, it cre- -> myList.Add(x));; val it : unit = () > myList.Count,
ates an 4-element array in memory. Adding the rst 4 myList.Capacity;; val it : int * int = (1000, 1000)
items is an O(1) operation. However, as soon as the 5th
element needs to be added, the list doubles the size of the
internal array, which means it has to reallocate new memory and copy elements in the existing list; this is a O(n)
18.2 LinkedList<'T> Class
operation, where n is the number of items in the list.
The List<'T>.Count property returns the number of items
currently held in the collection, the List<'T>.Capacity A LinkedList<'T> represented a doubly-linked sequence
collection returns the size of the underlying array. This of nodes which allows ecient O(1) inserts and removal,
code sample demonstrates how the underlying array is re- supports forward and backward traversal, but its imple52

18.2. LINKEDLIST<'T> CLASS

53

mentation prevents ecient random access. Linked lists in the stack is at the bottom of the stack, and the last coin
have a few valuable methods:
in the stack appears at the top. We remove coins from top
(* Prepends an item to the LinkedList *) val AddFirst to bottom, so the last coin added to the stack is the rst
: 'T -> LinkedListNode<'T> (* Appends an items to one removed.
the LinkedList *) val AddLast : 'T -> LinkedListNode<'T> (* Adds an item before a LinkedListNode *) val
AddBefore : LinkedListNode<'T> -> 'T -> LinkedListNode<'T> (* Adds an item after a LinkedListNode *) val
AddAfter : LinkedListNode<'T> -> 'T -> LinkedListNode<'T>

Push

Pop

Note that these methods return a LinkedListNode<'T>,


not a new LinkedList<'T>. Adding nodes actually mutates the LinkedList object:
>
open
System.Collections.Generic;;
>
let
items = new LinkedList<string>();; val items :
LinkedList<string> > items.AddLast(AddLast1);;
val it :
LinkedListNode<string> = System.Collections.Generic.LinkedListNode`1[System.String] A simple representation of a stack
{List = seq ["AddLast1"]; Next = null; Previous = null;
Value = AddLast1";} > items.AddLast(AddLast2);;
val it :
LinkedListNode<string> = System.Collections.Generic.LinkedListNode`1[System.String]
{List
=
seq
["AddLast1";
AddLast2"];
Next = null;
Previous = Sys- 18.2.2 Queue<'T> Class
tem.Collections.Generic.LinkedListNode`1[System.String];
Value = AddLast2";} > let rstItem =
items.AddFirst(AddFirst1);;
val
rstItem A Queue<'T> only allows programmers to ap:
LinkedListNode<string> > let addAfter = pend/enqueue to the rear of a list and remove/dequeue
items.AddAfter(rstItem, addAfter);; val addAfter from the front of a list, which makes it a rst in, rst out
:
LinkedListNode<string> > let addBefore = (FIFO) data structure.
items.AddBefore(addAfter, addBefore);; val ad- > let queue = new Queue<string>();; val queue
dBefore : LinkedListNode<string> > items |> Seq.iter :
Queue<string> > queue.Enqueue(First);; (*
(fun x -> printfn "%s x);; AddFirst1 addBefore addAfter Adds item to the rear of the list *) val it : unit
AddLast1 AddLast2
= () > queue.Enqueue(Second);; val it : unit =
() > queue.Enqueue(Third);; val it : unit = () >
The Stack<'T> and Queue<'T> classes are special cases queue.Dequeue();; (* Returns and removes item
of a linked list (they can be thought of as linked lists with from front of the queue *) val it : string = First
> queue.Dequeue();; val it : string = Second >
restrictions on where you can add and remove items).
queue.Dequeue();; val it : string = Third

18.2.1

Stack<'T> Class

A line of people might be represented by a queue: people


add themselves to the rear of the line, and are removed
A Stack<'T> only allows programmers prepend/push and from the front. The rst person to stand in line is the rst
remove/pop items from the front of a list, which makes it person to be served.
a last in, rst out (LIFO) data structure. The Stack<'T>
class can be thought of as a mutable version of the F# list.
> stack.Push(First);; (* Adds item to front of the list *)
val it : unit = () > stack.Push(Second);; val it : unit = ()
> stack.Push(Third);; val it : unit = () > stack.Pop();;
(* Returns and removes item from front of the list *)
val it : string = Third > stack.Pop();; val it : string =
Second > stack.Pop();; val it : string = First
A stack of coins could be represented with this data structure. If we stacked coins one on top another, the rst coin

54

CHAPTER 18. MUTABLE COLLECTIONS

18.3 HashSet<'T>,
tionary<'TKey,
Classes

and Dic'TValue>

The HashSet<'T> and Dictionary<'TKey, 'TValue>


classes are mutable analogs of the F# set and map data
structures and contain many of the same functions.
Using the HashSet<'T>
open System open System.Collections.Generic let
nums_1to10 = new HashSet<int>() let nums_5to15 =
new HashSet<int>() let main() = let printCollection
msg targetSet = printf "%s: " msg targetSet |> Seq.sort
|> Seq.iter(fun x -> printf "%O " x) printfn "" let
addNums min max (targetSet : ICollection<_>) = seq
{ min .. max } |> Seq.iter(fun x -> targetSet.Add(x))
addNums 1 10 nums_1to10 addNums 5 15 nums_5to15
printCollection nums_1to10 (before)" nums_1to10
printCollection nums_5to15 (before)" nums_5to15
nums_1to10.IntersectWith(nums_5to15) (* mutates
nums_1to10 *) printCollection nums_1to10 (after)"
nums_1to10 printCollection nums_5to15 (after)"
nums_5to15 Console.ReadKey(true) |> ignore main()
nums_1to10 (before): 1 2 3 4 5 6 7 8 9 10 nums_5to15
(before): 5 6 7 8 9 10 11 12 13 14 15 nums_1to10
(after): 5 6 7 8 9 10 nums_5to15 (after): 5 6 7 8 9 10 11
12 13 14 15
Using the Dictionary<'TKey, 'TValue>
> open System.Collections.Generic;; > let dict =
new Dictionary<string, string>();; val dict : Dictionary<string,string> > dict.Add(Gareld, Jim Davis);;
val it : unit = () > dict.Add(Farside, Gary Larson);;
val it : unit = () > dict.Add(Calvin and Hobbes, Bill
Watterson);; val it : unit = () > dict.Add(Peanuts,
Charles Schultz);; val it : unit = () > dict.["Farside"];;
(* Use the '.[key]' operator to retrieve items *) val it :
string = Gary Larson

18.4 Dierences Between .NET


BCL and F# Collections
The major dierence between the collections built into
the .NET BCL and their F# analogs is, of course, mutability. The mutable nature of BCL collections dramatically
aects their implementation and time-complexity:
* These classes are built on top of internal arrays. They may take a performance hit as the
internal arrays are periodically resized when
adding items.
Note: the Big-O notation above refers to the
time-complexity of the insert/remove/retrieve

operations relative to the number of items in


the data structure, not the relative amount of
time required to evaluate the operations relative to other data structures. For example, accessing arrays by index vs. accessing dictionaries by key have the same time complexity,
O(1), but the operations do not necessarily occur in the same amount of time.

Chapter 19

Input and Output


Input and output, also called I/O, refers to any kind of
%[ags][width][.precision][type]
communication between two hardware devices or between the user and the computer. This includes printing [ags] (optional)
text out to the console, reading and writing les to disk,
sending data over a socket, sending data to the printer, Valid ags are:
and a wide variety of other common tasks.
0: add zeros instead of spaces to make up the
This page is not intended to provide an exhaustive look
required width
at .NETs I/O methods (readers seeking exhaustive refer'-': left justify the result within the width specences are encouraged to review the excellent documentaied
tion on the System.IO namespace on MSDN). This page
will provide a cursory overview of some of the basic
'+': add a '+' character if the number is positive
methods available to F# programmers for printing and
(to match a '-' sign for negatives)
working with les.
' ': add an extra space if the number is positive
(to match a '-' sign for negatives)

19.1 Working with the Console

[width] (optional)

The optional width is an integer indicating the minimal


width of the result. For instance, %6d prints an integer,
19.1.1 With F#
prexing it with spaces to ll at least 6 characters. If width
is '*', then an extra integer argument is taken to specify
By now, you're probably familiar with the printf, printfn, the corresponding width.
sprintf and its variants in the Printf module. However,
just to describe these methods more formally, these methany number
ods are used for printf-style printing and formatting using
'*':
% markers as placeholders:
Print methods take a format string and a series of argu[.precision] (optional)
ments, for example:
> sprintf Hi, I'm %s and I'm a %s Juliet Scorpio";; Represents the number of digits after a oating point
number.
val it : string = Hi, I'm Juliet and I'm a Scorpio
> sprintf "%.2f 12345.67890;; val it : string =
Methods in the Printf module are type-safe. For exam- 12345.68 > sprintf "%.7f 12345.67890;; val it : string
= 12345.6789000
ple, attempting to use substitute an int placeholder with a
string results in a compilation error:
> sprintf I'm %i years old kitty";; sprintf I'm [type] (required)
%i years old kitty";;
--------------------------- The following placeholder types are interpreted as fol^^^^^^^^ stdin(17,28): error FS0001: The type lows:
'string' is not compatible with any of the types %b: bool, formatted as true or false %s: string,
byte,int16,int32,int64,sbyte,uint16,uint32,uint64,nativeint,unativeint,
formatted as its unescaped contents %d, %i: any basic
arising from the use of a printf-style format string.
integer type formatted as a decimal integer, signed if
the basic integer type is signed. %u: any basic integer
According to the F# documentation, % placeholders con- type formatted as an unsigned decimal integer %x, %X,
%o: any basic integer type formatted as an unsigned
sist of the following:
55

56

CHAPTER 19. INPUT AND OUTPUT

hexadecimal (a-f)/Hexadecimal (A-F)/Octal integer %e, See Number Format Strings, Date and Time Format
%E, %f, %F, %g, %G: any basic oating point type Strings, and Enum Format Strings for a comprehensive
(oat,oat32) formatted using a C-style oating point reference on format speciers for .NET.
format specications, i.e %e, %E: Signed value having Programmers can read and write to the console using the
the form [-]d.dddde[sign]ddd where d is a single decimal System.Console class:
digit, dddd is one or more decimal digits, ddd is exactly
three decimal digits, and sign is + or - %f: Signed value open System let main() = Console.Write(Whats
having the form [-]dddd.dddd, where dddd is one or more your name? ") let name = Console.ReadLine() Condecimal digits. The number of digits before the decimal sole.Write(Hello, {0}", name) main()
point depends on the magnitude of the number, and the
number of digits after the decimal point depends on the
requested precision. %g, %G: Signed value printed in
f or e format, whichever is more compact for the given 19.2 System.IO Namespace
value and precision. %M: System.Decimal value %O:
Any value, printed by boxing the object and using its The System.IO namespace contains a variety of useful
ToString method(s) %A: Any value, printed by using Mi- classes for performing basic I/O.
crosoft.FSharp.Text.StructuredFormat.Display.any_to_string
with the default layout settings %a: A general format
specier, requires two arguments: (1) a function which 19.2.1 Files and Directories
accepts two arguments: (a) a context parameter of
the appropriate type for the given formatting func- The following classes are useful for interrogating the host
tion (e.g. an #System.IO.TextWriter) (b) a value to lesystem:
print and which either outputs or returns appropriate
text. (2) the particular value to print %t: A gen The System.IO.File class exposes several useful
eral format specier, requires one argument: (1) a
members for creating, appending, and deleting les.
function which accepts a context parameter of the
appropriate type for the given formatting function
System.IO.Directory exposes methods for creating,
(e.g. an #System.IO.TextWriter)and which either outmoving, and deleting directories.
puts or returns appropriate text. Basic integer types are:
System.IO.Path performs operations on strings
byte,sbyte,int16,uint16,int32,uint32,int64,uint64,nativeint,unativeint
which represent le paths.
Basic oating point types are: oat, oat32
Programmers can print to the console using the printf
method, however F# recommends reading console input
using the System.Console.ReadLine() method.

System.IO.FileSystemWatcher which allows users


to listen to a directory for changes.

19.2.2 Streams
19.1.2

With .NET

A stream is a sequence of bytes. .NET provides some


.NET includes its own notation for format speciers. classes which allow programmers to work with steams in.NET format strings are untyped. Additionally, .NETs cluding:
format strings are designed to be extensible, meaning that
a programmer can implement their own custom format
System.IO.StreamReader which is used to read
strings. Format placeholders have the following form:
characters from a stream.
{index[, length][:formatString]}
For example, using the String.Format method in fsi:
> System.String.Format(Hi, my name is {0}
and I'm a {1}", Juliet, Scorpio);; val it :
string = Hi, my name is Juliet and I'm a Scorpio > System.String.Format("|{0,50}|",
Left
justied);; val it : string = "|Left justied |"
> System.String.Format("|{0,50}|",
Right justied);; val it : string = "| Right justied|" >
System.String.Format("|{0:yyyy-MMM-dd}|",
System.DateTime.Now);; val it : string = "|2009-Apr-06|"

System.IO.StreamWriter which is used to write


characters to a stream.
System.IO.MemoryStream which creates an inmemory stream of bytes.

Chapter 20

Exception Handling
When a program encounters a problem or enters an in- FormatException
valid state, it will often respond by throwing an exception.
Left to its own devices, an uncaught exception will crash
Occurs when s does not represent a numeric inan application. Programmers write exception handling
put.
code to rescue an application from an invalid state.
OverowException

20.1 Try/With

Occurs when s represents number greater


than or less than Int32.MaxValue or
Int32.MinValue (i.e.
the number cannot
be represented with a 32-bit signed integer).

Lets look at the following code:

let
getNumber
msg
=
printf
msg;
int32(System.Console.ReadLine()) let x = getNumWe can catch all of these exceptions by adding additional
ber(x = ") let y = getNumber(y = ") printfn "%i + %i
match cases:
= %i x y (x + y)
let getNumber msg = printf msg;
try
int32(System.Console.ReadLine())
with
|
:?
This code is syntactically valid, and it has the correct
System.FormatException -> 1 | :?
Systypes. However, it can fail at run time if we give it a
tem.OverowException -> System.Int32.MinValue
bad input:
| :? System.ArgumentNullException -> 0
This program outputs the following:
x = 7 y = monkeys! ------------ FormatException was un- Its not necessary to have an exhaustive list of match cases
handled. Input string was not in a correct format.
on exception types, as the uncaught exception will simply
The string monkeys does not represent a number, so the move to the next method in the stack trace.
conversion fails with an exception. We can handle this
exception using F#'s try... with, a special kind of pattern
matching construct:
let getNumber msg = printf msg;
try
int32(System.Console.ReadLine()) with | :?
System.FormatException -> System.Int32.MinValue let x
= getNumber(x = ") let y = getNumber(y = ") printfn
"%i + %i = %i x y (x + y)

20.2 Raising Exceptions


The code above demonstrates how to recover from an invalid state. However, when designing F# libraries, its often useful to throw exceptions to notify users that the program encountered some kind of invalid input. There are
several standard functions for raising exceptions:

(* General failure *) val failwith : string -> 'a (* General


failure with formatted message *) val failwithf : Stringx = 7 y = monkeys! 7 + 2147483648 = 2147483641 Format<'a, 'b> -> 'a (* Raise a specic exception *) val
It is, of course, wholly possible to catch multiple types of raise : #exn -> 'a (* Bad input *) val invalidArg : string
exceptions in a single with block. For example, according -> 'a
to the MSDN documentation, the System.Int32.Parse(s :
string) method will throw three types of exceptions:
For example:
This program outputs the following:

ArgumentNullException
Occurs when s is a null reference.

type 'a tree = | Node of 'a * 'a tree * 'a tree | Empty let
rec add x = function | Empty -> Node(x, Empty, Empty)
| Node(y, left, right) -> if x > y then Node(y, left, add
57

58

CHAPTER 20. EXCEPTION HANDLING

x right) else if x < y then Node(y, add x left, right) else ministic cleanup of unmanaged resources. Its considered
failwithf Item '%A' has already been added to tree x
a best practice to call Dispose on these types of objects
as soon as they are no longer needed.
Traditionally, we'd use a try/nally block in this fashion:
let writeToFile leName = let sw = new System.IO.StreamWriter(leName
:
string)
try
sw.Write(Hello
")
sw.Write(World!")
nally
Normally, an exception will cause a function to exit im- sw.Dispose()
mediately. However, a nally block will always execute,
even if the code throws an exception:
However, this can be occasionally bulky and cumberlet tryWithFinallyExample f = try printfn tryWithFi- some, especially when dealing with many objects which
nallyExample: outer try block try printfn tryWith- implement the IDisposable interface. F# provides the
FinallyExample: inner try block f() with | exn -> keyword use as syntactic sugar for the pattern above. An
printfn tryWithFinallyExample: inner with block equivalent version of the code above can be written as
reraise() (* raises the same exception we just caught *) follows:
nally printfn tryWithFinally: outer nally block let
catchAllExceptions f = try printfn "-------------" printfn let writeToFile leName = use sw = new SyscatchAllExceptions: try block tryWithFinallyExample tem.IO.StreamWriter(leName : string) sw.Write(Hello
f with | exn -> printfn catchAllExceptions: with block ") sw.Write(World!")
printfn Exception message: %s exn.Message let
main() = catchAllExceptions (fun () -> printfn Function The scope of a use statement is identical to the scope of a
executed successfully) catchAllExceptions (fun () -> let statement. F# will automatically call Dispose() on an
failwith Function executed with an error) main()
object when the identier goes out of scope.

20.3 Try/Finally

This program will output the following:


------------- catchAllExceptions: try block tryWithFinallyExample: outer try block tryWithFinallyExample: inner try block Function executed successfully tryWithFinally: outer nally block ------------- catchAllExceptions:
try block tryWithFinallyExample: outer try block tryWithFinallyExample: inner try block tryWithFinallyExample: inner with block tryWithFinally: outer nally
block catchAllExceptions: with block Exception message: Function executed with an error
Notice that our nally block executed in spite of the exception. Finally blocks are used most commonly to clean
up resources, such as closing an open le handle or closing
a database connection (even in the event of an exception,
we do not want to leave le handles or database connections open):

20.4 Dening New Exceptions


F# allows us to easily dene new types of exceptions using
the exception declaration. Heres an example using fsi:
> exception ReindeerNotFoundException of string let
reindeer = ["Dasher"; Dancer"; Prancer"; Vixen";
Comet"; Cupid"; Donner"; Blitzen"] let getReindeerPosition name = match List.tryFindIndex
(fun x -> x = name) reindeer with | Some(index)
-> index | None -> raise (ReindeerNotFoundException(name));; exception ReindeerNotFoundException
of string val reindeer : string list val getReindeerPosition : string -> int > getReindeerPosition Comet";;
val it : int = 4 > getReindeerPosition Donner";;
val it : int = 6 > getReindeerPosition Rudolf";;
FSI_0033+ReindeerNotFoundExceptionException:
Rudolf at FSI_0033.getReindeerPosition(String name)
at
<StartupCode$FSI_0036>.$FSI_0036._main()
stopped due to error

open System.Data.SqlClient let executeScalar connectionString sql = let conn = new SqlConnection(connectionString) try conn.Open() (* this line
can throw an exception *) let comm = new SqlCommand(sql, conn) comm.ExecuteScalar() (* this line can
throw an exception *) nally (* nally block guarantees
We can pattern match on our new existing exception type
our SqlConnection is closed, even if our sql statement
just as easily as any other exception:
fails *) conn.Close()
> let tryGetReindeerPosition name = try getReindeerPosition name with | ReindeerNotFoundException(s)
-> printfn Got ReindeerNotFoundException: %s
s 1;; val tryGetReindeerPosition : string -> int >
20.3.1 use Statement
tryGetReindeerPosition Comet";; val it : int = 4 >
Many objects in the .NET framework implement the tryGetReindeerPosition Rudolf";; Got ReindeerNotSystem.IDisposable interface, which means the objects FoundException: Rudolf val it : int = 1
have a special method called Dispose to guarantee deter-

20.5. EXCEPTION HANDLING CONSTRUCTS

20.5 Exception
structs

Handling

59

Con-

Chapter 21

Operator Overloading
Operator overloading allows programmers to provide new F# supports two types of operators: inx operators and
behavior for the default operators in F#. In practice, pro- prex operators.
grammers overload operators to provide a simplied syntax for objects which can be combined mathematically.

21.3.1 Inx operators

You've already used operators:

An inx operator takes two arguments, with the operator appearing in between both arguments (i.e. arg1 {op}
arg2). We can dene our own inx operators using the
syntax:

let sum = x + y

let (op) arg1 arg2 = ...

21.1 Using Operators

Here + is example of using a mathematical addition op- In addition to mathematical operators, F# has a variety of
inx operators dened as part of its library, for example:
erator.
let inline (|>) x f = f x let inline (::) hd tl = Cons(hd, tl)
let inline (:=) (x : 'a ref) value = x.contents <- value

21.2 Operator Overloading


Operators are functions with special names, enclosed in
brackets. They must be dened as static class members.
Heres an example on declaring + operator on complex
numbers:

Lets say we're writing an application which performs a


lot of regex matching and replacing. We can match text
using Perl-style operators by dening our own operators
as follows:

open System.Text.RegularExpressions let (=~) input


type Complex = { Re: double Im: double } static pattern = Regex.IsMatch(input, pattern) let main() =
member ( + ) (left: Complex, right: Complex) = { Re = printfn cat =~ dog: %b (cat =~ dog) printfn cat
=~ cat|dog: %b (cat =~ cat|dog) printfn monkey
left.Re + right.Re; Im = left.Im + right.Im }
=~ monk*: %b (monkey =~ monk*") main()
In FSI, we can add two complex numbers as follows:

This program outputs the following:


> let rst = { Re = 1.0; Im = 7.0 };; val rst : Complex
> let second = { Re = 2.0; Im = 10.5 };; val second : cat =~ dog: false cat =~ cat|dog: true monkey =~ monk*:
Complex > rst + second;; val it : Complex = {Re = 3.0; true
Im = 3.5;}

21.3.2 Prex Operators

21.3 Dening New Operators

Prex operators take a single argument which appears to


the right side of the operator ({op}argument). You've
already seen how the ! operator is dened for ref cells:

In addition to overloading existing operators, its possible type 'a ref = { mutable contents : 'a } let (!) (x : 'a ref) =
to dene new operators. The names of custom operators x.contents
can only be one or more of the following characters:
!%&*+./<=>?@^|~

Lets say we're writing a number crunching application,


and we wanted to dene some operators that work on lists
60

21.3. DEFINING NEW OPERATORS


of numbers. We might dene some prex operators in fsi
as follows:
> let ( !+ ) l = List.reduce ( + ) l let ( !- ) l = List.reduce (
- ) l let ( !* ) l = List.reduce ( * ) l let ( !/ ) l = List.reduce
( / ) l;; val ( !+ ) : int list -> int val ( !- ) : int list -> int val
( !* ) : int list -> int val ( !/ ) : int list -> int > !* [2; 3;
5];; val it : int = 30 > !+ [2; 3; 5];; val it : int = 10 > !- [2;
3; 7];; val it : int = 8 > !/ [100; 10; 2];; val it : int = 5

61

Chapter 22

Classes
In the real world, an object is a real thing. A cat, per- 22.1.1 Implicit Class Construction
son, computer, and a roll of duct tape are all real things
in the tangible sense. When we think about these things, Implicit class syntax is dened as follows:
we can broadly describe them in terms of a number of
type TypeName optional-type-arguments arguments [
attributes:
as ident ] = [ inherit type { as base } ] [ let-binding |
let-rec bindings ] * [ do-statement ] * [ abstract-binding |
Properties: a person has a name, a cat has four legs, member-binding | interface-implementation ] *
computers have a price tag, duct tape is sticky.
Behaviors: a person reads the newspaper, cats sleep
all day, computers crunch numbers, duct tape attaches things to other things.

Elements in brackets are optional, elements followed by a * may appear zero or more times.

This syntax above is not as daunting as it looks. Heres a


Types/group membership: an employee is a type of
simple class written in implicit style:
person, a cat is a pet, a Dell and Mac are types of
computers, duct tape is part of the broader family of type Account(number : int, holder : string) = class let
mutable amount = 0m member x.Number = number
adhesives.
member x.Holder = holder member x.Amount = amount
member x.Deposit(value) = amount <- amount + value
In the programming world, an object is, in the simplest
member x.Withdraw(value) = amount <- amount - value
of terms, a model of something in the real world. Object- end
oriented programming (OOP) exists because it allows
programmers to model real-world entities and simulate
their interactions in code. Just like their real-world coun- The code above denes a class called Account, which has
terparts, objects in computer programming have proper- three properties and two methods. Lets take a closer look
ties and behaviors, and can be classied according to their at the following:
type.
type Account(number : int, holder : string) = class
While we can certainly create objects that represents cats, The underlined code is called the class constructor. A
people, and adhesives, objects can also represent less con- constructor is a special kind of function used to initialcrete things, such as a bank account or a business rule.
ize the elds in an object. In this case, our constructor
Although the scope of OOP has expanded to include denes two values, number and holder, which can be acsome advanced concepts such as design patterns and the cessed anywhere in our class. You create an instance of
large-scale architecture of applications, this page will Account by using the new keyword and passing the apkeep things simple and limit the discussion of OOP to propriate parameters into the constructor as follows:
data modeling.
let bob = new Account(123456, Bobs Saving)
Additionally, lets look at the way a member is dened:

22.1 Dening an Object

member x.Deposit(value) = amount <- amount + value

Before you create an object, you have to identify the properties of your object and describe what it does. You dene properties and methods of an object in a class. There
are actually two dierent syntaxes for dening a class: an
implicit syntax and an explicit syntax.

The x above is an alias for the object currently in scope.


Most OO languages provide an implicit this or self variable to access the object in scope, but F# requires programmers to create their own alias.
After we can create an instance of our Account, we

62

22.1. DEFINING AN OBJECT

63

can access its properties using .propertyName notation. 67890, x.Holder: Marge, x.Amount: 145
Heres an example in FSI:
> let printAccount (x : Account) = printfn x.Number:
%i, x.Holder: %s, x.Amount: %M x.Number x.Holder
x.Amount;; val printAccount : Account -> unit >
let bob = new Account(123456, Bobs Savings);;
val bob : Account > printAccount bob;; x.Number:
123456, x.Holder: Bobs Savings, x.Amount: 0 val it
: unit = () > bob.Deposit(100M);; val it : unit = ()
> printAccount bob;; x.Number: 123456, x.Holder:
Bobs Savings, x.Amount: 100 val it : unit = () >
bob.Withdraw(29.95M);; val it : unit = () > printAccount
bob;; x.Number: 123456, x.Holder: Bobs Savings,
x.Amount: 70.05

Example
Lets use this class in a real program:
open System type Account(number : int, holder : string)
= class let mutable amount = 0m member x.Number =
number member x.Holder = holder member x.Amount
= amount member x.Deposit(value) = amount <amount + value member x.Withdraw(value) = amount
<- amount - value end let homer = new Account(12345,
Homer) let marge = new Account(67890, Marge) let
transfer amount (source : Account) (target : Account)
= source.Withdraw amount target.Deposit amount let
printAccount (x : Account) = printfn x.Number: %i,
x.Holder: %s, x.Amount: %M x.Number x.Holder
x.Amount let main() = let printAccounts() = [homer;
marge] |> Seq.iter printAccount printfn "\nInializing
account homer.Deposit 50M marge.Deposit 100M
printAccounts() printfn "\nTransferring $30 from Marge
to Homer transfer 30M marge homer printAccounts()
printfn "\nTransferring $75 from Homer to Marge
transfer 75M homer marge printAccounts() main()
The program has the following types:

Example using the do keyword


The do keyword is used for post-constructor initialization. For example, lets say we wanted to create an object which represents a stock. We only need to pass in
the stock symbol, and initialize the rest of the properties
in our constructor:
open System open System.Net type Stock(symbol
:
string) = class let url = "http://download.
finance.yahoo.com/d/quotes.csv?s=" + symbol +
"&f=sl1d1t1c1ohgv&e=.csv let mutable _symbol =
String.Empty let mutable _current = 0.0 let mutable
_open = 0.0 let mutable _high = 0.0 let mutable _low
= 0.0 let mutable _volume = 0 do (* We initialize
our object in the do block *) let webClient = new
WebClient() (* Data comes back as a comma-seperated
list, so we split it on each comma *) let data = webClient.DownloadString(url).Split([|','|]) _symbol <data.[0] _current <- oat data.[1] _open <- oat data.[5]
_high <- oat data.[6] _low <- oat data.[7] _volume
<- int data.[8] member x.Symbol = _symbol member
x.Current = _current member x.Open = _open member x.High = _high member x.Low = _low member
x.Volume = _volume end let main() = let stocks =
["msft"; noc"; yhoo"; gm"] |> Seq.map (fun x -> new
Stock(x)) stocks |> Seq.iter (fun x -> printfn Symbol:
%s (%F)" x.Symbol x.Current) main()
This program has the following types:
type Stock = class new : symbol:string -> Stock member
Current : oat member High : oat member Low : oat
member Open : oat member Symbol : string member
Volume : int end
This program outputs the following (your outputs will
vary):

Symbol: MSFT (19.130000) Symbol: NOC


type Account = class new : number:int * holder:string (43.240000) Symbol: YHOO (12.340000) Symbol:
-> Account member Deposit : value:decimal -> unit GM (3.660000)
member Withdraw : value:decimal -> unit member
Amount : decimal member Holder : string member
Note: Its possible to have any number of do
Number : int end val homer : Account val marge :
statements in a class denition, although theres
Account val transfer : decimal -> Account -> Account
no particular reason why you'd need more than
-> unit val printAccount : Account -> unit
one.
The program outputs the following:
Initializing account x.Number: 12345, x.Holder: Homer,
x.Amount: 50 x.Number: 67890, x.Holder: Marge,
x.Amount: 100 Transferring $30 from Marge to Homer
x.Number: 12345, x.Holder: Homer, x.Amount: 80
x.Number: 67890, x.Holder: Marge, x.Amount: 70
Transferring $75 from Homer to Marge x.Number:
12345, x.Holder: Homer, x.Amount: 5 x.Number:

22.1.2 Explicit Class Denition


Classes written in explicit style follow this format:
type TypeName = [ inherit type ] [ val-denitions ] [
new ( optional-type-arguments arguments ) [ as ident ]
= { eld-initialization } [ then constructor-statements
] ] * [ abstract-binding | member-binding | interface-

64
implementation ] *
Heres a class dened using the explicit syntax:

CHAPTER 22. CLASSES


type Car = class val used: bool val owner: string val
mutable mileage: int new : owner:string * mileage:int ->
Car new : owner:string -> Car end

type Line = class val X1 : oat val Y1 : oat val X2 :


Notice that our val elds are included in the public interoat val Y2 : oat new (x1, y1, x2, y2) = { X1 = x1; Y1
= y1; X2 = x2; Y2 = y2} member x.Length = let sqr x = face of our class denition.
x * x sqrt(sqr(x.X1 - x.X2) + sqr(x.Y1 - x.Y2) ) end
This program outputs the following:
Each val denes a eld in our object. Unlike other objectoriented languages, F# does not implicitly initialize elds
in a class to any value. Instead, F# requires programmers
to dene a constructor and explicitly initialize each eld
in their object with a value.
We can perform some post-constructor processing using
a then block as follows:
type Line = class val X1 : oat val Y1 : oat val X2 :
oat val Y2 : oat new (x1, y1, x2, y2) as this = { X1
= x1; Y1 = y1; X2 = x2; Y2 = y2;} then printfn Line
constructor: {(%F, %F), (%F, %F)}, Line.Length: %F
this.X1 this.Y1 this.X2 this.Y2 this.Length member
x.Length = let sqr x = x * x sqrt(sqr(x.X1 - x.X2) +
sqr(x.Y1 - x.Y2) ) end

Cars created c.used: false, c.owner: Steve, c.mileage:


0 c.used: true, c.owner: Bob, c.mileage: 83000 Steve
drives all over the state c.used: false, c.owner: Steve,
c.mileage: 780 c.used: true, c.owner: Bob, c.mileage:
83000 Bob commits odometer fraud c.used: false,
c.owner: Steve, c.mileage: 780 c.used: true, c.owner:
Bob, c.mileage: 0

22.1.3 Dierences Between Implicit and


Explicit Syntaxes

As you've probably guessed, the major dierence between the two syntaxes is related to the constructor: the
explicit syntax forces a programmer to provide explicit
constructor(s), whereas the implicit syntax fuses the primary constructor with the class body. However, there are
Notice that we have to add an alias after our construc- a few other subtle dierences:
tor, new (x1, y1, x2, y2) as this), to access the elds of
The explicit syntax does not allow programmers to
our object being constructed. Each time we create a Line
declare let and do bindings.
object, the constructor will print to the console. We can
demonstrate this using fsi:
Even though you can use val elds in the implicit syntax, they must have the attribute [<Default> let line = new Line(1.0, 1.0, 4.0, 2.5);; val line : Line
Value>] and be mutable. It is more convenient to
Line constructor: {(1.000000, 1.000000), (4.000000,
use let bindings in this case. You can add public
2.500000)}, Line.Length: 3.354102
member accessors when they need to be public.

Example Using Two Constructors


Since the constructor is dened explicitly, we have the
option to provide more than one constructor.
open System open System.Net type Car = class val
used : bool val owner : string val mutable mileage : int
(* rst constructor *) new (owner) = { used = false;
owner = owner; mileage = 0 } (* another constructor *)
new (owner, mileage) = { used = true; owner = owner;
mileage = mileage } end let main() = let printCar (c :
Car) = printfn c.used: %b, c.owner: %s, c.mileage:
%i c.used c.owner c.mileage let stevesNewCar = new
Car(Steve) let bobsUsedCar = new Car(Bob, 83000)
let printCars() = [stevesNewCar; bobsUsedCar] |>
Seq.iter printCar printfn "\nCars created printCars()
printfn "\nSteve drives all over the state stevesNewCar.mileage <- stevesNewCar.mileage + 780 printCars()
printfn "\nBob commits odometer fraud bobsUsedCar.mileage <- 0 printCars() main()
This program has the following types:

In the implicit syntax, the primary constructor parameters are visible throughout the whole class
body. By using this feature, you do not need to write
code that copies constructor parameters to instance
members.
While both syntaxes support multiple constructors,
when you declare additional constructors with the
implicit syntax, they must call the primary constructor. In the explicit syntax all constructors are declared with new() and there is no primary constructor that needs to be referenced from others.
In general, its up to the programmer to use the implicit
or explicit syntax to dene classes. However, the implicit
syntax is used more often in practice as it tends to result
in shorter, more readable class denitions.

22.1.4 Class Inference


F#'s #light syntax allows programmers to omit the class
and end keywords in class denitions, a feature commonly referred to as class inference or type kind inference.

22.2. CLASS MEMBERS

65

For example, there is no dierence between the following type SomeClass(prop : int) = class member x.Prop =
class denitions:
prop static member SomeStaticMethod = This is a static
Both classes compile down to the same bytecode, but the method static member Copy (source : SomeClass) =
code using class inference allows us to omit a few unnec- new SomeClass(source.Prop) end
essary keywords.
Class inference and class explicit styles are considered acceptable. At the very least, when writing F# libraries,
don't dene half of your classes using class inference and
the other half using class explicit style -- pick one style
and use it consistently for all of your classes throughout
your project.

22.2 Class Members


22.2.1

We can experiment with this method in fsi:


> let instance = new SomeClass(10);; val instance :
SomeClass > let shallowCopy = instance;; (* copies
pointer to another symbol *) val shallowCopy :
SomeClass > let deepCopy = SomeClass.Copy instance;; (* copies values into a new object *) val
deepCopy : SomeClass > open System;; > Object.ReferenceEquals(instance, shallowCopy);; val it :
bool = true > Object.ReferenceEquals(instance, deepCopy);; val it : bool = false

Instance and Static Members

Object.ReferenceEquals is a static method on the SysThere are two types of members you can add to an object: tem.Object class which determines whether two objects
instances are the same object. As shown above, our Copy
method takes an instance of SomeClass and accesses its
Instance members, which can only be called from an Prop property.
object instance created using the new keyword.
When should you use static methods rather than in Static members, which are not associated with any stance methods?
object instance.
When the designers of the .NET framework were designing the System.String class, they had to decide where the
The following class has a static method and an instance Length method should go. They had the option of making
method:
the property an instance method (s.Length) or making it
type SomeClass(prop : int) = class member x.Prop = static (String.GetLength(s)). The .NET designers chose
prop static member SomeStaticMethod = This is a static to make Length an instance method because it is an intrinsic property of strings.
method end
On the other hand, the String class also has several static
We invoke a static method using the form class- methods, including String.Concat which takes a list of
Name.methodName. We invoke instance methods by string and concatenates them all together. Concatenatcreating an instance of our class and calling the methods ing strings is instance-agnostic, its does not depend on
using classInstance.methodName. Here is a demonstra- the instance members of any particular strings.
tion in fsi:
The following general principles apply to all OO lan> SomeClass.SomeStaticMethod;; (* invoking static guages:
method *) val it : string = This is a static method
> SomeClass.Prop;; (* doesn't make sense, we haven't
Instance members should be used to access propcreated an object instance yet *) SomeClass.Prop;; (*
erties intrinsic to an object, such as stringIndoesn't make sense, we haven't created an object instance
stance.Length.
yet *) ^^^^^^^^^^^^^^^ stdin(78,1): error FS0191:
property 'Prop' is not static. > let instance = new Some Instance methods should be used when they depend
Class(5);; val instance : SomeClass > instance.Prop;;
on state of a particular object instance, such as strin(* now we have an instance, we can call our instance
gInstance.Contains.
method *) val it : int = 5 > instance.SomeStaticMethod;;
(* can't invoke static method from instance *) in Instance methods should be used when its expected
stance.SomeStaticMethod;; (* can't invoke static
that programmers will want to override the method
method from instance *) ^^^^^^^^^^^^^^^^^^^^^^^^^^
in a derived class.
stdin(81,1): error FS0191: property 'SomeStaticMethod'
is static.
Static methods should not depend on a particular instance of an object, such as Int32.TryParse.
We can, of course, invoke instance methods from objects
passed into static methods, for example, lets say we add
a Copy method to our object dened above:

Static methods should return the same value as long


as the inputs remain the same.

66

CHAPTER 22. CLASSES

Constants, which are values that don't change for any - x.X2) + sqr(x.Y1 - x.Y2)) member x.ShiftH amount
class instance, should be declared as a static mem- = { x with X1 = x.X1 + amount; X2 = x.X2 + amount
bers, such as System.Boolean.TrueString.
} member x.ShiftV amount = { x with Y1 = x.Y1 +
amount; Y2 = x.Y2 + amount };; type Line = {X1: oat;
Y1: oat; X2: oat; Y2: oat;} with member ShiftH
22.2.2 Getters and Setters
: amount:float -> Line member ShiftV : amount:float
-> Line member Length : oat end > let line = { X1 =
Getters and setters are special functions which allow pro- 1.0; Y1 = 2.0; X2 = 5.0; Y2 = 4.5 };; val line : Line >
grammers to read and write to members using a conve- line.Length;; val it : oat = 4.716990566 > line.ShiftH
nient syntax. Getters and setters are written using this 10.0;; val it : Line = {X1 = 11.0; Y1 = 2.0; X2 = 15.0;
format:
Y2 = 4.5;} > line.ShiftV 5.0;; val it : Line = {X1 =
member alias.PropertyName with get() = some-value 1.0; Y1 = 3.0; X2 = 5.0; Y2 = 0.5;}
and set(value) = some-assignment
Union example
Heres a simple example using fsi:
> type shape = | Circle of oat | Rectangle of oat * oat
> type IntWrapper() = class let mutable num = 0 member | Triangle of oat * oat with member x.Area = match
x.Num with get() = num and set(value) = num <- value x with | Circle(r) -> Math.PI * r * r | Rectangle(b, h)
end;; type IntWrapper = class new : unit -> IntWrapper -> b * h | Triangle(b, h) -> b * h / 2.0 member x.Scale
member Num : int member Num : int with set end > let value = match x with | Circle(r) -> Circle(r + value)
wrapper = new IntWrapper();; val wrapper : IntWrapper | Rectangle(b, h) -> Rectangle(b + value, h + value) |
> wrapper.Num;; val it : int = 0 > wrapper.Num <- 20;; Triangle(b, h) -> Triangle(b + value, h + value);; type
shape = | Circle of oat | Rectangle of oat * oat |
val it : unit = () > wrapper.Num;; val it : int = 20
Triangle of oat * oat with member Scale : value:float
-> shape member Area : oat end > let mycircle =
Getters and setters are used to expose private members Circle(5.0);; val mycircle : shape > mycircle.Area;; val
to outside world. For example, our Num property allows it : oat = 78.53981634 > mycircle.Scale(7.0);; val it :
users to read/write to the internal num variable. Since shape = Circle 12.0
getters and setters are gloried functions, we can use them
to sanitize input before writing the values to our internal
variables. For example, we can modify our IntWrapper
class to constrain our to values between 0 and 10 by modifying our class as follows:
22.3 Generic classes
type IntWrapper() = class let mutable num = 0 member
x.Num with get() = num and set(value) = if value > 10 We can also create classes which take generic types:
|| value < 0 then raise (new Exception(Values must be type 'a GenericWrapper(initialVal : 'a) = class let
between 0 and 10)) else num <- value end
mutable internalVal = initialVal member x.Value with
get() = internalVal and set(value) = internalVal <- value
end
We can use this class in fsi:
> let wrapper = new IntWrapper();; val wrapper :
IntWrapper > wrapper.Num <- 5;; val it : unit = ()
> wrapper.Num;; val it : int = 5 > wrapper.Num <20;; System.Exception: Values must be between 0 and
10 at FSI_0072.IntWrapper.set_Num(Int32 value) at
<StartupCode$FSI_0076>.$FSI_0076._main() stopped
due to error

We can use this class in FSI as follows:

> let intWrapper = new GenericWrapper<_>(5);; val


intWrapper : int GenericWrapper > intWrapper.Value;;
val it : int = 5 > intWrapper.Value <- 20;; val it : unit = ()
> intWrapper.Value;; val it : int = 20 > intWrapper.Value
<- 2.0;; (* throws an exception *) intWrapper.Value <2.0;; (* throws an exception *) --------------------^^^^
stdin(156,21): error FS0001: This expression has type
22.2.3 Adding Members to Records and oat but is here used with type int. > let boolWrapper =
new GenericWrapper<_>(true);; val boolWrapper : bool
Unions
GenericWrapper > boolWrapper.Value;; val it : bool =
true
Its just as easy to add members to records and union types
as well.
Generic classes help programmers generalize classes to
Record example:
operate on multiple dierent types. They are used in fun> type Line = { X1 : oat; Y1 : oat; X2 : oat; Y2 : oat damentally the same way as all other generic types already
} with member x.Length = let sqr x = x * x sqrt(sqr(x.X1 seen in F#, such as Lists, Sets, Maps, and union types.

22.4. PATTERN MATCHING OBJECTS

22.4 Pattern Matching Objects


While its not possible to match objects based on their
structure in quite the same way that we do for lists and
union types, F# allows programmers to match on types
using the syntax:
match arg with | :? type1 -> expr | :? type2 -> expr
Heres an example which uses type testing:
type Cat() = class member x.Meow() = printfn Meow
end type Person(name : string) = class member
x.Name = name member x.SayHello() = printfn Hi,
I'm %s x.Name end type Monkey() = class member
x.SwingFromTrees() = printfn swinging from trees end
let handlesAnything (o : obj) = match o with | null ->
printfn "<null>" | :? Cat as cat -> cat.Meow() | :? Person
as person -> person.SayHello() | _ -> printfn I don't
recognize type '%s" (o.GetType().Name) let main() =
let cat = new Cat() let bob = new Person(Bob) let bill
= new Person(Bill) let phrase = Hello world!" let
monkey = new Monkey() handlesAnything cat handlesAnything bob handlesAnything bill handlesAnything
phrase handlesAnything monkey handlesAnything null
main()
This program outputs:
Meow Hi, I'm Bob Hi, I'm Bill I don't recognize type
'String' I don't recognize type 'Monkey' <null>

67

Chapter 23

Inheritance
Many object-oriented languages use inheritance exten- other words, its not possible to create a class which desively in the .NET BCL to construct class hierarchies.
rives from Student and Employee simultaneously.

23.1 Subclasses
A subclass is, in the simplest terms, a class derived from
a class which has already been dened. A subclass inher- 23.1.1 Overriding Methods
its its members from a base class in addition to adding
its own members. A subclass is dened using the inherit
keyword as shown below:
Occasionally, you may want a derived class to change the
type Person(name) = member x.Name = name member default behavior of methods inherited from the base class.
x.Greet() = printfn Hi, I'm %s x.Name type Stu- For example, the output of the .ToString() method above
dent(name, studentID : int) = inherit Person(name) let isn't very useful. We can override that behavior with a
mutable _GPA = 0.0 member x.StudentID = studentID dierent implementation using the override:
member x.GPA with get() = _GPA and set value =
_GPA <- value type Worker(name, employer : string) =
inherit Person(name) let mutable _salary = 0.0 member
x.Salary with get() = _salary and set value = _salary <value member x.Employer = employer

type Person(name) = member x.Name = name member


x.Greet() = printfn Hi, I'm %s x.Name override
x.ToString() = x.Name (* The ToString() method is
inherited from System.Object *)

We've overridden the default implementation of the


ToString() method, causing it to print out a persons
System.Object (* All classes descend from *) - Person - name.
Student - Worker
Methods in F# are not overridable by default. If you
The Student and Worker subclasses both inherit the Name expect users will want to override methods in a derived
and Greet methods from the Person base class. This can class, you have to declare your method as overridable using the abstract and default keywords as follows:
be demonstrated in fsi:
Our simple class hierarchy looks like this:

> let somePerson, someStudent, someWorker = new


Person(Juliet), new Student(Monique, 123456),
new Worker(Carla, Awesome Hair Salon);; val
someWorker : Worker val someStudent : Student val
somePerson : Person > somePerson.Name, someStudent.Name, someWorker.Name;; val it : string *
string * string = (Juliet, Monique, Carla) >
someStudent.StudentID;; val it : int = 123456 >
someWorker.Employer;; val it : string = Awesome
Hair Salon > someWorker.ToString();; (* ToString
method inherited from System.Object *) val it : string =
FSI_0002+Worker

type Person(name) = member x.Name = name abstract


Greet : unit -> unit default x.Greet() = printfn Hi,
I'm %s x.Name type Quebecois(name) = inherit
Person(name) override x.Greet() = printfn Bonjour, je
m'appelle %s, eh. x.Name
Our class Person provides a Greet method which may be
overridden in derived classes. Heres an example of these
two classes in fsi:

> let terrance, phillip = new Person(Terrance), new


Quebecois(Phillip);; val terrance : Person val phillip
: Quebecois > terrance.Greet();; Hi, I'm Terrance val
it : unit = () > phillip.Greet();; Bonjour, je m'appelle
.NETs object model supports single-class inheritance, Phillip, eh.
meaning that a subclass is limited to one base class. In
68

23.2. WORKING WITH SUBCLASSES

23.1.2

69

Abstract Classes

An abstract class is one which provides an incomplete implementation of an object, and requires a programmer to
create subclasses of the abstract class to ll in the rest of
the implementation. For example, consider the following:
[<AbstractClass>] type Shape(position : Point) = member x.Position = position override x.ToString() = sprintf
position = {%i, %i}, area = %f position.X position.Y
(x.Area()) abstract member Draw : unit -> unit abstract
member Area : unit -> oat
The rst thing you'll notice is the AbstractClass attribute,
which tells the compiler that our class has some abstract
members. Additionally, you notice two abstract members, Draw and Area don't have an implementation, only
a type signature.
We can't create an instance of Shape because the class
hasn't been fully implemented. Instead, we have to derive from Shape and override the Draw and Area methods
with a concrete implementation:
type Circle(position : Point, radius : oat) = inherit
Shape(position) member x.Radius = radius override x.Draw() = printfn "(Circle)" override x.Area() =
Math.PI * radius * radius type Rectangle(position : Point,
width : oat, height : oat) = inherit Shape(position)
member x.Width = width member x.Height = height
override x.Draw() = printfn "(Rectangle)" override
x.Area() = width * height type Square(position : Point,
width : oat) = inherit Shape(position) member x.Width
= width member x.ToRectangle() = new Rectangle(position, width, width) override x.Draw() = printfn
"(Square)" override x.Area() = width * width type Triangle(position : Point, sideA : oat, sideB : oat, sideC :
oat) = inherit Shape(position) member x.SideA = sideA
member x.SideB = sideB member x.SideC = sideC override x.Draw() = printfn "(Triangle)" override x.Area() =
(* Herons formula *) let a, b, c = sideA, sideB, sideC let
s = (a + b + c) / 2.0 Math.Sqrt(s * (s - a) * (s - b) * (s - c) )

23.2 Working With Subclasses


23.2.1 Up-casting and Down-casting
A type cast is an operation which changes the type of an
object from one type to another. This is not the same as
a map function, because a type cast does not return an
instance of a new object, it returns the same instance of
an object with a dierent type.
For example, lets say B is a subclass of A. If we have
an instance of B, we are able to cast as an instance of A.
Since A is upward in the class hiearchy, we call this an
up-cast. We use the :> operator to perform upcasts:
> let regularString = Hello world";; val regularString
: string > let upcastString = Hello world :> obj;; val
upcastString : obj > regularString.ToString();; val it :
string = Hello world > regularString.Length;; val it
: int = 11 > upcastString.ToString();; (* type obj has
a .ToString method *) val it : string = Hello world
> upcastString.Length;; (* however, obj does not have
Length method *) upcastString.Length;; (* however, obj
does not have Length method *) -------------^^^^^^^
stdin(24,14): error FS0039: The eld, constructor or
member 'Length' is not dened.
Up-casting is considered safe, because a derived class is
guaranteed to have all of the same members as an ancestor class. We can, if necessary, go in the opposite direction: we can down-cast from an ancestor class to a derived
class using the :?> operator:

> let intAsObj = 20 :> obj;; val intAsObj : obj > intAsObj,
intAsObj.ToString();; val it : obj * string = (20, 20) >
let intDownCast = intAsObj :?> int;; val intDownCast
: int > intDownCast, intDownCast.ToString();; val
it : int * string = (20, 20) > let stringDownCast =
intAsObj :?> string;; (* boom! *) val stringDownCast
: string System.InvalidCastException: Unable to cast
Now we have several dierent implementations of the object of type 'System.Int32' to type 'System.String'. at
Shape class. We can experiment with these in fsi:
<StartupCode$FSI_0067>.$FSI_0067._main() stopped
> let position = { X = 0; Y = 0 };; val position : due to error
Point > let circle, rectangle, square, triangle = new
Circle(position, 5.0), new Rectangle(position, 2.0, 7.0), Since intAsObj holds an int boxed as an obj, we can
new Square(position, 10.0), new Triangle(position, downcast to an int as needed. However, we cannot down3.0, 4.0, 5.0);; val triangle : Triangle val square : cast to a string because its an incompatible type. DownSquare val rectangle : Rectangle val circle : Circle casting is considered unsafe because the error isn't de> circle.ToString();; val it : string = Circle, position tectable by the type-checker, so an error with a down-cast
= {0, 0}, area = 78.539816 > triangle.ToString();; always results in a runtime exception.
val it : string = Triangle, position = {0, 0}, area
= 6.000000 > square.Width;; val it : oat = 10.0
> square.ToRectangle().ToString();; val it : string = Up-casting example
Rectangle, position = {0, 0}, area = 100.000000 >
rectangle.Height, rectangle.Width;; val it : oat * oat = open System type Point = { X : int; Y : int } [<AbstractClass>] type Shape() = override x.ToString() = sprintf
(7.0, 2.0)

70

CHAPTER 23. INHERITANCE

"%s, area = %f (x.GetType().Name) (x.Area()) abstract


member Draw : unit -> unit abstract member Area :
unit -> oat type Circle(radius : oat) = inherit Shape()
member x.Radius = radius override x.Draw() = printfn
"(Circle)" override x.Area() = Math.PI * radius * radius
type Rectangle(width : oat, height : oat) = inherit
Shape() member x.Width = width member x.Height
= height override x.Draw() = printfn "(Rectangle)"
override x.Area() = width * height type Square(width
: oat) = inherit Shape() member x.Width = width
member x.ToRectangle() = new Rectangle(width, width)
override x.Draw() = printfn "(Square)" override x.Area()
= width * width type Triangle(sideA : oat, sideB :
oat, sideC : oat) = inherit Shape() member x.SideA
= sideA member x.SideB = sideB member x.SideC =
sideC override x.Draw() = printfn "(Triangle)" override
x.Area() = (* Herons formula *) let a, b, c = sideA,
sideB, sideC let s = (a + b + c) / 2.0 Math.Sqrt(s * (s a) * (s - b) * (s - c) ) let shapes = [(new Circle(5.0) :>
Shape); (new Circle(12.0) :> Shape); (new Square(10.5)
:> Shape); (new Triangle(3.0, 4.0, 5.0) :> Shape); (new
Rectangle(5.0, 2.0) :> Shape)] (* Notice we have to cast
each object as a Shape *) let main() = shapes |> Seq.iter
(fun x -> printfn x.ToString: %s (x.ToString()) ) main()
This program has the following types:
type Point = {X: int; Y: int;} type Shape = class abstract
member Area : unit -> oat abstract member Draw
: unit -> unit new : unit -> Shape override ToString
: unit -> string end type Circle = class inherit Shape
new : radius:float -> Circle override Area : unit ->
oat override Draw : unit -> unit member Radius :
oat end type Rectangle = class inherit Shape new :
width:float * height:float -> Rectangle override Area
: unit -> oat override Draw : unit -> unit member
Height : oat member Width : oat end type Square =
class inherit Shape new : width:float -> Square override
Area : unit -> oat override Draw : unit -> unit member
ToRectangle : unit -> Rectangle member Width : oat
end type Triangle = class inherit Shape new : sideA:float
* sideB:float * sideC:float -> Triangle override Area :
unit -> oat override Draw : unit -> unit member SideA
: oat member SideB : oat member SideC : oat end val
shapes : Shape list
This program outputs:
x.ToString: Circle, area = 78.539816 x.ToString: Circle, area = 452.389342 x.ToString: Square, area =
110.250000 x.ToString: Triangle, area = 6.000000
x.ToString: Rectangle, area = 10.000000

23.2.2

Public, Private,
Members

and Protected

Chapter 24

Interfaces
An objects interface refers to all of the public members 24.1 Dening Interfaces
and functions that a function exposes to consumers of the
object. For example, take the following:
According to the F# specication, interfaces are dened
type Monkey(name : string, birthday : DateTime) with the following syntax:
= let mutable _birthday = birthday let mutable type type-name = interface inherits-decl member-defns
_lastEaten = DateTime.Now let mutable _food- end
sEaten = [] : string list member this.Speak() =
printfn Ook ook!" member this.Name = name
member this.Birthday with get() = _birthday and
Note: The interface/end tokens can be omitted
set(value) = _birthday <- value member internal
when using the #light syntax option, in which
this.UpdateFoodsEaten(food) = _foodsEaten <- food
case Type Kind Inference (10.1) is used to
:: _foodsEaten member internal this.ResetLastEaten()
determine the kind of the type. The presence
= _lastEaten <- DateTime.Now member this.IsHungry
of any non-abstract members or constructors
= (DateTime.Now - _lastEaten).TotalSeconds >= 5.0
means a type is not an interface type.
member this.GetFoodsEaten() = _lastEaten member this.Feed(food) = this.UpdateFoodsEaten(food)
For example:
this.ResetLastEaten() this.Speak()
type ILifeForm = (* .NET convention recommends
This class contains several public, private, and internal the prex 'I' on all interfaces *) abstract Name : string
members. However, consumers of this class can only ac- abstract Speak : unit -> unit abstract Eat : unit -> unit
cess the public members; when a consumer uses this class,
they see the following interface:
type Monkey = class new :
name:string *
birthday:DateTime -> Monkey member Feed : 24.2 Using Interfaces
food:string -> unit member GetFoodsEaten : unit
-> DateTime member Speak : unit -> unit member Since they only dene a set of public method signatures,
Birthday : DateTime member IsHungry : bool member users need to create an object to implement the interface.
Name : string member Birthday : DateTime with set end Here are three classes which implement the ILifeForm
interface in fsi:
Notice the _birthday, _lastEaten, _foodsEaten, UpdateFoodsEaten, and ResetLastEaten members are inaccessible to the outside world, so they are not part of this objects public interface.
All interfaces you've seen so far have been intrinsically
tied to a specic object. However, F# and many other
OO languages allow users to dene interfaces as standalone types, allowing us to eectively separate an objects
interface from its implementation.

> type ILifeForm = abstract Name : string abstract


Speak : unit -> unit abstract Eat : unit -> unit type
Dog(name : string, age : int) = member this.Age =
age interface ILifeForm with member this.Name =
name member this.Speak() = printfn Woof!" member this.Eat() = printfn Yum, doggy biscuits!" type
Monkey(weight : oat) = let mutable _weight = weight
member this.Weight with get() = _weight and set(value)
= _weight <- value interface ILifeForm with member
this.Name = Monkey!!!" member this.Speak() = printfn
Ook ook member this.Eat() = printfn Bananas!" type
Ninja() = interface ILifeForm with member this.Name
= Ninjas have no name member this.Speak() = printfn
Ninjas are silent, deadly killers member this.Eat() =
printfn Ninjas don't eat, they wail on guitars because

71

72

CHAPTER 24. INTERFACES

they're totally sweet";; type ILifeForm = interface abstract member Eat : unit -> unit abstract member Speak :
unit -> unit abstract member Name : string end type Dog
= class interface ILifeForm new : name:string * age:int
-> Dog member Age : int end type Monkey = class interface ILifeForm new : weight:float -> Monkey member
Weight : oat member Weight : oat with set end type
Ninja = class interface ILifeForm new : unit -> Ninja end
Typically, we call an interface an abstraction, and any
class which implements the interface as a concrete implementation. In the example above, ILifeForm is an abstraction, whereas Dog, Monkey, and Ninja are concrete
implementations.
Its worth noting that interfaces only dene instance members signatures on objects. In other words, they cannot
dene static member signatures or constructor signatures.

24.2.1

What are interfaces used for?

Interfaces are a mystery to newbie programmers (after all,


whats the point of creating a type with no implementation?), however they are essential to many object-oriented
programming techniques. Interfaces allow programmers
to generalize functions to all classes which implement a
particular interface, even if those classes don't necessarily
descend from one another. For example, using the Dog,
Monkey, and Ninja classes dened above, we can write
a method to operate on all of them, as well as any other
classes which implement the ILifeForm interface.

24.2.2

Implementing Interfaces with Object Expressions

ple = [| { name = Larry"; age = 20 }; { name =


Moe"; age = 30 }; { name = Curly"; age = 25
} |] let sortAndPrint msg items (comparer : System.Collections.Generic.IComparer<person>) = Array.Sort(items, comparer) printf "%s: " msg Seq.iter
(fun x -> printf "(%s, %i) " x.name x.age) items
printfn "" (* sorting by age *) sortAndPrint age
people { new IComparer<person> with member
this.Compare(x, y) = x.age.CompareTo(y.age) } (*
sorting by name *) sortAndPrint name people { new
IComparer<person> with member this.Compare(x, y)
= x.name.CompareTo(y.name) } (* sorting by name
descending *) sortAndPrint name desc people { new
IComparer<person> with member this.Compare(x, y) =
y.name.CompareTo(x.name) };; type person = {name:
string; age: int;} val people : person array val sortAndPrint : string -> person array -> IComparer<person>
-> unit age: (Larry, 20) (Curly, 25) (Moe, 30) name:
(Curly, 25) (Larry, 20) (Moe, 30) name desc: (Moe, 30)
(Larry, 20) (Curly, 25)

24.2.3 Implementing Multiple Interfaces


Unlike inheritance, its possible to implement multiple interfaces:
open System type Person(name : string, age : int)
= member this.Name = name member this.Age =
age (* IComparable is used for ordering instances
*) interface IComparable<Person> with member
this.CompareTo(other) = (* sorts by name, then
age *) match this.Name.CompareTo(other.Name)
with | 0 -> this.Age.CompareTo(other.Age) | n ->
n (* Used for comparing this type against other
types *) interface IEquatable<string> with member
this.Equals(othername) = this.Name.Equals(othername)

Interfaces are extremely useful for sharing snippets of


implementation logic between other classes, however it
Its just as easy to implement multiple interfaces in object
can be very cumbersome to dene and implement a new
expressions as well.
class for ad hoc interfaces. Object expressions allow users
to implement interfaces on anonymous classes using the
following syntax:
24.2.4 Interface Hierarchies
{ new ty0 [ args-expr ] [ as base-ident ] [ with
val-or-member-defns end ] interface ty1 with [ val- Interfaces can extend other interfaces in a kind of interor-member-defns1 end ] interface tyn with [ face hierarchy. For example:
val-or-member-defnsn end ] }
type ILifeForm = abstract member location : System.Drawing.Point type 'a IAnimal = (* interface with
Using a concrete example, the .NET BCL has a method generic type parameter *) inherit ILifeForm inherit
called System.Array.Sort<T>(T array, IComparer<T>), System.IComparable<'a> abstract member speak : unit
where IComparer<T> exposes a single method called -> unit type IFeline = inherit IAnimal<IFeline> abstract
Compare. Lets say we wanted to sort an array on an ad member purr : unit -> unit
hoc basis using this method; rather than litter our code
with one-time use classes, we can use object expressions When users create a concrete implementation of IFeline,
to dene anonymous classes on the y:
they are required to provide implementations for all of
> open System open System.Collections.Generic the methods dened in the IAnimal, IComparable, and
type person = { name : string; age : int } let peo- ILifeForm interfaces.

24.3. EXAMPLES
Note: Interface hierarchies are occasionally
useful, however deep, complicated hierarchies
can be cumbersome to work with.

24.3 Examples
24.3.1

73

24.3.2 Using interfaces in generic type definitions


We can constrain generic types in class and function denitions to particular interfaces. For example, lets say that
we wanted to create a binary tree which satises the following property: each node in a binary tree has two children, left and right, where all of the child nodes in left are
less than all of its parent nodes, and all of the child nodes
in right are greater than all of its parent nodes.

We can implement a binary tree with these properties


Generalizing a function to many dening a binary tree which constrains our tree to the
IComparable<T> interface.

classes

open System type ILifeForm = abstract Name : string


abstract Speak : unit -> unit abstract Eat : unit -> unit
type Dog(name : string, age : int) = member this.Age
= age interface ILifeForm with member this.Name =
name member this.Speak() = printfn Woof!" member this.Eat() = printfn Yum, doggy biscuits!" type
Monkey(weight : oat) = let mutable _weight = weight
member this.Weight with get() = _weight and set(value)
= _weight <- value interface ILifeForm with member
this.Name = Monkey!!!" member this.Speak() = printfn
Ook ook member this.Eat() = printfn Bananas!" type
Ninja() = interface ILifeForm with member this.Name
= Ninjas have no name member this.Speak() = printfn
Ninjas are silent, deadly killers member this.Eat() =
printfn Ninjas don't eat, they wail on guitars because
they're totally sweet let lifeforms = [(new Dog(Fido,
7) :> ILifeForm); (new Monkey(500.0) :> ILifeForm);
(new Ninja() :> ILifeForm)] let handleLifeForm (x :
ILifeForm) = printfn Handling lifeform '%s" x.Name
x.Speak() x.Eat() printfn "" let main() = printfn Processing...\n lifeforms |> Seq.iter handleLifeForm printfn
Done. main()
This program has the following types:
type ILifeForm = interface abstract member Eat : unit
-> unit abstract member Speak : unit -> unit abstract
member Name : string end type Dog = class interface
ILifeForm new : name:string * age:int -> Dog member
Age : int end type Monkey = class interface ILifeForm
new : weight:float -> Monkey member Weight : oat
member Weight : oat with set end type Ninja = class
interface ILifeForm new : unit -> Ninja end val lifeforms
: ILifeForm list val handleLifeForm : ILifeForm -> unit
val main : unit -> unit
This program outputs the following:
Processing... Handling lifeform 'Fido' Woof! Yum,
doggy biscuits! Handling lifeform 'Monkey!!!' Ook ook
Bananas! Handling lifeform 'Ninjas have no name' Ninjas are silent, deadly killers Ninjas don't eat, they wail on
guitars because they're totally sweet Done.

Note: .NET has a number of interfaces


dened in the BCL, including the very
important
IComparable<T>
interface.
IComparable exposes a single method,
objectInstance.CompareTo(otherInstance),
which should return 1, 1, or 0 when the objectInstance is greater than, less than, or equal
to otherInstance respectively. Many classes
in the .NET framework already implement
IComparable, including all of the numeric
data types, strings, and datetimes.
For example, using fsi:
> open System type tree<'a> when 'a :> IComparable<'a> = | Nil | Node of 'a * 'a tree * 'a tree let rec insert
(x : #IComparable<'a>) = function | Nil -> Node(x,
Nil, Nil) | Node(y, l, r) as node -> if x.CompareTo(y)
= 0 then node elif x.CompareTo(y) = 1 then Node(y,
insert x l, r) else Node(y, l, insert x r) let rec contains (x
: #IComparable<'a>) = function | Nil -> false | Node(y,
l, r) as node -> if x.CompareTo(y) = 0 then true elif
x.CompareTo(y) = 1 then contains x l else contains
x r;; type tree<'a> when 'a :> IComparable<'a>> = |
Nil | Node of 'a * tree<'a> * tree<'a> val insert : 'a ->
tree<'a> -> tree<'a> when 'a :> IComparable<'a> val
contains : #IComparable<'a> -> tree<'a> -> bool when
'a :> IComparable<'a> > let x = let rnd = new Random()
[ for a in 1 .. 10 -> rnd.Next(1, 100) ] |> Seq.fold (fun
acc x -> insert x acc) Nil;; val x : tree<int> > x;; val it :
tree<int> = Node (25,Node (20,Node (6,Nil,Nil),Nil),
Node (90, Node (86,Node (65,Node (50,Node (39,Node
(32,Nil,Nil),Nil),Nil),Nil),Nil), Nil)) > contains 39 x;;
val it : bool = true > contains 55 x;; val it : bool = false

24.3.3 Simple dependency injection


Dependency injection refers to the process of supplying
an external dependency to a software component. For
example, lets say we had a class which, in the event of
an error, sends an email to the network administrator, we
might write some code like this:

74
type Processor() = (* ... *) member this.Process items
= try (* do stu with items *) with | err -> (new
Emailer()).SendMsg(admin@company.com, Error! "
+ err.Message)
The Process method creates an instance of Emailer, so we
can say that the Processor class depends on the Emailer
class.
Lets say we're testing our Processor class, and we don't
want to be sending emails to the network admin all the
time. Rather than comment out the lines of code we don't
want to run while we test, its much easier to substitute
the Emailer dependency with a dummy class instead. We
can achieve that by passing in our dependency through the
constructor:
type IFailureNotier = abstract Notify : string -> unit
type Processor(notier : IFailureNotier) = (* ... *)
member this.Process items = try // do stu with items
with | err -> notier.Notify(err.Message) (* concrete implementations of IFailureNotier *) type EmailNotier()
= interface IFailureNotier with member Notify(msg)
= (new Emailer()).SendMsg(admin@company.com,
Error! " + msg) type DummyNotier() = interface
IFailureNotier with member Notify(msg) = () // swallow message type LogleNotier(lename : string) =
interface IFailureNotifer with member Notify(msg) =
System.IO.File.AppendAllText(lename, msg)
Now, we create a processor and pass in the kind of FailureNotier we're interested in. In test environments,
we'd use new Processor(new DummyNotier()); in production, we'd use new Processor(new EmailNotier()) or
new Processor(new LogleNotier(@"C:\log.txt)).
To demonstrate dependency injection using a somewhat
contrived example, the following code in fsi shows how
to hot swap one interface implementation with another:
> #time;; --> Timing now on > type IAddStrategy =
abstract add : int -> int -> int type DefaultAdder() =
interface IAddStrategy with member this.add x y = x
+ y type SlowAdder() = interface IAddStrategy with
member this.add x y = let rec loop acc = function | 0
-> acc | n -> loop (acc + 1) (n - 1) loop x y type OByOneAdder() = interface IAddStrategy with member
this.add x y = x + y - 1 type SwappableAdder(adder :
IAddStrategy) = let mutable _adder = adder member
this.Adder with get() = _adder and set(value) = _adder
<- value member this.Add x y = this.Adder.add x y;;
type IAddStrategy = interface abstract member add : int
-> (int -> int) end type DefaultAdder = class interface
IAddStrategy new : unit -> DefaultAdder end type
SlowAdder = class interface IAddStrategy new : unit ->
SlowAdder end type OByOneAdder = class interface
IAddStrategy new : unit -> OByOneAdder end type
SwappableAdder = class new : adder:IAddStrategy
-> SwappableAdder member Add : x:int -> (int ->
int) member Adder : IAddStrategy member Adder :

CHAPTER 24. INTERFACES


IAddStrategy with set end Real: 00:00:00.000, CPU:
00:00:00.000, GC gen0: 0, gen1: 0, gen2: 0 > let
myAdder = new SwappableAdder(new DefaultAdder());;
val myAdder : SwappableAdder Real: 00:00:00.000,
CPU: 00:00:00.000, GC gen0: 0, gen1: 0, gen2: 0 >
myAdder.Add 10 1000000000;; Real: 00:00:00.001,
CPU: 00:00:00.015, GC gen0: 0, gen1: 0, gen2: 0
val it : int = 1000000010 > myAdder.Adder <- new
SlowAdder();; Real: 00:00:00.000, CPU: 00:00:00.000,
GC gen0: 0, gen1: 0, gen2: 0 val it : unit = () >
myAdder.Add 10 1000000000;; Real: 00:00:01.085,
CPU: 00:00:01.078, GC gen0: 0, gen1: 0, gen2: 0 val it
: int = 1000000010 > myAdder.Adder <- new OByOneAdder();; Real: 00:00:00.000, CPU: 00:00:00.000,
GC gen0: 0, gen1: 0, gen2: 0 val it : unit = () >
myAdder.Add 10 1000000000;; Real: 00:00:00.000,
CPU: 00:00:00.000, GC gen0: 0, gen1: 0, gen2: 0 val it
: int = 1000000009

Chapter 25

Events
Events allow objects to communicate with one another
through a kind of synchronous message passing. Events
are simply hooks to other functions: objects register callback functions to an event, and these callbacks will be
executed when (and if) the event is triggered by some object.
For example, lets say we have a clickable button which
exposed an event called Click. We can register a block of
code, something like fun () -> printfn I've been clicked!",
to the buttons click event. When the click event is triggered, it will execute the block of code we've registered.
If we wanted to, we could register an indenite number of callback functions to the click event -- the button
doesn't care what code is trigged by the callbacks or how
many callback functions are registered to its click event,
it blindly executes whatever functions are hooked to its
click event.

Now, any object can listen to the changes on the person


method. By convention and Microsofts recommendation, events are usually named Verb or VerbPhrase, as well
as adding tenses like Verbed and Verbing to indicate postand pre-events.

25.2 Adding Callbacks to Event


Handlers
Its very easy to add callbacks to event handlers. Each
event handler has the type IEvent<'T> which exposes several methods:
val Add : event:('T -> unit) -> unit

Event-driven programming is natural in GUI code, as


Connect a listener function to the event. The
GUIs tend to consist of controls which react and respond
listener will be invoked when the event is red.
to user input. Events are, of course, useful in non-GUI
applications as well. For example, if we have an object
val AddHandler : 'del -> unit
with mutable properties, we may want to notify another
object when those properties change.
Connect a handler delegate object to the event.
A handler can be later removed using RemoveHandler. The listener will be invoked when the
25.1 Dening Events
event is red.
Events are created and used though F#'s Event class. To val RemoveHandler : 'del -> unit
create an event, use the Event constructor as follows:
type Person(name : string) = let mutable _name =
Remove a listener delegate from an event lisname; let nameChanged = new Event<string>() member
tener store.
this.Name with get() = _name and set(value) = _name
<- value
Heres an example program:
type Person(name : string) = let mutable _name = name;
To allow listeners to hook onto our event, we need to ex- let nameChanged = new Event<unit>() (* creates event
pose the nameChanged eld as a public member using *) member this.NameChanged = nameChanged.Publish
the events Publish property:
(* exposed event handler *) member this.Name
type Person(name : string) = let mutable _name = name; with get() = _name and set(value) = _name <- value
let nameChanged = new Event<unit>() (* creates event nameChanged.Trigger() (* invokes event handler *) let
*) member this.NameChanged = nameChanged.Publish p = new Person(Bob) p.NameChanged.Add(fun () ->
(* exposed event handler *) member this.Name printfn "-- Name changed! New name: %s p.Name)
with get() = _name and set(value) = _name <- value printfn Event handling is easy p.Name <- Joe printfn
nameChanged.Trigger() (* invokes event handler *)
It handily decouples objects from one another p.Name
75

76

CHAPTER 25. EVENTS

<- Moe p.NameChanged.Add(fun () -> printfn "-Another handler attached to NameChanged!") printfn
Its also causes programs behave non-deterministically.
p.Name <- Bo printfn The function NameChanged is
invoked eortlessly.

printfn
Event
handling
is
easy
p.Name
<- Joe printfn It handily decouples objects from one another p.Name <- Moe
p.NameChanged.RemoveHandler(person_NameChanged)
p.NameChanged.Add(fun () -> printfn "-- Another handler attached to NameChanged!") printfn Its also
causes programs behave non-deterministically. p.Name
This program outputs the following:
<- Bo printfn The function NameChanged is invoked
Event handling is easy -- Name changed! New name: Joe eortlessly.
It handily decouples objects from one another -- Name
changed! New name: Moe Its also causes programs
behave non-deterministically. -- Name changed! New This program outputs the following:
name: Bo -- Another handler attached to NameChanged! Event handling is easy -- Name changed! New name: Joe
The function NameChanged is invoked eortlessly.
It handily decouples objects from one another -- Name
changed! New name: Moe Its also causes programs behave non-deterministically. -- Another handler attached
Note: When multiple callbacks are connected
to NameChanged! The function NameChanged is into a single event, they are executed in the orvoked eortlessly.
der they are added. However, in practice, you
should not write code with the expectation that
events will trigger in a particular order, as do25.3.2 Dening New Delegate Types
ing so can introduce complex dependencies between functions. Event-driven programming
F#'s event handling model is a little dierent from the
is often non-deterministic and fundamentally
rest of .NET. If we want to expose F# events to dierent
stateful, which can occasionally be at odds with
languages like C# or VB.NET, we can dene a custom
the spirit of functional programming. Its best
delegate type which compiles to a .NET delegate using
to write callback functions which do not modthe delegate keyword, for example:
ify state, and do not depend on the invocation
of any prior events.
type NameChangingEventArgs(oldName : string, new-

25.3 Working with EventHandlers


Explicitly
25.3.1

Name : string) = inherit System.EventArgs() member


this.OldName = oldName member this.NewName =
newName type NameChangingDelegate = delegate of
obj * NameChangingEventArgs -> unit
The convention obj * NameChangingEventArgs corre-

Adding and Removing Event Han- sponds to the .NET naming guidelines which recommend
dlers
that all events have the type val eventName : (sender : obj

* e : #EventArgs) -> unit.


The code above demonstrates how to use the
IEvent<'T>.add method.
However, occasionally we need to remove callbacks. To do so, we 25.3.3 Use existing .NET WPF Event and
need to work with the IEvent<'T>.AddHandler and
Delegate Types
IEvent<'T>.RemoveHandler methods, as well as .NETs
built-in System.Delegate type.
Try using existing .NET WPF Event and Delegate, exThe function person.NameChanged.AddHandler has the ample, ClickEvent and RoutedEventHandler. Create F#
type val AddHandler : Handler<'T> -> unit, where Han- Windows Application .NET project with referring these
dler<'T> inherits from System.Delegate. We can create libraries (PresentationCore PresentationFramework Sysan instance of Handler as follows:
tem.Xaml WindowsBase). The program will display a
type Person(name : string) = let mutable _name = name; button in a window. Clicking the button will display the
let nameChanged = new Event<unit>() (* creates event buttons content as string.
*) member this.NameChanged = nameChanged.Publish
(* exposed event handler *) member this.Name
with get() = _name and set(value) = _name <- value
nameChanged.Trigger() (* invokes event handler *) let
p = new Person(Bob) let person_NameChanged =
new Handler<unit>(fun sender eventargs -> printfn
"-- Name changed!
New name: %s p.Name)
p.NameChanged.AddHandler(person_NameChanged)

open System.Windows open System.Windows.Controls


open System.Windows.Input open System [<EntryPoint>] [<STAThread>] // STAThread is SingleThreading-Apartment which is required by WPF let
main argv = let b = new Button(Content="Button)
// b is a Button with Button as content let
f(sender:obj)(e:RoutedEventArgs) = // (#3) f is
a fun going to handle the Button.ClickEvent // f

25.5. RETRIEVING STATE FROM CALLERS


signature must be curried, not tuple as govened
by Delegate-RoutedEventHandler.
// that means
f(sender:obj,e:RoutedEventArgs) will not work. let
b = sender:?>Button // sender will have Buttontype.
Convert it to Button into b.
MessageBox.Show(b.Content:?>string) // Retrieve the content of b which is obj. // Convert it to string and
display by <code>Messagebox.Show</code> |>
ignore // ignore the return because f-signature requires: obj->RoutedEventArgs->unit let d = new
RoutedEventHandler(f) // (#2) d will have typeRoutedEventHandler, // RoutedEventHandler is a kind
of delegate to handle Button.ClickEvent. // The f
must have signature governed by RoutedEventHandler.
b.AddHandler(Button.ClickEvent,d) // (#1) attach a
RountedEventHandler-d for Button.ClickEvent let w =
new Window(Visibility=Visibility.Visible,Content=b) //
create a window-w have a Button-b // which will show the
content of b when clicked (new Application()).Run(w) //
create new Application() running the Window-w.
(#1) To attach a handler to a control for an event:
b.AddHandler(Button.ClickEvent,d)
(#2) Create a delegate/handler using a function: let d =
new RoutedEventHandler(f)
(#3) Create a function with specic signature dened by
the delegate: let f(sender:obj)(e:RoutedEventArgs) = ....
b is the control.
AddHandler is attach.
Button.ClickEvent is the event.
d is delegate/handler. It is a layer to make sure the
signature is correct
f is the real function/program provided to the delegate.
Rule#1: b must have this event Button.ClickEvent: b
is type-Button-object. ClickEvent is a static property
of type-ButtonBase which is inherited by type-Button.
So Button-type will also have this static property ClickEvent.
Rule#2: d must be the handler of ClickEvent: ClickEvent is type-RoutedEvent. RoutedEvents handler is
RoutedEventHandler, just adding Handler at end. RoutedEventHandler is a dened delegate in .NET library.
To create d, just let d = new RoutedEventHandler(f),
where f is function.
Rule#3: f must have signature obeying delegate-ds
denition: Check .NET library, RoutedEventHandler
is a delegate of C#-signature: void RoutedEventHandler(object sender, RoutedEventArgs e). So f must
have same signature. Present the signature in F# is (obj
* RountedEventHandler) -> unit

77
open System type SuperFileReader() = let progressChanged
=
new
Event<int>()
member
this.ProgressChanged = progressChanged.Publish member this.OpenFile (lename : string, charsPerBlock)
= use sr = new System.IO.StreamReader(lename) let
streamLength = int64 sr.BaseStream.Length let sb =
new System.Text.StringBuilder(int streamLength) let
charBuer = Array.zeroCreate<char> charsPerBlock let
mutable oldProgress = 0 let mutable totalCharsRead = 0
progressChanged.Trigger(0) while not sr.EndOfStream
do (* sr.ReadBlock returns number of characters read
from stream *) let charsRead = sr.ReadBlock(charBuer,
0, charBuer.Length) totalCharsRead <- totalCharsRead
+ charsRead (* appending chars read from buer *)
sb.Append(charBuer, 0, charsRead) |> ignore let
newProgress = int(decimal totalCharsRead / decimal
streamLength * 100m) if newProgress > oldProgress
then progressChanged.Trigger(newProgress) // passes
newProgress as state to callbacks oldProgress <newProgress sb.ToString() let leReader = new SuperFileReader() leReader.ProgressChanged.Add(fun
percent -> printfn "%i percent done... percent) let
x = leReader.OpenFile(@"C:\Test.txt, 50) printfn
"%s[...]" x.[0 .. if x.Length <= 100 then x.Length - 1
else 100]
This program has the following types:
type SuperFileReader = class new : unit -> SuperFileReader member OpenFile : filename:string *
charsToRead:int -> string member ProgressChanged :
IEvent<int> end val leReader : SuperFileReader val x :
string
Since our event has the type IEvent<int>, we can pass int
data as state to listening callbacks. This program outputs
the following:
0 percent done... 4 percent done... 9 percent done... 14
percent done... 19 percent done... 24 percent done... 29
percent done... 34 percent done... 39 percent done... 44
percent done... 49 percent done... 53 percent done... 58
percent done... 63 percent done... 68 percent done... 73
percent done... 78 percent done... 83 percent done... 88
percent done... 93 percent done... 98 percent done... 100
percent done... In computer programming, event-driven
programming or event-based programming is a programming paradig[...]

25.5 Retrieving State from Callers


A common idiom in event-driven programming is preand post-event handling, as well as the ability to cancel
events. Cancellation requires two-way communication
Events can pass state to callbacks with minimal eort. between an event handler and a listener, which we can
Here is a simple program which reads a le in blocks of easily accomplish through the use of ref cells or mutable
members:
characters:

25.4 Passing State To Callbacks

78
type Person(name : string) = let mutable _name =
name; let nameChanging = new Event<string * bool
ref>() let nameChanged = new Event<unit>() member
this.NameChanging = nameChanging.Publish member
this.NameChanged = nameChanged.Publish member
this.Name with get() = _name and set(value) = let
cancelChange = ref false nameChanging.Trigger(value,
cancelChange) if not !cancelChange then _name
<- value nameChanged.Trigger() let p = new Person(Bob) p.NameChanging.Add(fun (name, cancel)
-> let exboyfriends = ["Steve"; Mike"; Jon"; Seth"]
if List.exists (fun forbiddenName -> forbiddenName =
name) exboyfriends then printfn "-- No %ss allowed!"
name cancel := true else printfn "-- Name allowed)
p.NameChanged.Add(fun () -> printfn "-- Name changed
to %s p.Name) let tryChangeName newName = printfn
Attempting to change name to '%s" newName p.Name
<- newName tryChangeName Joe tryChangeName
Moe tryChangeName Jon tryChangeName Thor
This program has the following types:

CHAPTER 25. EVENTS


Name allowed) (* ... rest of program ... *)
By convention, custom event parameters should inherit
from System.EventArgs, and should have the sux EventArgs.

25.6 Using the Event Module


F# allows users to pass event handlers around as rst-class
values in fundamentally the same way as all other functions. The Event module has a variety of functions for
working with event handlers:
val choose : ('T -> 'U option) -> IEvent<'del,'T> ->
IEvent<'U> (requires delegate and 'del :> Delegate)
Return a new event which res on a selection
of messages from the original event. The selection function takes an original message to an
optional new message.

type Person = class new : name:string -> Person member


Name : string member NameChanged : IEvent<unit> val lter : ('T -> bool) -> IEvent<'del,'T> ->
member NameChanging : IEvent<string * bool ref> IEvent<'T> (requires delegate and 'del :> Delegate)
member Name : string with set end val p : Person val
tryChangeName : string -> unit
Return a new event that listens to the original
event and triggers the resulting event only when
the argument to the event passes the given funcThis program outputs the following:
tion.
Attempting to change name to 'Joe' -- Name allowed - Name changed to Joe Attempting to change name to
val listen : ('T -> unit) -> IEvent<'del,'T> -> unit
'Moe' -- Name allowed -- Name changed to Moe At(requires delegate and 'del :> Delegate)
tempting to change name to 'Jon' -- No Jons allowed!
Attempting to change name to 'Thor' -- Name allowed -Run the given function each time the given
Name changed to Thor
event is triggered.
If we need to pass a signicant amount of state to listeners, then its recommended to wrap the state in an object
val map : ('T -> 'U) -> IEvent<'del,'T> ->
as follows:
IEvent<'U> (requires delegate and 'del :> Delegate)
type NameChangingEventArgs(newName : string)
= inherit System.EventArgs() let mutable cancel =
Return a new event which res on a selection
false member this.NewName = newName memof messages from the original event. The seber this.Cancel with get() = cancel and set(value)
lection function takes an original message to an
= cancel <- value type Person(name : string) = let
optional new message.
mutable _name = name; let nameChanging = new
Event<NameChangingEventArgs>() let nameChanged
val merge : IEvent<'del1,'T> -> IEvent<'del2,'T> ->
= new Event<unit>() member this.NameChanging =
IEvent<'T> (requires delegate and 'del1 :> Delegate
nameChanging.Publish member this.NameChanged
and delegate and 'del2 :> Delegate)
= nameChanged.Publish member this.Name with
get() = _name and set(value) = let eventArgs =
Fire the output event when either of the input
new NameChangingEventArgs(value) nameChangevents re.
ing.Trigger(eventArgs) if not eventArgs.Cancel then
_name <- value nameChanged.Trigger() let p = new
Person(Bob) p.NameChanging.Add(fun e -> let val pairwise : IEvent<'del,'T> -> IEvent<'T * 'T>
exboyfriends = ["Steve"; Mike"; Jon"; Seth"] if (requires delegate and 'del :> Delegate)
List.exists (fun forbiddenName -> forbiddenName =
e.NewName) exboyfriends then printfn "-- No %ss
Return a new event that triggers on the second
allowed!" e.NewName e.Cancel <- true else printfn "-and subsequent triggerings of the input event.

25.6. USING THE EVENT MODULE


The Nth triggering of the input event passes
the arguments from the N-1th and Nth triggering as a pair. The argument passed to the N1th triggering is held in hidden internal state
until the Nth triggering occurs. You should
ensure that the contents of the values being
sent down the event are not mutable. Note
that many EventArgs types are mutable, e.g.
MouseEventArgs, and each ring of an event
using this argument type may reuse the same
physical argument obejct with dierent values.
In this case you should extract the necessary information from the argument before using this
combinator.
val partition : ('T -> bool) -> IEvent<'del,'T> ->
IEvent<'T> * IEvent<'T> (requires delegate and 'del
:> Delegate
Return a new event that listens to the original
event and triggers the rst resulting event if the
application of the predicate to the event arguments returned true, and the second event if it
returned false.
val scan : ('U -> 'T -> 'U) -> 'U -> IEvent<'del,'T> ->
IEvent<'U> (requires delegate and 'del :> Delegate)
Return a new event consisting of the results
of applying the given accumulating function to
successive values triggered on the input event.
An item of internal state records the current
value of the state parameter. The internal state
is not locked during the execution of the accumulation function, so care should be taken
that the input IEvent not triggered by multiple
threads simultaneously.
val split :
('T -> Choice<'U1,'U2>) ->
IEvent<'del,'T> -> IEvent<'U1> * IEvent<'U2>
(requires delegate and 'del :> Delegate)
Return a new event that listens to the original
event and triggers the rst resulting event if the
application of the function to the event arguments returned a Choice2Of1, and the second
event if it returns a Choice2Of2.
Take the following snippet:
p.NameChanging.Add(fun (e : NameChangingEventArgs) -> let exboyfriends = ["Steve"; Mike"; Jon";
Seth"] if List.exists (fun forbiddenName -> forbiddenName = e.NewName) exboyfriends then printfn "-- No
%ss allowed!" e.NewName e.Cancel <- true)
We can rewrite this in a more functional style as follows:

79
p.NameChanging |> Event.lter (fun (e : NameChangingEventArgs) -> let exboyfriends = ["Steve"; Mike";
Jon"; Seth"] List.exists (fun forbiddenName >forbiddenName = e.NewName) exboyfriends ) |>
Event.listen (fun e -> printfn "-- No %ss allowed!"
e.NewName e.Cancel <- true)

Chapter 26

Modules and Namespaces


Modules and Namespaces are primarily used for group- Like all modules, we can use the open keyword to give
ing and organizing code.
us access to the methods inside a module without fully
qualifying the naming of the method. This allows us to
revise Program.fs as follows:

26.1 Dening Modules

open DataStructures let x = StackNode(1, StackNode(2,


StackNode(3, EmptyStack))) let y = getRange 5 10
printfn "%A x printfn "%A y

No code is required to dene a module. If a codele does


not contain a leading namespace or module declaration,
F# code will implicitly place the code in a module, where
the name of the module is the same as the le name with 26.1.1 Submodules
the rst letter capitalized.
Its very easy to create submodules using the module keyTo access code in another module, simply use . notation: word:
moduleName.member. Notice that this notation is similar to the syntax used to access static members -- this is (* DataStructures.fs *) type 'a Stack = | EmptyStack |
not a coincidence. F# modules are compiled as classes StackNode of 'a * 'a Stack module StackOps = let rec
which only contain static members, values, and type def- getRange startNum endNum = if startNum > endNum
then EmptyStack else StackNode(startNum, getRange
initions.
(startNum+1) endNum)
Lets create two les:
DataStructures.fs

Since the getRange method is under another module,


type 'a Stack = | EmptyStack | StackNode of 'a * 'a Stack the fully qualied name of this method is DataStruclet rec getRange startNum endNum = if startNum > tures.StackOps.getRange. We can use it as follows:
endNum then EmptyStack else StackNode(startNum, (* Program.fs *) open DataStructures let x = StackNgetRange (startNum+1) endNum)
ode(1, StackNode(2, StackNode(3, EmptyStack))) let y
Program.fs

= StackOps.getRange 5 10 printfn "%A x printfn "%A


y

let x = DataStructures.StackNode(1, DataStructures.StackNode(2,


DataStructures.StackNode(3, F# allows us to create a module and a type having the
DataStructures.EmptyStack))) let y = DataStruc- same name, for example the following code is perfectly
acceptable:
tures.getRange 5 10 printfn "%A x printfn "%A y
type 'a Stack = | EmptyStack | StackNode of 'a * 'a Stack
module Stack = let rec getRange startNum endNum =
This program outputs:
if startNum > endNum then EmptyStack else StackNStackNode (1,StackNode (2,StackNode (3,Empode(startNum, getRange (startNum+1) endNum)
tyStack))) StackNode (5, StackNode (6,StackNode
(7,StackNode (8,StackNode (9,StackNode (10,EmptyStack))))))
Note: Its possible to nest submodules inside
other submodules. However, as a general prinNote: Remember, order of compilation matciple, its best to avoid creating complex module
ters in F#. Dependencies must come before
hierarchies. Functional programming libraries
dependents, so DataStructures.fs comes before
tend to be very at with nearly all functionProgram.fs when compiling this program.
ality accessible in the rst 2 or 3 levels of a
80

26.1. DEFINING MODULES


hierarchy. This is in contrast to many other
OO languages which encourage programmers
to create deeply nested class libraries, where
functionality might be buried 8 or 10 levels
down the hierarchy.

26.1.2

81
Seq = begin val foralli : (int -> 'a -> bool) -> seq<'a>
-> bool end module StringExtensions = begin end >
hello world.IsPalindrome;; val it : bool = false > System.String.Reverse(hello world);; val it : System.String
= dlrow olleh

Extending Types and Modules


26.1.3 Module Signatures

F# supports extension methods, which allow programmers to add new static and instance methods to classes By default, all members in a module are accessible from
and modules without inheriting from them.
outside the module. However, a module often contain
members which should not be accessible outside itself,
Extending a Module
such as helper functions. One way to expose only a subThe Seq module contains several pairs of methods:
set of a modules members is by creating a signature le
for that module. (Another way is to apply .Net CLR ac iter/iteri
cess modiers of public, internal, or private to individual
declarations).
map/mapi
Seq has a forall member, but does not have a corresponding foralli function, which includes the index of each
sequence element. We add this missing method to the
module simply by creating another module with the same
name. For example, using fsi:
> module Seq = let foralli f s = s |> Seq.mapi (fun i x ->
i, x) (* pair item with its index *) |> Seq.forall (fun (i, x)
-> f i x) (* apply item and index to function *) let isPalindrome (input : string) = input |> Seq.take (input.Length
/ 2) |> Seq.foralli (fun i x -> x = input.[input.Length i - 1]);; module Seq = begin val foralli : (int -> 'a ->
bool) -> seq<'a> -> bool end val isPalindrome : string
-> bool > isPalindrome hello";; val it : bool = false >
isPalindrome racecar";; val it : bool = true
Extending a Type
The System.String has many useful methods, but lets say
we thought it was missing a few important functions, Reverse and IsPalindrome. Since this class is marked as
sealed or NotInheritable, we can't create a derived version of this class. Instead, we create a module with the
new methods we want. Heres an example in fsi which
demonstrates how to add new static and instance methods to the String class:
> module Seq = let foralli f s = s |> Seq.mapi (fun i x ->
i, x) (* pair item with its index *) |> Seq.forall (fun (i, x)
-> f i x) (* apply item and index to function *) module
StringExtensions = type System.String with member
this.IsPalindrome = this |> Seq.take (this.Length / 2) |>
Seq.foralli (fun i x -> this.[this.Length - i - 1] = x) static
member Reverse(s : string) = let chars : char array =
let temp = Array.zeroCreate s.Length let charsToTake
= if temp.Length % 2 <> 0 then (temp.Length + 1)
/ 2 else temp.Length / 2 s |> Seq.take charsToTake
|> Seq.iteri (fun i x -> temp.[i] <- s.[temp.Length
- i - 1] temp.[temp.Length - i - 1] <- x) temp new
System.String(chars) open StringExtensions;; module

Signature les have the same name as their corresponding module, but end with a ".fsi extension (f-sharp interface). Signature les always come before their implementation les, which have a corresponding ".fs extension.
For example:
DataStructures.fsi
type 'a stack = | EmptyStack | StackNode of 'a * 'a stack
module Stack = val getRange : int -> int -> int stack val
hd : 'a stack -> 'a val tl : 'a stack -> 'a stack val fold : ('a
-> 'b -> 'a) -> 'a -> 'b stack -> 'a val reduce : ('a -> 'a ->
'a) -> 'a stack -> 'a
DataStructures.fs
type 'a stack = | EmptyStack | StackNode of 'a * 'a
stack module Stack = (* helper functions *) let internal_head_tail = function | EmptyStack -> failwith
Empty stack | StackNode(hd, tail) -> hd, tail let rec
internal_fold_left f acc = function | EmptyStack -> acc
| StackNode(hd, tail) -> internal_fold_left f (f acc hd)
tail (* public functions *) let rec getRange startNum
endNum = if startNum > endNum then EmptyStack
else StackNode(startNum, getRange (startNum+1)
endNum) let hd s = internal_head_tail s |> fst let tl
s = internal_head_tail s |> snd let fold f seed stack
= internal_fold_left f seed stack let reduce f stack =
internal_fold_left f (hd stack) (tl stack)
Program.fs
open DataStructures let x = Stack.getRange 1 10 printfn
"%A (Stack.hd x) printfn "%A (Stack.tl x) printfn
"%A (Stack.fold ( * ) 1 x) printfn "%A (Stack.reduce
( + ) x) (* printfn "%A (Stack.internal_head_tail x) *)
(* will not compile *)
Since Stack.internal_head_tail is not dened in our interface le, the method is marked private and no longer
accessible outside of the DataStructures module.

82
Module signatures are useful to building a code librarys
skeleton, however they have a few caveats. If you want
to expose a class, record, or union in a module through
a signature, then the signature le must expose all of the
objects members, records elds, and unions cases. Additionally, the signature of the function dened in the module and its corresponding signature in the signature le
must match exactly. Unlike OCaml, F# does not allow a
function in a module with the generic type 'a -> 'a -> 'a to
be restricted to int -> int -> int in the signature le.

26.2 Dening Namespaces


A namespace is a hierarchial catergorization of modules, classes, and other namespaces. For example, the
System.Collections namespace groups together all of the
collections and data structures in the .NET BCL, whereas
the System.Security.Cryptography namespace groups together all classes which provide cryptographic services.
Namespaces are primarily used to avoid name conicts.
For example, lets say we were writing an application incorporated code from several dierent vendors. If Vendor A and Vendor B both have a class called Collections.Stack, and we wrote the code let s = new Stack(),
how would the compiler know whether which stack we
intended to create? Namespaces can eliminate this ambiguity by adding one more layer of grouping to our code.

CHAPTER 26. MODULES AND NAMESPACES


10 printfn "%A (Stack.hd x) printfn "%A (Stack.tl
x) printfn "%A (Stack.fold ( * ) 1 x) printfn "%A
(Stack.reduce ( + ) x)
Where is the DataStructures Module?
You may have expected the code in Program.fs above
to open Princess.Collections.DataStructures rather than
Princess.Collections. According to the F# spec, F# treats
anonymous implementation les (which are les without a
leading module or namespace declaration) by putting all
code in an implicit module which matches the codes lename. Since we have a leading namespace declaration,
F# does not create the implicit module.
.NET does not permit users to create functions or values
outside of classes or modules. As a consequence, we cannot write the following code:
namespace Princess.Collections type 'a stack = | EmptyStack | StackNode of 'a * 'a stack let somefunction() =
12 (* <--- functions not allowed outside modules *) (* ...
*)
If we prefer to have a module called DataStructures, we
can write this:
namespace Princess.Collections module DataStructures
type 'a stack = | EmptyStack | StackNode of 'a * 'a stack
let somefunction() = 12 (* ... *)

Code is grouped under a namespace using the namespace Or equivalently, we dene a module and place it a namespace simultaneously using:
keyword:
module Princess.Collections.DataStructures type 'a
stack = | EmptyStack | StackNode of 'a * 'a stack let
namespace Princess.Collections type 'a stack = | Empsomefunction() = 12 (* ... *)
tyStack | StackNode of 'a * 'a stack module Stack = val
getRange : int -> int -> int stack val hd : 'a stack -> 'a
val tl : 'a stack -> 'a stack val fold : ('a -> 'b -> 'a) -> 'a
-> 'b stack -> 'a val reduce : ('a -> 'a -> 'a) -> 'a stack -> 'a
DataStructures.fsi

DataStructures.fs
namespace Princess.Collections type 'a stack = | EmptyStack | StackNode of 'a * 'a stack module Stack = (*
helper functions *) let internal_head_tail = function |
EmptyStack -> failwith Empty stack | StackNode(hd,
tail) -> hd, tail let rec internal_fold_left f acc = function
| EmptyStack -> acc | StackNode(hd, tail) -> internal_fold_left f (f acc hd) tail (* public functions *) let rec
getRange startNum endNum = if startNum > endNum
then EmptyStack else StackNode(startNum, getRange
(startNum+1) endNum) let hd s = internal_head_tail s
|> fst let tl s = internal_head_tail s |> snd let fold f seed
stack = internal_fold_left f seed stack let reduce f stack
= internal_fold_left f (hd stack) (tl stack)

26.2.1 Adding to Namespace from Multiple Files

Unlike modules and classes, any le can contribute to a


namespace. For example:
DataStructures.fs

namespace Princess.Collections type 'a stack = | EmptyStack | StackNode of 'a * 'a stack module Stack = (*
helper functions *) let internal_head_tail = function |
EmptyStack -> failwith Empty stack | StackNode(hd,
tail) -> hd, tail let rec internal_fold_left f acc = function
| EmptyStack -> acc | StackNode(hd, tail) -> internal_fold_left f (f acc hd) tail (* public functions *) let rec
getRange startNum endNum = if startNum > endNum
then EmptyStack else StackNode(startNum, getRange
(startNum+1) endNum) let hd s = internal_head_tail s
|> fst let tl s = internal_head_tail s |> snd let fold f seed
Program.fs
stack = internal_fold_left f seed stack let reduce f stack
open Princess.Collections let x = Stack.getRange 1 = internal_fold_left f (hd stack) (tl stack)

26.2. DEFINING NAMESPACES

MoreDataStructures.fs
namespace Princess.Collections type 'a tree when 'a :>
System.IComparable<'a> = | EmptyTree | TreeNode of
'a * 'a tree * 'a tree module Tree = let rec insert (x :
#System.IComparable<'a>) = function | EmptyTree ->
TreeNode(x, EmptyTree, EmptyTree) | TreeNode(y, l,
r) as node -> match x.CompareTo(y) with | 0 -> node | 1
-> TreeNode(y, l, insert x r) | 1 -> TreeNode(y, insert
x l, r) | _ -> failwith CompareTo returned illegal value
Since we have a leading namespace declaration in both
les, F# does not create any implicit modules. The 'a
stack, 'a tree, Stack, and Tree types are all accessible
through the Princess.Collections namespace:
Program.fs
open Princess.Collections let x = Stack.getRange 1 10 let
y = let rnd = new System.Random() [ for a in 1 .. 10 ->
rnd.Next(0, 100) ] |> Seq.fold (fun acc x -> Tree.insert
x acc) EmptyTree printfn "%A (Stack.hd x) printfn
"%A (Stack.tl x) printfn "%A (Stack.fold ( * ) 1 x)
printfn "%A (Stack.reduce ( + ) x) printfn "%A y

26.2.2

Controlling Class and Module Accessibility

Unlike modules, there is no equivalent to a signature le


for namespaces. Instead, the visibility of classes and submodules is controlled through standard accessibility modiers:
namespace Princess.Collections type 'a tree when 'a :>
System.IComparable<'a> = | EmptyTree | TreeNode of
'a * 'a tree * 'a tree (* InvisibleModule is only accessible
by classes or modules inside the Princess.Collections
namespace*) module private InvisibleModule = let msg
= I'm invisible!" module Tree = (* InvisibleClass is only
accessible by methods inside the Tree module *) type
private InvisibleClass() = member x.Msg() = I'm invisible too!" let rec insert (x : #System.IComparable<'a>)
= function | EmptyTree -> TreeNode(x, EmptyTree,
EmptyTree) | TreeNode(y, l, r) as node -> match
x.CompareTo(y) with | 0 -> node | 1 -> TreeNode(y,
l, insert x r) | 1 -> TreeNode(y, insert x l, r) | _ ->
failwith CompareTo returned illegal value

83

Chapter 27

Units of Measure
Units of measure allow programmers to annotate oats
and integers with statically-typed unit metadata. This can
be handy when writing programs which manipulate oats
and integers representing specic units of measure, such
as kilograms, pounds, meters, newtons, pascals, etc. F#
will verify that units are used in places where the programmer intended. For example, the F# compiler will
throw an error if a oat<m/s> is used where it expects a
oat<kg>.

27.1 Use Cases


27.1.1

Statically Checked Type Conversions

Units of measure are invaluable to programmers who


work in scientic research, they add an extra layer of
protection to guard against conversion related errors. To
cite a famous case study, NASAs $125 million Mars Climate Orbiter project ended in failure when the orbiter
dipped 90 km closer to Mars than originally intended,
causing it to tear apart and disintegrate spectacularly in
the Mars atmosphere. A post mortem analysis narrowed
down the root cause of the problem to a conversion error in the orbiters propulsion systems used to lower the
spacecraft into orbit: NASA passed data to the systems in
metric units, but the software expected data in Imperial
units. Although there were many contributing projectmanagement errors which resulted in the failed mission,
this software bug in particular could have been prevented
if the software engineers had used a type-system powerful enough to detect unit-related errors.

scrollable windows, so every coordinate has to


be interpreted as either relative to the window
or relative to the page, and that makes a big
dierence, and keeping them straight is pretty
important. [...]
The compiler wont help you if you assign one
to the other and Intellisense wont tell you bupkis. But they are semantically dierent; they
need to be interpreted dierently and treated
dierently and some kind of conversion function will need to be called if you assign one to
the other or you will have a runtime bug. If
youre lucky. [...]
In Excels source code you see a lot of rw and
col and when you see those you know that they
refer to rows and columns. Yep, theyre both
integers, but it never makes sense to assign
between them. In Word, I'm told, you see a
lot of xl and xw, where xl means horizontal coordinates relative to the layout and xw
means horizontal coordinates relative to the
window. Both ints. Not interchangeable. In
both apps you see a lot of cb meaning count
of bytes. Yep, its an int again, but you know
so much more about it just by looking at the
variable name. Its a count of bytes: a buer
size. And if you see xl = cb, well, blow the Bad
Code Whistle, that is obviously wrong code,
because even though xl and cb are both integers, its completely crazy to set a horizontal
oset in pixels to a count of bytes.

In short, Microsoft depends on coding conventions to en27.1.2 Decorating Data With Contextual code contextual data about a variable, and they depend on
code reviews to enforce correct usage of a variable from
Information
its context. This works in practice, but its still possible
for incorrect code to work its way it the product without
In an article Making Code Look Wrong, Joel Spolsky
the bug being detected for months.
describes a scenario in which, during the design of Microsoft Word and Excel, programmers at Microsoft were If Microsoft were using a language with units of measure,
required to track the position of objects on a page using they could have dened their own rw, col, xw, xl, and cb
units of measure so that an assignment of the form int<xl>
two non-interchangeable coordinate systems:
= int<cb> not only fails visual inspection, it doesn't even
In WYSIWYG word processing, you have
compile.
84

27.3. DIMENSIONLESS VALUES

27.2 Dening Units

85
Units can be dened for integral types too:

> [<Measure>] type col [<Measure>] type row let


New units of measure are dened using the Measure at- colOset (a : int<col>) (b : int<col>) = a - b let rowOtribute:
set (a : int<row>) (b : int<row>) = a - b;; [<Measure>]
[<Measure>] type m (* meter *) [<Measure>] type s (* type col [<Measure>] type row val colOset : int<col>
-> int<col> -> int<col> val rowOset : int<row> ->
second *)
int<row> -> int<row>
Additionally, we can dene types measures which are derived from existing measures as well:
[<Measure>] type m (* meter *) [<Measure>] type 27.3 Dimensionless Values
s (* second *) [<Measure>] type kg (* kilogram *)
[<Measure>] type N = (kg * m)/(s^2) (* Newtons *)
A value without a unit is dimensionless. Dimension[<Measure>] type Pa = N/(m^2) (* Pascals *)
less values are represented implicitly by writing them
out without units (i.e. 7.0, 14, 200.5), or they can be
represented explicitly using the <1> type (i.e. 7.0<1>,
Important: Units of measure look like a data
14<1>, 200.5<1>).
type, but they aren't. .NETs type system does
not support the behaviors that units of measure
have, such as being able to square, divide, or
raise datatypes to powers. This functionality is
provided by the F# static type checker at compile time, but units are erased from compiled
code. Consequently, it is not possible to determine values unit at runtime.

We can convert dimensionless units to a specic measure


by multiplying by 1<targetMeasure>. We can convert a
measure back to a dimensionless unit by passing it to the
built-in oat or int methods:
[<Measure>] type m (* val to_meters : (x : oat<'u>)
-> oat<'u m> *) let to_meters x = x * 1<m> (* val
of_meters : (x : oat<m>) -> oat *) let of_meters (x :
oat<m>) = oat x

We can create instances of oat and integer data which


represent these units using the same notation we use with
Alternatively, its often easier (and safer) to divide away
generics:
unneeded units:
> let distance = 100.0<m> let time = 5.0<s> let speed =
distance / time;; val distance : oat<m> = 100.0 val time let of_meters (x : oat<m>) = x / 1.0<m>
: oat<s> = 5.0 val speed : oat<m/s> = 20.0
Notice the that F# automatically derives a new unit, m/s,
for the value speed. Units of measure will multiply, divide, and cancel as needed depending on how they are
used. Using these properties, its very easy to convert between two units:

27.4 Generalizing Units of Measure

Since measures and dimensionless values are (or appear


[<Measure>] type C [<Measure>] type F let to be) generic types, we can write functions which operate
to_fahrenheit (x : oat<C>) = x * (9.0<F>/5.0<C>) +
on both transparently:
32.0<F> let to_celsius (x : oat<F>) = (x - 32.0<F>) *
> [<Measure>] type m [<Measure>] type kg let vanil(5.0<C>/9.0<F>)
laFloats = [10.0; 15.5; 17.0] let lengths = [ for a in [2.0;
7.0; 14.0; 5.0] -> a * 1.0<m> ] let masses = [ for a in
Units of measure are statically checked at compile time [155.54; 179.01; 135.90] -> a * 1.0<kg> ] let densities =
for proper usage. For example, if we use a measure where [ for a in [0.54; 1.0; 1.1; 0.25; 0.7] -> a * 1.0<kg/m^3>
it isn't expected, we get a compilation error:
] let average (l : oat<'u> list) = let sum, count = l |>
> [<Measure>] type m [<Measure>] type s let speed (x List.fold (fun (sum, count) x -> sum + x, count + 1.0<_>)
: oat<m>) (y : oat<s>) = x / y;; [<Measure>] type (0.0<_>, 0.0<_>) sum / count;; [<Measure>] type m
m [<Measure>] type s val speed : oat<m> -> oat<s> [<Measure>] type kg val vanillaFloats : oat list =
-> oat<m/s> > speed 20.0<m> 4.0<s>;; (* should get [10.0; 15.5; 17.0] val lengths : oat<m> list = [2.0; 7.0;
a speed *) val it : oat<m/s> = 5.0 > speed 20.0<m> 14.0; 5.0] val masses : oat<kg> list = [155.54; 179.01;
4.0<m>;; (* boom! *) speed 20.0<m> 4.0<m>;; --------- 135.9] val densities : oat<kg/m ^ 3> list = [0.54; 1.0;
-----^^^^^^ stdin(39,15): error FS0001: Type mismatch. 1.1; 0.25; 0.7] val average : oat<'u> list -> oat<'u> >
Expecting a oat<s> but given a oat<m>. The unit of average vanillaFloats, average lengths, average masses,
measure 's does not match the unit of measure 'm'
average densities;; val it : oat * oat<m> * oat<kg>
* oat<kg/m ^ 3> = (14.16666667, 7.0, 156.8166667,

86

CHAPTER 27. UNITS OF MEASURE


F# Units of Measure (MSDN)

0.718)
Since units are erased from compiled code, they are not
considered a real data type, so they can't be used directly
as a type parameter in generic functions and classes. For
example, the following code will not compile:
> type triple<'a> = { a : oat<'a>; b : oat<'a>; c :
oat<'a>};; type triple<'a> = { a : oat<'a>; b : oat<'a>;
c : oat<'a>};; ------------------------------^^ stdin(40,31):
error FS0191: Expected unit-of-measure parameter, not
type parameter. Explicit unit-of-measure parameters
must be marked with the [<Measure>] attribute
F# does not infer that 'a is a unit of measure above, possibly because the following code appears correct, but it can
be used in non-sensical ways:
type quad<'a> = { a : oat<'a>; b : oat<'a>; c :
oat<'a>; d : 'a}
The type 'a can be a unit of measure or a data type, but
not both at the same time. F#'s type checker assumes 'a is
a type parameter unless otherwise specied. We can use
the [<Measure>] attribute to change the 'a to a unit of
measure:
> type triple<[<Measure>] 'a> = { a : oat<'a>; b :
oat<'a>; c : oat<'a>};; type triple<[<Measure>] 'a> =
{a: oat<'a>; b: oat<'a>; c: oat<'a>;} > { a = 7.0<kg>;
b = 10.5<_>; c = 0.5<_> };; val it : triple<kg> = {a =
7.0; b = 10.5; c = 0.5;}

27.5 F# PowerPack
The F# PowerPack (FSharp.PowerPack.dll) includes a
number of predened units of measure for scientic applications. These are available in the following modules:
Microsoft.FSharp.Math.SI - a variety of predened
measures in the International System of Units (SI).
Microsoft.FSharp.Math.PhysicalConstants - Fundamental physical constants with units of measure.

27.6 External Resources


Andrew Kennedys 4-part tutorial on units of measure:
Part 1: Introducing Units
Part 2: Unit Conversions
Part 3: Generic Units
Part 4: Parameterized Types

Chapter 28

Caching
Caching is often useful to re-use data which has already puted on every successive call. Long-running pure funcbeen computed. F# provides a number of built-in tech- tions (i.e. functions which have no side-eects) are good
niques to cache data for future use.
candidates for memoization. Consider the recursive definition for computing Fibonacci numbers:
> #time;; --> Timing now on > let rec b n = if n = 0I
then 0I elif n = 1I then 1I else b (n - 1I) + b(n - 2I);;
Real: 00:00:00.000, CPU: 00:00:00.000, GC gen0: 0,
F# automatically caches the value of any function which gen1: 0, gen2: 0 val b : Math.bigint -> Math.bigint > b
takes no parameters. When F# comes across a function 35I;; Real: 00:00:23.557, CPU: 00:00:23.515, GC gen0:
with no parameters, F# will only evaluate the function 2877, gen1: 3, gen2: 0 val it : Math.bigint = 9227465I
once and reuse its value everytime the function is accessed. Compare the following:
Beyond b 35I, the runtime of the function becomes unlet isNebraskaCity_bad city = let cities = printfn Cre- bearable. Each recursive call to the b function throws
ating cities Set ["Bellevue"; Omaha"; Lincoln"; away all of its intermediate calculations to b(n - 1I)
Papillion"] |> Set.ofList cities.Contains(city) let isNe- and b(n - 2I), giving it a runtime complexity of about
braskaCity_good = let cities = printfn Creating cities O(2^n). What if we kept all of those intermediate calcuSet ["Bellevue"; Omaha"; Lincoln"; Papillion"] |> lations around in a lookup table? Heres the memoized
version of the b function:
Set.ofList fun city -> cities.Contains(city)
> #time;; --> Timing now on > let rec b = let dict =
Both functions accept and return the same values, but they new System.Collections.Generic.Dictionary<_,_>() fun
have very dierent behavior. Heres a comparison of the n -> match dict.TryGetValue(n) with | true, v -> v |
false, _ -> let temp = if n = 0I then 0I elif n = 1I then
output in fsi:
1I else b (n - 1I) + b(n - 2I) dict.Add(n, temp) temp;;
The implementation of isNebraskaCity_bad forces F# to val b : (Math.bigint -> Math.bigint) > b 35I;; Real:
re-create the internal set on each call. On the other hand, 00:00:00.000, CPU: 00:00:00.000, GC gen0: 0, gen1:
isNebraskaCity_good is a value initialized to the function 0, gen2: 0 val it : Math.bigint = 9227465I
fun city -> cities.Contains(city), so it creates its internal
set once and reuses it for all successive calls.
Much better! This version of the b function runs almost
instaneously. In fact, since we only calculate the value of
Note: Internally, isNebraskaCity_bad is comany b(n) precisely once, and dictionary lookups are an
piled as a static function which constructs a set
O(1) operation, this b function runs in O(n) time.
on every call. isNebraskaCity_good is comNotice all of the memoization logic is contained in the
piled as a static readonly property, where the
b function. We can write a more general function to
value is initialized in a static constructor.
memoize any function:

28.1 Partial Functions

This distinction is often subtle, but it can have a huge im- let memoize f = let dict = new System.Collections.Generic.Dictionary<_,_>() fun n ->
pact on an applications performance.
match dict.TryGetValue(n) with | (true, v) -> v | _ ->
let temp = f(n) dict.Add(n, temp) temp let rec b =
memoize(fun n -> if n = 0I then 0I elif n = 1I then 1I
28.2 Memoization
else b (n - 1I) + b (n - 2I) )
Memoization is a fancy word meaning that computed
values are stored in a lookup table rather than recom87

Note: Its very important to remember that

88

CHAPTER 28. CACHING


the implementation above is not thread-safe
-- the dictionary should be locked before
adding/retrieving items if it will be accessed by
multiple threads.
Additionally, although dictionary lookups occur in constant time, the hash function used
by the dictionary can take an arbitrarily long
time to execute (this is especially true with
strings, where the time it takes to hash a string
is proportional to its length). For this reason, it is wholly possible for a memoized function to have less performance than an unmemoized function. Always prole code to determine whether optimization is necessary and
whether memoization genuinely improves performance.

28.3 Lazy Values


The F# lazy data type is an interesting primitive which
delays evaluation of a value until the value is actually
needed. Once computed, lazy values are cached for reuse
later:
> let x = lazy(printfn I'm lazy"; 5 + 5);; val x : Lazy<int>
= <unevaluated> > x.Force();; (* Should print I'm lazy
*) I'm lazy val it : int = 10 > x.Force();; (* Value already
computed, should not print I'm lazy again *) val it : int
= 10
F# uses some compiler magic to avoid evaluating the expression (printfn I'm lazy"; 5 + 5) on declaration. Lazy
values are probably the simplest form of caching, however they can be used to create some interesting and sophisticated data structures. For example, two F# data
structures are implemented on top of lazy values, namely
the F# Lazy List and Seq.cache method.
Lazy lists and cached sequences represent arbitrary sequences of potentially innite numbers of elements. The
elements are computed and cached the rst time they are
accessed, but will not be recomputed when the sequence
is enumerated again. Heres a demonstration in fsi:
> let x = seq { for a in 1 .. 10 -> printfn Got %i a; a }
|> Seq.cache;; val x : seq<int> > let y = Seq.take 5 x;; val
y : seq<int> > Seq.reduce (+) y;; Got 1 Got 2 Got 3 Got
4 Got 5 val it : int = 15 > Seq.reduce (+) y;; (* Should
not recompute values *) val it : int = 15 > Seq.reduce
(+) x;; (* Values 1 through 5 already computed, should
only compute 6 through 10 *) Got 6 Got 7 Got 8 Got 9
Got 10 val it : int = 55

Chapter 29

Active Patterns
Active Patterns allow programmers to wrap arbitrary val- tween active patterns and explicitly dened unions:
ues in a union-like data structure for easy pattern matching. For example, its possible wrap objects with an active
Active patterns dene an anonymous union, where
pattern, so that you can use objects in pattern matching
the explicit union has a name (numKind, seqWrapas easily as any other union type.
per, etc.).
Active patterns determine their constructor parameters using a kind of type-inference, whereas we need
to explicitly dene the constructor parameters for
each case of our explicit union.

29.1 Dening Active Patterns


Active patterns look like functions with a funny name:
let (|name1|name2|...|) = ...

29.1.1 Using Active Patterns

This function denes an ad hoc union data structure,


where each union case namen is separated from the next
by a | and the entire list is enclosed between (| and |)
(humbly called banana brackets). In other words, the
function does not have a simple name at all, it instead denes a series of union constructors.

The syntax for using active patterns looks a little odd, but
once you know whats going on, its very easy to understand. Active patterns are used in pattern matching expressions, for example:

A typical active pattern might look like this:


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

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


let testNum n = match n with | Even -> printfn "%i is
even n | Odd -> printfn "%i is odd n;; val ( |Even|Odd|
) : int -> Choice<unit,unit> val testNum : int -> unit >
testNum 12;; 12 is even val it : unit = () > testNum 17;;
17 is odd

Even and Odd are union constructors, so our active pattern either returns an instance of Even or an instance of Whats going on here? When the pattern matching funcOdd. The code above is roughly equivalent to the follow- tion encounters Even, it calls (|Even|Odd|) with parameter
ing:
in the match clause, its as if you've written:
type numKind = | Even | Odd let get_choice n = if n % 2 type numKind = | Even | Odd let get_choice n = if n
= 0 then Even else Odd
% 2 = 0 then Even else Odd let testNum n = match
get_choice n with | Even -> printfn "%i is even n | Odd
Active patterns can also dene union constructors which -> printfn "%i is odd n
take a set of parameters. For example, consider we can
wrap a seq<'a> with an active pattern as follows:
The parameter in the match clause is always passed as the
let (|SeqNode|SeqEmpty|) s = if Seq.isEmpty s then last argument to the active pattern expression. Using our
seq example from earlier, we can write the following:
SeqEmpty else SeqNode ((Seq.hd s), Seq.skip 1 s)
> let (|SeqNode|SeqEmpty|) s = if Seq.isEmpty s then
SeqEmpty else SeqNode ((Seq.head s), Seq.skip 1 s)
This code is, of course, equivalent to the following:
let perfectSquares = seq { for a in 1 .. 10 -> a * a
type 'a seqWrapper = | SeqEmpty | SeqNode of 'a } let rec printSeq = function | SeqEmpty -> printfn
* seq<'a> let get_choice s = if Seq.isEmpty s then Done. | SeqNode(hd, tl) -> printf "%A " hd printSeq
SeqEmpty else SeqNode ((Seq.hd s), Seq.skip 1 s)
tl;; val ( |SeqNode|SeqEmpty| ) : seq<'a> -> Choice<('a
* seq<'a>),unit> val perfectSquares : seq<int> val
You've probably noticed the immediate dierence be- printSeq : seq<'a> -> unit > printSeq perfectSquares;; 1
89

90

CHAPTER 29. ACTIVE PATTERNS

4 9 16 25 36 49 64 81 100 Done.

29.1.3 Partial Active Patterns

Traditionally, seqs are resistant to pattern matching, but A partial active pattern is a special class of single-case
now we can operate on them just as easily as lists.
active patterns: it either returns Some or None. For example, a very handy active pattern for working with regex
can be dened as follows:

29.1.2

Parameterizing Active Patterns

Its possible to pass arguments to active patterns, for example:

> let (|RegexContains|_|) pattern input = let matches =


System.Text.RegularExpressions.Regex.Matches(input,
pattern) if matches.Count > 0 then Some [ for m in
matches -> m.Value ] else None let testString = function | RegexContains "http://\char"005C\relax{}S+"
urls -> printfn Got urls: %A urls | RegexContains
"[^@]@[^.]+\.\W+" emails -> printfn Got email
address: %A emails | RegexContains "\d+" numbers
-> printfn Got numbers: %A numbers | _ -> printfn
Didn't nd anything.";; val ( |RegexContains|_| ) : string
-> string -> string list option val testString : string ->
unit > testString 867-5309, Jenny are you there?";; Got
numbers: ["867"; 5309"]

> let (|Contains|) needle (haystack : string) =


haystack.Contains(needle) let testString = function |
Contains kitty true -> printfn Text contains 'kitty'" |
Contains doggy true -> printfn Text contains 'doggy'"
| _ -> printfn Text neither contains 'kitty' nor 'doggy'";;
val ( |Contains| ) : string -> string -> bool val testString
: string -> unit > testString I have a pet kitty and shes
super adorable!";; Text contains 'kitty' val it : unit = ()
> testString Shes fat and purrs a lot :)";; Text neither
contains 'kitty' nor 'doggy'
This is equivalent to writing:
The single-case active pattern (|Contains|) wraps the
String.Contains function. When we call Contains kitty
true, F# passes kitty and the argument we're matching
against to the (|Contains|) active pattern and tests the return value against the value true. The code above is equivalent to:

type choice = | RegexContains of string list let


get_choice pattern input = let matches = System.Text.RegularExpressions.Regex.Matches(input, pattern) if matches.Count > 0 then Some (RegexContains [
for m in matches -> m.Value ]) else None let testString n =
match get_choice "http://\char"005C\relax{}S+" n with
| Some(RegexContains(urls)) -> printfn Got urls: %A
urls | None -> match get_choice "[^@]@[^.]+\.\W+"
n with | Some(RegexContains emails) -> printfn Got
email address: %A emails | None -> match get_choice
"\d+" n with | Some(RegexContains numbers) -> printfn
Got numbers: %A numbers | _ -> printfn Didn't nd
anything.

type choice = | Contains of bool let


get_choice needle (haystack :
string) = Contains(haystack.Contains(needle)) let testString n =
match get_choice kitty n with | Contains(true) ->
printfn Text contains 'kitty'" | _ -> match get_choice
doggy n with | Contains(true) -> printfn Text contains
'doggy'" | printfn Text neither contains 'kitty' nor
'doggy'"
Using partial active patterns, we can test an input against
any number of active patterns:
As you can see, the code using the active patterns is much let (|StartsWith|_|) needle (haystack : string) = if
cleaner and easier to read than the equivalent code using haystack.StartsWith(needle) then Some() else None
the explicitly dened union.
let (|EndsWith|_|) needle (haystack : string) = if

Note: Single-case active patterns might


not look terribly useful at rst, but they can
really help to clean up messy code. For
example, the active pattern above wraps up
the String.Contains method and allows us to
invoke it in a pattern matching expression.
Without the active pattern, pattern matching
quickly becomes messy:
let testString = function | (n : string) when
n.Contains(kitty) -> printfn Text contains
'kitty'" | n when n.Contains(doggy) -> printfn
Text contains 'doggy'" | _ -> printfn Text
neither contains 'kitty' nor 'doggy'"

haystack.EndsWith(needle) then Some() else None let


(|Equals|_|) x y = if x = y then Some() else None let
(|EqualsMonkey|_|) = function (* Higher-order active
pattern *) | Equals monkey () -> Some() | _ -> None
let (|RegexContains|_|) pattern input = let matches =
System.Text.RegularExpressions.Regex.Matches(input,
pattern) if matches.Count > 0 then Some [ for m in
matches -> m.Value ] else None let testString n =
match n with | StartsWith kitty () -> printfn starts
with 'kitty'" | StartsWith bunny () -> printfn starts
with 'bunny'" | EndsWith doggy () -> printfn ends
with 'doggy'" | Equals monkey () -> printfn equals
'monkey'" | EqualsMonkey -> printfn EqualsMonkey!"
(* Note: EqualsMonkey and EqualsMonkey() are equivalent *) | RegexContains "http://\char"005C\relax{}S+"
urls -> printfn Got urls: %A urls | RegexContains

29.2. ADDITIONAL RESOURCES


"[^@]@[^.]+\.\W+" emails -> printfn Got email
address: %A emails | RegexContains "\d+" numbers
-> printfn Got numbers: %A numbers | _ -> printfn
Didn't nd anything.
Partial active patterns don't constrain us to a nite set of
cases like traditional unions do, we can use as many partial active patterns in match statement as we need.

29.2 Additional Resources


Introduction to Active Patterns by Chris Smith
Extensible Pattern Matching via Lightweight Language Extension by Don Syme

91

Chapter 30

Advanced Data Structures


F# comes with its own set of data structures, however its such as a method which updates an item at a certain index.
very important to know how to implement data structures Since our nodes are immutable, we can't update our list
from scratch.
in place; we need to copy all of the nodes up to the node
we want to update.
Incidentally, hundreds of authors have written thousands
of lengthy volumes on this single topic alone, so its unrea- Setting item at index 2 to the value 9. 0 1 2 3 4 ___ ___
sonable to provide a comprehensive picture of data struc- ___ ___ ___ let x = |_1_|->|_2_|->|_3_|->|_4_|->|_5_|tures in the short amount of space available for this book. >Empty ^ / ___ ___ ___ / let y = |_1_|->|_2_|->|_9_|
Instead, this chapter is intended as a cursory introduction So, we copy all of the nodes up to index 2 and reuse the
to the development of immutable data structures using remaining nodes. A function like this is very easy to write:
F#. Readers are encouraged to use the resources listed at
the bottom of this page for a more comprehensive treat- let rec update index value s = match index, s with |
index, EmptyStack -> failwith Index out of range
ment of algorithms and data structures.
| 0, StackNode(hd, tl) -> StackNode(value, tl) | n,
StackNode(hd, tl) -> StackNode(hd, update (index - 1)
value tl)

30.1 Stacks

F#'s built-in list data structure is essentially an immutable


stack. While its certainly usable, for the purposes of writing exploratory code, we're going to implement a stack
from scratch. We can represent each node in a stack using a simple union:
type 'a stack = | EmptyStack | StackNode of 'a * 'a stack
Its easy enough to create an instance of a stack using:

Appending items from one stack to the rear of another


uses a similar technique. Since we can't modify stacks in
place, we append two stacks by copying all of the nodes
from the front stack and pointing the last copied node
to the our rear stack, resulting in the following:
Append x and y ___ ___ ___ ___ ___ let x = |_1_|->|_2_|>|_3_|->|_4_|->|_5_|->Empty ___ ___ ___ let y = |_6_|>|_7_|->|_8_|->Empty ^ / ___ ___ ___ ___ ___ / let z =
|_1_|->|_2_|->|_3_|->|_4_|->|_5_|

let stack = StackNode(1, StackNode(2, StackNode(3, We can implement this function with minimal eort using
the following:
StackNode(4, StackNode(5, EmptyStack)))))
let rec append x y = match x with | EmptyStack -> y |
Each StackNode contains a value and a pointer to the next StackNode(hd, tl) -> StackNode(hd, append tl y)
stack in the list. The resulting data structure can be diagrammed as follows:
Stacks are very easy to work with and implement. The
___ ___ ___ ___ ___ |_1_|->|_2_|->|_3_|->|_4_|->|_5_|- principles behind copying nodes to modify stacks is
fundamentally the same for all persistent data structures.
>Empty
We can create a boilerplate stack module as follows:

Complete Stack Module

module Stack = type 'a stack = | EmptyStack | StackNode


of 'a * 'a stack let hd = function | EmptyStack -> failwith
Empty stack | StackNode(hd, tl) -> hd let tl = function
| EmptyStack -> failwith Empty stack | StackNode(hd,
tl) -> tl let cons hd tl = StackNode(hd, tl) let empty =
EmptyStack

module Stack = type 'a stack = | EmptyStack | StackNode


of 'a * 'a stack let hd = function | EmptyStack -> failwith
Empty stack | StackNode(hd, tl) -> hd let tl = function
| EmptyStack -> failwith Emtpy stack | StackNode(hd,
tl) -> tl let cons hd tl = StackNode(hd, tl) let empty =
EmptyStack let rec update index value s = match index,
s with | index, EmptyStack -> failwith Index out of
range | 0, StackNode(hd, tl) -> StackNode(value, tl) | n,

Lets say we wanted to add a few methods to our stack,

92

30.2. QUEUES
StackNode(hd, tl) -> StackNode(hd, update (index - 1)
value tl) let rec append x y = match x with | EmptyStack
-> y | StackNode(hd, tl) -> StackNode(hd, append tl y)
let rec map f = function | EmptyStack -> EmptyStack
| StackNode(hd, tl) -> StackNode(f hd, map f tl) let
rec rev s = let rec loop acc = function | EmptyStack
-> acc | StackNode(hd, tl) -> loop (StackNode(hd,
acc)) tl loop EmptyStack s let rec contains x = function
| EmptyStack -> false | StackNode(hd, tl) -> hd =
x || contains x tl let rec fold f seed = function | EmptyStack -> seed | StackNode(hd, tl) -> fold f (f seed hd) tl

30.2 Queues

93
very practical in practice. We can certainly improve on
the implementation of immutable queues.

30.2.2 Queue From Two Stacks


The implementation above isn't very ecient because it
requires reversing our underlying data representation several times. Why not keep those reversed stacks around for
future use? Rather than using one stack, we can have two
stacks: a front stack f and a rear stack r.
Stack f holds items in the correct order, while stack r
holds items in reverse order; this allows the rst element
in f to be the head of the queue, and the rst element in r
to be the last item in queue. So, a queue of the numbers 1
.. 6 might be represented with f = [1;2;3] and r = [6;5;4].

To enqueue a new item, prepend it to the front of r; to dequeue an item, pop it o f. Both enqueues and dequeues
Queues aren't quite as straightforward as stacks. A naive are O(1) operations. Of course, at some point, f will be
queue can be implemented using a stack, with the caveat empty and there will be no more items to dequeue; in this
case, simply move all items from r to f and reverse the list.
that:
While the queue certainly has O(n) worst-case behavior,
it has acceptable O(1) amortized (average case) bounds.
Items are always appended to the end of the list, and
The code for this implementation is straight forward:
dequeued from the head of the stack.

30.2.1

Naive Queue

-OR- Items are prepended to the front of the stack, type Queue<'a>(f : stack<'a>, r : stack<'a>) = let check
and dequeued by reversing the stack and getting its = function | EmptyStack, r -> Queue(Stack.rev r, EmptyStack) | f, r -> Queue(f, r) member this.hd = match f
head.
with | EmptyStack -> failwith empty | StackNode(hd,
tl) -> hd member this.tl = match f, r with | EmptyStack,
(* AwesomeCollections.fsi *) type 'a stack = | EmptyS- _ -> failwith empty | StackNode(x, f), r -> check(f,
tack | StackNode of 'a * 'a stack module Stack = begin r) member this.enqueue(x) = check(f, StackNode(x,
val hd : 'a stack -> 'a val tl : 'a stack -> 'a stack val cons : r)) static member empty = Queue<'a>(Stack.empty,
'a -> 'a stack -> 'a stack val empty : 'a stack val rev : 'a Stack.empty)
stack -> 'a stack end [<Class>] type 'a Queue = member
hd : 'a member tl : 'a Queue member enqueue : 'a -> 'a
This is a simple, common, and useful implementation of
Queue static member empty : 'a Queue
(* AwesomeCollections.fs *) type 'a stack = | Emp- an immutable queue. The magic is in the check function
tyStack | StackNode of 'a * 'a stack module Stack = which maintains that f always contains items if they are
let hd = function | EmptyStack -> failwith Empty available.
stack | StackNode(hd, tl) -> hd let tl = function
Note: The queues periodic O(n) worst case
| EmptyStack -> failwith Emtpy stack | StackNbehavior can give it unpredictable response
ode(hd, tl) -> tl let cons hd tl = StackNode(hd, tl) let
times, especially in applications which rely
empty = EmptyStack let rec rev s = let rec loop acc
heavily on persistence since its possible to hit
= function | EmptyStack -> acc | StackNode(hd, tl) ->
the pathological case each time the queue is acloop (StackNode(hd, acc)) tl loop EmptyStack s type
cessed. However, this particular implementaQueue<'a>(item : stack<'a>) = member this.hd with
tion of queues is perfectly adequate for the vast
get() = Stack.hd (Stack.rev item) member this.tl with
majority of applications which do not require
get() = Queue(item |> Stack.rev |> Stack.tl |> Stack.rev)
persistence or uniform response times.
member this.enqueue(x) = Queue(StackNode(x, item))
override this.ToString() = sprintf "%A item static
member empty = Queue<'a>(Stack.empty)
As shown above, we often want to wrap our underlying
data structure in class for two reasons:
We use an interface le to hide the Queue classs con1. To simplify the interface to the data structure. For
structor. Although this technically satises the function
example, clients neither know nor care that our
of a queue, every dequeue is an O(n) operation where n
queue uses two stacks; they only know that items in
is the number of items in the queue. There are lots of
the queue obey the principle of rst-in, rst-out.
variations on the same approach, but these are often not

94

CHAPTER 30. ADVANCED DATA STRUCTURES

2. To prevent clients from putting the underlying data t.insert(x)) BinaryTree.empty


in the data structure in an invalid state.
Results in this tree:
Beyond stacks, virtually all data structures are complex
enough to require wrapping up class to hide away complex 1 / \ E 2 / \ E 3 / \ E 4 / \ E 5 / \ E 6 / \ E 7 / \ E E
details from clients.
A tree like this isn't much better than our inecient queue
implementation above! Trees are most ecient when
they have a minimum height and are as full as possible.
Ideally, we'd like to represent the tree above as follows:
30.3 Binary Search Trees
_4_/\26/\/\1357/\/\/\/\EEEEEEEE
Binary search trees are similar to stacks, but each node
The minimum height of the tree is ceiling(log n + 1),
points to two other nodes called the left and right child
where n is the number of items in the list. When we innodes:
sert items into the tree, we want the tree to balance itself
type 'a tree = | EmptyTree | TreeNode of 'a * 'a tree * 'a to maintain the minimum height. There are a variety of
tree
self-balancing tree implementations, many of which are
easy to implement as immutable data structures.
Additionally, nodes in the tree are ordered in a particular
way: each item in a tree is greater than all items in its left 30.3.1
child node and less than all items in its right child node.
Since our tree is immutable, we insert into the tree by
returning a brand new tree with the node inserted. This
process is more ecient than it sounds: we copy nodes as
we traverse down the tree, so we only copy nodes which
are in the path of our node being inserted. Writing a binary search tree is relatively straightforward:

Red Black Trees

Red-black trees are self-balancing trees which attach a


color attribute to each node in the tree. In addition
to the rules dening a binary search tree, red-black trees
must maintain the following set of rules:
1. A node is either red or black.

(* AwesomeCollections.fsi *) [<Class>] type 'a Bina2. The root node is always black.
ryTree = member hd : 'a member exists : 'a -> bool
3. No red node has a red child.
member insert : 'a -> 'a BinaryTree
(* AwesomeCollections.fs *) type 'a tree = | EmptyTree
4. Every simple path from a given node to any of its de| TreeNode of 'a * 'a tree * 'a tree module Tree =
scendant leaves contains the same number of black
let hd = function | EmptyTree -> failwith empty |
nodes.
TreeNode(hd, l, r) -> hd let rec exists item = function |
EmptyTree -> false | TreeNode(hd, l, r) -> if hd = item
then true elif item < hd then exists item l else exists item
13
r let rec insert item = function | EmptyTree -> TreeNode(item, EmptyTree, EmptyTree) | TreeNode(hd, l,
8
17
r) as node -> if hd = item then node elif item < hd
1
11
15
25
then TreeNode(hd, insert item l, r) else TreeNode(hd,
l, insert item r) type 'a BinaryTree(inner : 'a tree) =
NIL
NIL
NIL
NIL
NIL
6
22
27
member this.hd = Tree.hd inner member this.exists
item = Tree.exists item inner member this.insert item =
NIL
NIL
NIL
NIL
NIL
NIL
BinaryTree(Tree.insert item inner) member this.empty
= BinaryTree<'a>(EmptyTree)
An example of a red-black tree
We're using an interface and a wrapper class to hide the
implementation details of the tree from the user, otherwise the user could construct a tree which invalidates the
specic ordering rules used in the binary tree.
This implementation is simple and it allows us to add and
lookup any item in the tree in O(log n) best case time.
However, it suers from a pathological case: if we add
items in sorted order, or mostly sorted order, then the
tree can become heavily unbalanced. For example, the
following code:
[1 ..

We can augment our binary tree with a color eld as follows:


type color = R | B type 'a tree = | E | T of color * 'a * 'a
tree * 'a tree

When we insert into the tree, we need to rebalance the


tree to restore the rules. In particular, we need to remove
nodes with a red child. There are four cases where a red
node may have a red child. They are depicted in the diagram below by the top, right, bottom, and left trees. The
7] |> Seq.fold (fun (t : BinaryTree<_>) x -> center tree is the balanced version.

30.3. BINARY SEARCH TREES

95

B(z) / \ R(x) d / \ a R(y) / \ b c || \/ B(z) R(y) B(x) / \ / \ / type 'a tree = | Node of int * 'a tree * 'a * 'a tree (* height,
\ R(y) d => B(x) B(z) <= a R(y) / \ / \ / \ / \ R(x) c a b c left child, value, right child *) | Nil
d b R(z) / \ / \ a b c d /\ || B(x) / \ a R(z) / \ R(y) d / \ b c
We can modify our binary tree class as follows:

The height of any node is equal to max(left height, right


height) + 1. For convenience, we'll use the following con(* AwesomeCollections.fsi *) [<Class>] type 'a BinaryTree = member hd : 'a member left : 'a BinaryTree structor to create a tree node and initialize its height:
member right : 'a BinaryTree member exists : 'a -> bool let height = function | Node(h, _, _, _) -> h | Nil -> 0 let
member insert : 'a -> 'a BinaryTree member print : unit make l x r = let h = 1 + max (height l) (height r) Node(h,
-> unit static member empty : 'a BinaryTree
l, x ,r)
(* AwesomeCollections.fs *) type color = R | B type 'a
tree = | E | T of color * 'a tree * 'a * 'a tree module Tree Inserting into an AVL tree is very similar to inserting
= let hd = function | E -> failwith empty | T(c, l, x, into an unbalanced binary tree with one exception: afr) -> x let left = function | E -> failwith empty | T(c, ter we insert a node, we use a series of tree rotations to
l, x, r) -> l let right = function | E -> failwith empty | re-balance the tree. Each node has an implicit property,
T(c, l, x, r) -> r let rec exists item = function | E -> false its balance factor, which refers to the left-childs height
| T(c, l, x, r) -> if item = x then true elif item < x then minus the right-childs height; a positive balance factor
exists item l else exists item r let balance = function (* indicates the tree is weighted on the left, negative indiRed nodes in relation to black root *) | B, T(R, T(R, a, cates the tree is weighted on the right, otherwise the tree
x, b), y, c), z, d (* Left, left *) | B, T(R, a, x, T(R, b, y, is balanced.
c)), z, d (* Left, right *) | B, a, x, T(R, T(R, b, y, c), z,
d) (* Right, left *) | B, a, x, T(R, b, y, T(R, c, z, d)) (* We only need to rebalance the tree when balance factor
Right, right *) -> T(R, T(B, a, x, b), y, T(B, c, z, d)) | c, for a node is +/2. There are four scenarios which can
l, x, r -> T(c, l, x, r) let insert item tree = let rec ins = cause our tree to become unbalanced:
function | E -> T(R, E, item, E) | T(c, a, y, b) as node -> Left-left case: root balance factor = +2, left-childs balif item = y then node elif item < y then balance(c, ins a, ance factor = +1. Balanced by right-rotating the root
y, b) else balance(c, a, y, ins b) (* Forcing root node to node:
be black *) match ins tree with | E -> failwith Should
never return empty from an insert | T(_, l, x, r) -> T(B, 5 3 / \ Root / \ 3 D Right rotation 2 5 / \ -----> / \ / \ 2 C
l, x, r) let rec print (spaces : int) = function | E -> () | A B C D / \ A B
T(c, l, x, r) -> print (spaces + 4) r printfn "%s %A%A Left-right case: root balance factor = +2, right-childs
(new System.String(' ', spaces)) c x print (spaces + 4) l balance factor = 1. Balanced by left-rotating the left
type 'a BinaryTree(inner : 'a tree) = member this.hd = child, then right-rotating the root (this operation is called
Tree.hd inner member this.left = BinaryTree(Tree.left a double right rotation):
inner) member this.right = BinaryTree(Tree.right inner)
5 5 4 / \ Left child / \ Root / \ 3 D Left rotation 4 D Right
member this.exists item = Tree.exists item inner member
rotation 3 5 / \ -----> / \ -----> / \ / \ A 4 3 C A B C D / \
this.insert item = BinaryTree(Tree.insert item inner)
/\BCAB
member this.print() = Tree.print 0 inner static member
Right-right case: root balance factor = 2, right-childs
empty = BinaryTree<'a>(E)
balance factor = 1. Balanced by left-rotating the root
node:
All of the magic that makes this tree work happens in
the balance function. We're not performing any terribly 3 5 / \ Root / \ A 5 Left rotation 3 7 / \ -----> / \ / \ B 7 A
complicated transformations to the tree, yet it comes out B C D / \ C D
relatively balanced (in fact, the maximum depth of this Right-left case: root balance factor = 2, right-childs
tree is 2 * ceiling(log n + 1) ).
balance factor = +1. Balanced by right-rotating the right
child, then left-rotating the root (this operation is called a
double-left rotation):

30.3.2

AVL Trees

3 3 4 / \ Right child / \ Root / \ A 5 Right rotation A 4


Left rotation 3 5 / \ -----> / \ -----> / \ / \ 4 D B 5 A B C
AVL trees are named after its two inventors, G.M. D / \ / \ B C C D
Adelson-Velskii and E.M. Landis. These trees are self- With this in mind, its very easy to put together the rest of
balancing because the heights of the two child subtrees our AVL tree:
of any node will only dier 0 or 1; therefore, these trees
(* AwesomeCollections.fsi *) [<Class>] type 'a AvlTree
are said to be height-balanced.
= member Height : int member Left : 'a AvlTree member
An empty node in a tree has a height of 0; non-empty Right : 'a AvlTree member Value : 'a member Insert :
nodes have a height >= 1. We can store the height of 'a -> 'a AvlTree member Contains : 'a -> bool module
each node in our tree denition:

96

CHAPTER 30. ADVANCED DATA STRUCTURES

AvlTree = [<GeneralizableValue>] val empty<'a> :


AvlTree<'a>
(* AwesomeCollections.fs *) type 'a tree = | Node of
int * 'a tree * 'a * 'a tree | Nil (* Notation: h = height
x = value l = left child r = right child lh = left childs
height lx = left childs value ll = left childs left child
lr = left childs right child rh = right childs height rx =
right childs value rl = right childs left child rr = right
childs right child *) let height = function | Node(h,
_, _, _) -> h | Nil -> 0 let make l x r = let h = 1 +
max (height l) (height r) Node(h, l, x ,r) let rotRight =
function | Node(_, Node(_, ll, lx, lr), x, r) -> let r' =
make lr x r make ll lx r' | node -> node let rotLeft =
function | Node(_, l, x, Node(_, rl, rx, rr)) -> let l' =
make l x rl make l' rx rr | node -> node let doubleRotLeft
= function | Node(h, l, x, r) -> let r' = rotRight r let
node' = make l x r' rotLeft node' | node -> node let
doubleRotRight = function | Node(h, l, x, r) -> let l' =
rotLeft l let node' = make l' x r rotRight node' | node ->
node let balanceFactor = function | Nil -> 0 | Node(_,
l, _, r) -> (height l) - (height r) let balance = function
(* left unbalanced *) | Node(h, l, x, r) as node when
balanceFactor node >= 2 -> if balanceFactor l >= 1 then
rotRight node (* left left case *) else doubleRotRight
node (* left right case *) (* right unbalanced *) | Node(h,
l, x, r) as node when balanceFactor node <= 2 -> if
balanceFactor r <= 1 then rotLeft node (* right right
case *) else doubleRotLeft node (* right left case *) |
node -> node let rec insert v = function | Nil -> Node(1,
Nil, v, Nil) | Node(_, l, x, r) as node -> if v = x then node
else let l', r' = if v < x then insert v l, r else l, insert v r
let node' = make l' x r' balance <| node' let rec contains
v = function | Nil -> false | Node(_, l, x, r) -> if v = x
then true else if v < x then contains v l else contains v r
type 'a AvlTree(tree : 'a tree) = member this.Height =
height tree member this.Left = match tree with | Node(_,
l, _, _) -> new AvlTree<'a>(l) | Nil -> failwith Empty
tree member this.Right = match tree with | Node(_, _,
_, r) -> new AvlTree<'a>(r) | Nil -> failwith Empty
tree member this.Value = match tree with | Node(_,
_, x, _) -> x | Nil -> failwith Empty tree member
this.Insert(x) = new AvlTree<'a>(insert x tree) member
this.Contains(v) = contains v tree module AvlTree =
[<GeneralizableValue>] let empty<'a> : AvlTree<'a> =
new AvlTree<'a>(Nil)

Note: The [<GeneralizableValue>] attribute


indicates to F# that the construct can give
rise to generic code through type inference .
Without the attribute, F# will infer the type
of AvlTree.empty as the undened type AvlTree<'_a>, resulting in a value restriction error at compilation.
Optimization tip: The tree supports inserts
and lookups in log(n) time, where n is the number of nodes in the tree. This is already pretty

good, but we can make it faster by eliminating


unnecessary comparisons. Notice when we insert a node into the left side of the tree, we can
only add weight to the left child; however, the
balance function checks both sides of the tree
for each insert. By re-writing balance into a
balance_left and balance_right function to handle, we can handle left- and right-child inserts
separately. Similar optimizations are possible
on the red-black tree implementation as well.
An AVL trees height is limited to 1.44 * log(n), whereas
a red-black trees height is limited to 2 * log(n). The
AVL trees smaller height and more rigid balancing leads
to slower insert/removal but faster retrieval than red-black
trees. In practice, the dierence will be hardly noticeable: a lookup on a 10,000,000 node AVL tree lookup
requires at most 34 comparisons, compared to 47 comparisons on a red-black tree.

30.3.3 Heaps
Binary search trees can eciently nd arbitrary elements
in a set, however it can be occasionally useful to access the
minimum element in set. Heaps are special data structure
which satisfy the heap property: the value of every node
is greater than the value of any of its child nodes. Additionally, we can keep the tree approximately balanced
using the leftist property, meaning that the height of any
left child heap is at least as large as its right sibling. We
can hold the height of each tree in each heap node.
Finally, since heaps can be implemented as min- or maxheaps, where the root element will either be the largest or
smallest element in the set, we support both types of heaps
by passing in an ordering function into heaps constructor
as such:
type 'a heap = | EmptyHeap | HeapNode of int * 'a * 'a
heap * 'a heap type 'a BinaryHeap(comparer : 'a -> 'a ->
int, inner : 'a heap) = static member make(comparer) =
BinaryHeap<_>(comparer, EmptyHeap)

Note: the functionality we gain by passing


the comparer function into the BinaryHeap
constructor approximates OCaml functors, although its not quite as elegant.
An interesting consequence of the leftist property is that
elements along any path in a heap are stored in sorted order. This means we can merge any two heaps by merging
their right spines and swapping children as necessary to
restore the leftist property. Since each right spine contains at least as many nodes as the left spine, the height
of each right spine is proportional to the logarithm of the
number of elements in the heap, so merging two heaps
can be performed in O(log n) time. We can implement
all of the properties of our heap as follows:

30.5. ADDITIONAL RESOURCES


(* AwesomeCollections.fsi *) [<Class>] type 'a
BinaryHeap = member hd : 'a member tl : 'a BinaryHeap member insert : 'a -> 'a BinaryHeap
member merge : 'a BinaryHeap -> 'a BinaryHeap
interface System.Collections.IEnumerable interface
System.Collections.Generic.IEnumerable<'a>
static
member make : ('b -> 'b -> int) -> 'b BinaryHeap
(* AwesomeCollections.fs *) type 'a heap = | EmptyHeap
| HeapNode of int * 'a * 'a heap * 'a heap module Heap =
let height = function | EmptyHeap -> 0 | HeapNode(h, _,
_, _) -> h (* Helper function to restore the leftist property
*) let makeT (x, a, b) = if height a >= height b then
HeapNode(height b + 1, x, a, b) else HeapNode(height
a + 1, x, b, a) let rec merge comparer = function | x,
EmptyHeap -> x | EmptyHeap, x -> x | (HeapNode(_,
x, l1, r1) as h1), (HeapNode(_, y, l2, r2) as h2) -> if
comparer x y <= 0 then makeT(x, l1, merge comparer
(r1, h2)) else makeT (y, l2, merge comparer (h1, r2))
let hd = function | EmptyHeap -> failwith empty |
HeapNode(h, x, l, r) -> x let tl comparer = function |
EmptyHeap -> failwith empty | HeapNode(h, x, l,
r) -> merge comparer (l, r) let rec to_seq comparer =
function | EmptyHeap -> Seq.empty | HeapNode(h, x,
l, r) as node -> seq { yield x; yield! to_seq comparer
(tl comparer node) } type 'a BinaryHeap(comparer :
'a -> 'a -> int, inner : 'a heap) = (* private *) member this.inner = inner (* public *) member this.hd =
Heap.hd inner member this.tl = BinaryHeap(comparer,
Heap.tl comparer inner) member this.merge (other :
BinaryHeap<_>) = BinaryHeap(comparer, Heap.merge
comparer (inner, other.inner)) member this.insert
x = BinaryHeap(comparer, Heap.merge comparer
(inner,(HeapNode(1, x, EmptyHeap, EmptyHeap))))
interface System.Collections.Generic.IEnumerable<'a>
with member this.GetEnumerator() = (Heap.to_seq
comparer inner).GetEnumerator() interface System.Collections.IEnumerable
with
member
this.GetEnumerator() = (Heap.to_seq comparer inner :>
System.Collections.IEnumerable).GetEnumerator()
static
member
make(comparer)
=
BinaryHeap<_>(comparer, EmptyHeap)

97
laziness in ways which make purely functional data structures just as ecient as their imperative counterparts.
For example, its easy to create a stack-like data structure
which delays all computation until its really needed:
type 'a lazyStack = | Node of Lazy<'a * 'a lazyStack> |
EmptyStack module LazyStack = let (|Cons|Nil|) = function | Node(item) -> let hd, tl = item.Force() Cons(hd, tl)
| EmptyStack -> Nil let hd = function | Cons(hd, tl) -> hd |
Nil -> failwith empty let tl = function | Cons(hd, tl) -> tl
| Nil -> failwith empty let cons(hd, tl) = Node(lazy(hd,
tl)) let empty = EmptyStack let rec append x y = match
x with | Cons(hd, tl) -> Node(lazy(printfn appending...
got %A hd; hd, append tl y)) | Nil -> y let rec iter f =
function | Cons(hd, tl) -> f(hd); iter f tl | Nil -> ()
In the example above, the append operation returns one
node delays the rest of the computation, so appending
two lists will occur in constant time. A printfn statement
above has been added to demonstrate that we really don't
compute appended values until the rst time they're accessed:
> open LazyStack;; > let x = cons(1, cons(2, cons(3,
cons(4, EmptyStack))));; val x : int lazyStack =
Node <unevaluated> > let y = cons(5, cons(6, cons(7,
EmptyStack)));; val y : int lazyStack = Node <unevaluated> > let z = append x y;; val z : int lazyStack
= Node <unevaluated> > hd z;; appending... got 1
val it : int = 1 > hd (tl (tl z) );; appending... got 2
appending... got 3 val it : int = 3 > iter (fun x -> printfn
"%i x) z;; 1 2 3 appending... got 4 4 5 6 7 val it : unit = ()
Interestingly, the append method clearly runs in O(1) time
because the actual appending operation is delayed until a
user grabs the head of the list. At the same time, grabbing
the head of the list may have the side eect of triggering,
at most, one call to the append method without causing
a monolithic rebuilding the rest of the data structure, so
grabbing the head is itself an O(1) operation. This stack
implementation supports supports constant-time consing
and appending, and linear time lookups.

This heap implements the IEnumerable<'a> interface, al- Similarly, implementations of lazy queues exists which
lowing us to iterate through it like a seq. In addition to support O(1) worst-case behavior for all operations.
the leftist heap shown above, its very easy to implement
immutable versions of splay heaps, binomial heaps, Fibonacci heaps, pairing heaps, and a variety other tree-like
30.5 Additional Resources
data structures in F#.

30.4 Lazy Data Structures

Purely Functional Data Structures by Chris Okasaki.


Highly recommended. Provides techniques and
analysis of immutable data structures using SML.

Its worth noting that some purely functional data structures above are not as ecient as their imperative implementations. For example, appending two immutable
stacks x and y together takes O(n) time, where n is the
number of elements in stack x. However, we can exploit

The Algorithm Design Manual by Steven S. Skiena.


Highly recommended. Provides language-agnostic
description of a variety of algorithms, data structures, and techniques for solving hard problems in
computer science.

98

CHAPTER 30. ADVANCED DATA STRUCTURES

Tutorial on immutable data structures using C#:


1. Kinds of Immutability
2. A Simple Immutable Stack
3. A Covariant Immutable Stack
4. An Immutable Queue
5. LOLZ!
6. A Simple Binary Tree
7. More on Binary Trees
8. Even More on Binary Trees
9. AVL Tree Implementation
10. A Double-ended Queue
11. A Working Double-ended Queue

Chapter 31

Reection
Reection allows programmers to inspect types and in- Version=2.0.0.0,
Culture=neutral,
PublicKeyTovoke methods of objects at runtime without knowing their ken=b77a5c561934e089"; Attributes = AutoLayout,
data type at compile time.
AnsiClass, Class, Public, Abstract, Sealed, BeforeFielAt rst glance, reection seems to go against the spirit dInit; BaseType = System.Object; ContainsGenericPaof ML as it is inherently not type-safe, so typing errors rameters = false; DeclaringMethod = ?; DeclaringType
using reection are not discovered until runtime. How- = null; FullName = System.IO.File"; ...
ever, .NETs typing philosophy is best stated as static typing where possible, dynamic typing when needed, where object.GetType and typeof return an instance of
reection serves to bring in the most desirable behaviors System.Type, which has a variety of useful properties
of dynamic typing into the static typing world. In fact, such as:
dynamic typing can be a huge time saver, often promotes
the design of more expressive APIs, and allows code to be
val Name : string
refactored much further than possible with static typing.
This section is intended as a cursory overview of reection, not a comprehensive tutorial.

Returns the name of the type.


val GetConstructors : unit -> ConstructorInfo
array

31.1 Inspecting Types

Returns an array of constructors dened on the


type.

There are a variety of ways to inspect the type of an


object. The most direct way is calling the .GetType()
method (inherited from System.Object) on any non-null
object:
> hello world.GetType();; val it : System.Type =
System.String {Assembly = mscorlib, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089;
AssemblyQualiedName = System.String, mscorlib,
Version=2.0.0.0,
Culture=neutral,
PublicKeyToken=b77a5c561934e089"; Attributes = AutoLayout,
AnsiClass, Class, Public, Sealed, Serializable, BeforeFieldInit; BaseType = System.Object; ContainsGenericParameters = false; DeclaringMethod = ?;
DeclaringType = null; FullName = System.String";
GUID = 296afb-1b0b-35-9d6c-4e7e599f8b57;
GenericParameterAttributes = ?; GenericParameterPosition = ?; ...

val GetMembers : unit -> MemberInfo array


Returns an array of members dened on the
type.
val InvokeMember : (name : string, invokeAttr :
BindingFlags, binder : Binder, target : obj, args
: obj) -> obj
Invokes the specied member, using the specied binding constraints and matching the specied argument list

31.1.1 Example: Reading Properties

Its also possible to get type information without an actual The following program will print out the properties of any
object passed into it:
object using the built-in typeof method:
> typeof<System.IO.File>;; val it : System.Type =
System.IO.File {Assembly = mscorlib, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089;
AssemblyQualiedName = System.IO.File, mscorlib,

type Car(make : string, model : string, year : int) =


member this.Make = make member this.Model = model
member this.Year = year member this.WheelCount =
4 type Cat() = let mutable age = 3 let mutable name

99

100

CHAPTER 31. REFLECTION

= System.String.Empty member this.Purr() = printfn


Purrr member this.Age with get() = age and set(v) =
age <- v member this.Name with get() = name and set(v)
= name <- v let printProperties x = let t = x.GetType() let
properties = t.GetProperties() printfn "-----------" printfn
"%s t.FullName properties |> Array.iter (fun prop -> if
prop.CanRead then let value = prop.GetValue(x, null)
printfn "%s: %O prop.Name value else printfn "%s: ?"
prop.Name) let carInstance = new Car(Ford, Focus,
2009) let catInstance = let temp = new Cat() temp.Name
<- Mittens temp printProperties carInstance printProperties catInstance

ticle on MSDN for more information.

31.2 Microsoft.FSharp.Reection
Namespace
While .NETs built-in reection API is useful, the
F# compiler performs a lot of magic which makes
built-in types like unions, tuples, functions, and other
built-in types appear strange using vanilla reection.
The Microsoft.FSharp.Reection namespace provides a
wrapper for exploring F# types.

This program outputs the following:

open
System.Reection
open
Mi----------- Program+Car WheelCount: 4 Year: 2009 crosoft.FSharp.Reection let explore x = let t =
Model: Focus Make: Ford ----------- Program+Cat x.GetType() if FSharpType.IsTuple(t) then let elds =
Name: Mittens Age: 3
FSharpValue.GetTupleFields(x) |> Array.map string |>
fun strings -> System.String.Join(", ", strings) printfn
Tuple: (%s)" elds elif FSharpType.IsUnion(t) then let
31.1.2 Example: Setting Private Fields
union, elds = FSharpValue.GetUnionFields(x, t) printfn
Union: %s(%A)" union.Name elds else printfn Got
In addition to discovering types, we can dynamically in- another type
voke methods and set properties:
let dynamicSet x propName propValue = let prop- Using fsi:
erty = x.GetType().GetProperty(propName) prop> explore (Some(Hello world));; Union: Some([|"Hello
erty.SetValue(x, propValue, null)
world"|]) val it : unit = () > explore (7, Hello world);;
Tuple: (7, Hello world) val it : unit = () > explore
Reection is particularly remarkable in that it can (Some(Hello world));; Union: Some([|"Hello world"|])
read/write private elds, even on objects which appear val it : unit = () > explore [1;2;3;4];; Union: Cons([|1;
to be immutable. In particular, we can explore and ma- [2; 3; 4]|]) val it : unit = () > explore Hello world";; Got
nipulate the underlying properties of an F# list:
another type
> open System.Reection let x = [1;2;3;4;5] let
lastNode = x.Tail.Tail.Tail.Tail;; val x : int list =
[1; 2; 3; 4; 5] val lastNode : int list = [5] > lastNode.GetType().GetFields(BindingFlags.NonPublic
31.3 Working With Attributes
||| BindingFlags.Instance) |> Array.map (fun eld ->
eld.Name);; val it : string array = [|"__Head"; "__Tail"|]
> let tailField = lastNode.GetType().GetField("__Tail, .NET attributes and reection go hand-in-hand. AtBindingFlags.NonPublic ||| BindingFlags.Instance);; tributes allow programmers to decorate classes, methods,
val
tailField
:
FieldInfo
=
Mi- members, and other source code with metadata used at
crosoft.FSharp.Collections.FSharpList`1[System.Int32] runtime. Many .NET classes use attributes to annotate
__Tail > tailField.SetValue(lastNode, x);; (* circular list code in a variety of ways; it is only possible to access
*) val it : unit = () > x |> Seq.take 20 |> Seq.to_list;; val and interpret attributes through reection. This section
it : int list = [1; 2; 3; 4; 5; 1; 2; 3; 4; 5; 1; 2; 3; 4; 5; 1; 2; will provide a brief overview of attributes. Readers interested in a more complete overview are encouraged to read
3; 4; 5]
MSDNs Extending Metadata With Attributes series.
The example above mutates the list in place and to produce a circularly linked list. In .NET, immutable
doesn't really mean immutable and private members are
mostly an illusion.
Note: The power of reection has denite security implications, but a full discussion of reection security is far outside of the scope of
this section. Readers are encouraged to visit
the Security Considerations for Reection ar-

Attributes are dened using [<AttributeName>], a notation already seen in a variety of places in previous chapters of this book. The .NET framework includes a number of built-in attributes, including:
System.ObsoleteAttribute - used to mark source
code intended to be removed in future versions.
System.FlagsAttribute - indicates that an enumeration can be treated as a bit eld.

31.3. WORKING WITH ATTRIBUTES

101

System.SerializableAttribute - indicates that class newInstance() [<ConstructionAttribute(true)>] type


can be serialized.
SingleOnly() = do printfn SingleOnly contructor [<ConstructionAttribute(false)>] type NewAl System.Diagnostics.DebuggerStepThroughAttribute ways() = do printfn NewAlways constructor let x =
- indicates that the debugger should not step into a make<SingleOnly>() let x' = make<SingleOnly>() let y
method unless it contains a break point.
= make<NewAlways>() let y' = make<NewAlways>()
printfn x = x': %b (x = x') printfn y = y': %b (y = y')
We can create custom attributes by dening a new type System.Console.ReadKey(true) |> ignore
which inherits from System.Attribute:
type MyAttribute(text :
string) = inherit System.Attribute() do printfn MyAttribute created. Text:
%s text member this.Text = text [<MyAttribute(Hello
world)>] type MyClass() = member this.SomeProperty
= This is a property
We can access attribute using reection:
> let x = new MyClass();; val x : MyClass >
x.GetType().GetCustomAttributes(true);;
MyAttribute created.
Text:
Hello world val
it :
obj [] = [|System.SerializableAttribute
{TypeId
=
System.SerializableAttribute;};
FSI_0028+MyAttribute {Text = Hello world";
TypeId
=
FSI_0028+MyAttribute;};
Microsoft.FSharp.Core.CompilationMappingAttribute
{SequenceNumber
=
0;
SourceConstructFlags
=
ObjectType;
TypeId
=
Microsoft.FSharp.Core.CompilationMappingAttribute;
VariantNumber = 0;}|]

This program outputs the following:


SingleOnly constructor NewAlways constructor NewAlways constructor x = x': true y = y': false
Using the attribute above, we've completely abstracted
away the implementation details of the singleton design
pattern, reducing it down to a single attribute. Its worth
noting that the program above hard-codes a value of true
or false into the attribute constructor; if we wanted to, we
could pass a string representing a key from the applications cong le and make class construction dependent
on the cong le.

The MyAttribute class has the side-eect of printing to


the console on instantiation, demonstrating that MyAttribute does not get constructed when instances of MyClass are created.

31.3.1

Example: Encapsulating Singleton


Design Pattern

Attributes are often used to decorate classes with any kind


of ad-hoc functionality. For example, lets say we wanted
to control whether single or multiple instances of classes
are created based on an attribute:
type ConstructionAttribute(singleInstance : bool) =
inherit System.Attribute() member this.IsSingleton
= singleInstance let singletons = new System.Collections.Generic.Dictionary<System.Type,obj>()
let make() :
'a = let newInstance() = System.Activator.CreateInstance<'a>() let attributes =
typeof<'a>.GetCustomAttributes(typeof<ConstructionAttribute>,
true) let singleInstance = if attributes.Length > 0 then
let contructionAttribute = attributes.[0] :?> ConstructionAttribute
contructionAttribute.IsSingleton
else false if singleInstance then match singletons.TryGetValue(typeof<'a>) with | true, v ->
v :?> 'a | _ -> let instance = newInstance() singletons.Add(typeof<'a>,
instance) instance else

Chapter 32

Computation Expressions
Computation expressions are easily the most dicult, let read_line() = System.Console.ReadLine() let
yet most powerful language constructs to understand in print_string(s) = printf "%s s print_string Whats your
F#.
name? " let name = read_line() print_string (Hello, " +
name)

32.1 Monad Primer

We can re-write the read_line and print_string functions


to take an extra parameter, namely a function to exeComputation expressions are inspired by Haskell monads, cute once our computation completes. Wed end up with
which in turn are inspired by the mathematical concept of something that looks more like this:
a monads in category theory. To avoid all of the abstract let read_line(f) = f(System.Console.ReadLine()) let
technical and mathematical theory underlying monads, a print_string(s, f) = f(printf "%s s) print_string(Whats
monad is, in very simple terms, a scary sounding word your name? ", fun () -> read_line(fun name ->
which means execute this function and pass its return value print_string(Hello, " + name, fun () -> () ) ) )
to this other function.
Note: The designers of F# use term computation expression and workow because
its less obscure and daunting than the word
monad. The author of this book prefers
monad to emphasize the parallel between the
F# and Haskell (and, strictly as an aside, its just
a neat sounding ve-dollar word).

If you can understand this much, then you can understand


any monad.

Of course, its perfectly reasonable to say what masochistic reason would anyone have for writing code like that?
All it does it print out Hello, Steve to the console! After all, C#, Java, C++, or other languages we know and
love execute code in exactly the order specied -- in other
words, monads solve a problem in Haskell which simply
Monads in Haskell
doesn't exist in imperative languages. Consequently, the
Haskell is interesting because its a functional program- monad design pattern is virtually unknown in imperative
ming language where all statements are executed lazily, languages.
meaning Haskell doesnt compute values until they are ac- However, monads are occasionally useful for modeling
tually needed. While this gives Haskell some unique fea- computations which are dicult to capture in an impertures such as the capacity to dene innite data struc- ative style.
tures, it also makes it hard to reason about the execution
The Maybe Monad
of programs since you can't guarantee that lines of code
A well-known monad, the Maybe monad, represents a
will be executed in any particular order (if at all).
short-circuited computation which should bail out if
Consequently, its quite a challenge to do things which
any part of the computation fails. Using a simple examneed to be executed in a sequence, which includes any
ple, lets say we wanted to write a function which asks the
form of I/O, acquiring locks objects in multithreaded
user for 3 integer inputs between 0 and 100 (inclusive)
code, reading/writing to sockets, and any conceivable ac-- if at any point, the user enters an input which is nontion which has a side-eect on any memory elsewhere in
numeric or falls out of our range, the entire computation
our application. Haskell manages sequential operations
should be aborted. Traditionally, we might represent this
using something called a monad, which can be used to
kind of program using the following:
simulate state in an immutable environment.
let addThreeNumbers() = let getNum msg = printf "%s
Visualizing Monads with F#
msg // NOTE: return values from .Net methods that acTo visualize monads, lets take some everyday F# code cept 'out' parameters are exposed to F# as tuples. match
written in imperative style:
102

32.2. DEFINING COMPUTATION EXPRESSIONS


System.Int32.TryParse(System.Console.ReadLine())
with | (true, n) when n >= 0 && n <= 100 -> Some(n) | _
-> None match getNum "#1: " with | Some(x) -> match
getNum "#2: " with | Some(y) -> match getNum "#3:
" with | Some(z) -> Some(x + y + z) | None -> None |
None -> None | None -> None
Note: Admittedly, the simplicity of this program -- grabbing a few integers -- is ridiculous,
and there are many more concise ways to write
this code by grabbing all of the values up front.
However, it might help to imagine that getNum
was a relatively expensive operation (maybe it
executes a query against a database, sends and
receives data over a network, initializes a complex data structure), and the most ecient way
to write this program requires us to bail out as
soon as we encounter the rst invalid value.
This code is very ugly and redundant. However, we can
simplify this code by converting it to monadic style:
let addThreeNumbers() = let bind(input, rest) = match
System.Int32.TryParse(input()) with | (true, n) when n >=
0 && n <= 100 -> rest(n) | _ -> None let createMsg msg
= fun () -> printf "%s msg; System.Console.ReadLine()
bind(createMsg "#1: ", fun x -> bind(createMsg "#2: ",
fun y -> bind(createMsg "#3: ", fun z -> Some(x + y +
z) ) ) )
The magic is in the bind method. We extract the return
value from our function input and pass it (or bind it) as
the rst parameter to rest.
Why use monads?
The code above is still quite extravagant and verbose for
practical use, however monads are especially useful for
modeling calculations which are dicult to capture sequentially. Multithreaded code, for example, is notoriously resistant to eorts to write in an imperative style;
however it becomes remarkably concise and easy to write
in monadic style. Lets modify our bind method above as
follows:

103
url msg bind( (fun () -> printMsg Creating webclient..."; new System.Net.WebClient()), fun webclient
-> bind( (fun () -> printMsg Downloading url...";
webclient.DownloadString(url)), fun html -> bind( (fun
() -> printMsg Extracting urls..."; Regex.Matches(html,
@"http://\char"005C\relax{}S+") ), fun matches > printMsg (Found " + matches.Count.ToString()
+ " links) ) ) ) ["http://www.google.com/";
"http://microsoft.com/"; "http://www.wordpress.com/";
"http://www.peta.org"] |> Seq.iter downloadAsync;;
val bind : (unit -> 'a) * ('a -> unit) -> unit val downloadAsync : string -> unit > ThreadID = 5, Url =
http://www.google.com/, Creating webclient... ThreadID = 11, Url = http://microsoft.com/, Creating webclient... ThreadID = 5, Url = http://www.peta.org,
Creating webclient...
ThreadID = 11, Url =
http://www.wordpress.com/,
Creating webclient...
ThreadID = 5, Url = http://microsoft.com/, Downloading url... ThreadID = 11, Url = http://www.google.com/,
Downloading url...
ThreadID = 11, Url =
http://www.peta.org, Downloading url... ThreadID = 13,
Url = http://www.wordpress.com/, Downloading url...
ThreadID = 11, Url = http://www.google.com/, Extracting urls... ThreadID = 11, Url = http://www.google.com/,
Found 21 links ThreadID = 11, Url = http:
//www.peta.org, Extracting urls... ThreadID = 11, Url =
http://www.peta.org, Found 111 links ThreadID = 5, Url
= http://microsoft.com/, Extracting urls... ThreadID = 5,
Url = http://microsoft.com/, Found 1 links ThreadID =
13, Url = http://www.wordpress.com/, Extracting urls...
ThreadID = 13, Url = http://www.wordpress.com/,
Found 132 links
Its interesting to notice that Google starts downloading on
thread 5 and nishes on thread 11. Additionally, thread
11 is shared between Microsoft, Peta, and Google at some
point. Each time we call bind, we pull a thread out of
.NETs threadpool, when the function returns the thread
is released back to the threadpool where another thread
might pick it up again -- its wholly possible for async functions to hop between any number of threads throughout
their lifetime.

This technique is so powerful that its baked into F# liopen System.Threading let bind(input, rest) = Thread- brary in the form of the async workow.
Pool.QueueUserWorkItem(new WaitCallback( fun _ ->
rest(input()) )) |> ignore

32.2 Dening Computation Ex-

Now our bind method will execute a function in its own


pressions
thread. Using monads, we can write multithreaded code
in a safe, imperative style. Heres an example in fsi
demonstrating this technique:
Computation expressions are fundamentally the same
>
open
System.Threading
open
Sys- concept as seen above, although they hide the complexity
tem.Text.RegularExpressions let bind(input, rest) = of monadic syntax behind a thick layer of syntactic sugar.
ThreadPool.QueueUserWorkItem(new WaitCallback( A monad is a special kind of class which must have the
fun _ -> rest(input()) )) |> ignore let downloadAsync (url : following methods: Bind, Delay, and Return.
string) = let printMsg msg = printfn ThreadID = %i, Url We can rewrite our Maybe monad described earlier as
= %s, %s (Thread.CurrentThread.ManagedThreadId) follows:

104

CHAPTER 32. COMPUTATION EXPRESSIONS

type MaybeBuilder() = member this.Bind(x, f) = match


x with | Some(x) when x >= 0 && x <= 100 -> f(x) | _ ->
None member this.Delay(f) = f() member this.Return(x)
= Some x
We can test this class in fsi:
> type MaybeBuilder() = member this.Bind(x, f) =
printfn this.Bind: %A x match x with | Some(x) when
x >= 0 && x <= 100 -> f(x) | _ -> None member
this.Delay(f) = f() member this.Return(x) = Some x let
maybe = MaybeBuilder();; type MaybeBuilder = class
new : unit -> MaybeBuilder member Bind : x:int option *
f:(int -> 'a0 option) -> 'a0 option member Delay : f:(unit
-> 'a0) -> 'a0 member Return : x:'a0 -> 'a0 option end val
maybe : MaybeBuilder > maybe.Delay(fun () -> let x =
12 maybe.Bind(Some 11, fun y -> maybe.Bind(Some 30,
fun z -> maybe.Return(x + y + z) ) ) );; this.Bind: Some
11 this.Bind: Some 30 val it : int option = Some 53 >
maybe.Delay(fun () -> let x = 12 maybe.Bind(Some 50,
fun y -> maybe.Bind(Some 30, fun z -> maybe.Return(x
+ y + z) ) ) );; this.Bind: Some 50 val it : int option =
None

> type MaybeBuilder() = member this.Bind(x, f) =


printfn this.Bind: %A x match x with | Some(x) when
x >= 0 && x <= 100 -> f(x) | _ -> None member
this.Delay(f) = f() member this.Return(x) = Some x let
maybe = MaybeBuilder();; type MaybeBuilder = class
new : unit -> MaybeBuilder member Bind : x:int option
* f:(int -> 'a0 option) -> 'a0 option member Delay :
f:(unit -> 'a0) -> 'a0 member Return : x:'a0 -> 'a0 option
end val maybe : MaybeBuilder > maybe { let x = 12
let! y = Some 11 let! z = Some 30 return x + y + z };;
this.Bind: Some 11 this.Bind: Some 30 val it : int option
= Some 53 > maybe { let x = 12 let! y = Some 50 let!
z = Some 30 return x + y + z };; this.Bind: Some 50
val it : int option = None
This code does the same thing as the desugared code, only
its much much easier to read.

32.2.2 Dissecting Syntax Sugar


According the F# spec, workows may be dened with
the following members:
These sugared constructs are de-sugared as follows:

32.2.1

Syntax Sugar

32.3 What are Computation Ex-

Monads are powerful, but beyond two or three variables,


pressions Used For?
the number of nested functions becomes cumbersome to
work with. F# provides syntactic sugar which allows us
to write the same code in a more readable fashion. Work- F# encourages of a programming style called language
ows are evaluated using the form builder { comp-expr }. oriented programming to solve problems. In contrast to
For example, the following pieces of code are equivalent: general purpose programming style, language oriented
programming centers around the programmers identifying problems they want to solve, then writing domain speNote: You probably noticed that the sugared
cic mini-languages to tackle the problem, and nally
syntax is strikingly similar to the syntax used
solve problem in the new mini-language.
to declare sequence expressions, seq { expr }.
Computation expressions are one of several tools F#
This is not a coincidence. In the F# library,
programmers have at their disposal for designing minisequences are dened as computation expreslanguages. Its surprising how often computation expressions and used as such. The async workow
sions and monad-like constructs occur in practice. For
is another computation expression you'll enexample, the Haskell User Group has a collection of comcounter while learning F#.
mon and uncommon monads, including those which compute distributions of integers and parse text. Another
The sugared form reads like normal F#. The code let x = signicant example, an F# implementation of software
12 behaves as expected, but what is let! doing? Notice transactional memory, is introduced on hubFS.
that we say let! y = Some 11, but the value y has the
type int rather than int option. The construct let! y =
... invokes a function called maybe.Bind(x, f), where the
32.4 Additional Resources
value y is bound to parameter passed into the f function.
Similarly, return ...
invokes a function called
maybe.Return(x).
Several new keywords de-sugar
to some other construct, including ones you've already
seen in sequence expressions like yield and yield!, as well
as new ones like use and use!.
This fsi sample shows how easy it is to use our maybe
monad with computation expression syntax:

Haskell.org: All About Monads - Another collection


of monads in Haskell.

Chapter 33

Async Workows
Async workows allow programmers to convert singlethreaded code into multi-threaded code with minimal
code changes.

a subsidiary of the AsyncGroup of the outer


computations.
member Start : computation:Async<unit> -> unit

33.1 Dening Async Workows

Start the asynchronous computation in the


thread pool. Do not await its result. Run as
part of the default AsyncGroup

Async workows are dened using computation expression notation:

Async.RunSynchronously is used to run async<'a> blocks


and wait for them to return, Run.Parallel automatically
Heres an example using fsi:
runs each async<'a> on as many processors as the CPU
> let asyncAdd x y = async { return x + y };; val asyncAdd has, and Async.Start runs without waiting for the operation to complete. To use the canonical example, down: int -> int -> Async<int>
loading a web page, we can write code for downloading a
web page asyncronously as follows in fsi:
Notice the return type of asyncAdd. It does not actually
run a function; instead, it returns an async<int>, which is > let extractLinks url = async { let webClient = new
System.Net.WebClient() printfn Downloading %s
a special kind of wrapper around our function.
url let html = webClient.DownloadString(url : string)
printfn Got %i bytes html.Length let matches =
System.Text.RegularExpressions.Regex.Matches(html,
33.1.1 The Async Module
@"http://\char"005C\relax{}S+") printfn Got %i
The Async Module is used for operating on async<'a> links matches.Count return url, matches.Count
objects. It contains several useful methods, the most im- };; val extractLinks : string -> Async<string *
int>
>
Async.RunSynchronously
(extractLinks
portant of which are:
"http://www.msn.com/");;
Downloading
http:
member RunSynchronously :
computation:
//www.msn.com/ Got 50742 bytes Got 260 links
Async<'T> * ?timeout:int -> 'T
val it : string * int = ("http://www.msn.com/", 260)
async { comp-exprs }

Run the asynchronous computation and await


its result. If an exception occurs in the asynchronous computation then an exception is reraised by this function. Run as part of the default AsyncGroup.

33.1.2 async<'a> Members

member
Parallel
:
computationList:
seq<Async<'T>> -> Async<'T array>
Specify an asynchronous computation that,
when run, executes all the given asynchronous
computations, initially queueing each in the
thread pool. If any raise an exception then
the overall computation will raise an exception,
and attempt to cancel the others. All the subcomputations belong to an AsyncGroup that is

async<'a> objects are constructed from the


AsyncBuilder, which has the following important
members:
member Bind : p:Async<'a> * f:('a -> Async<'b>)
-> Async<'b> / let!
Specify an asynchronous computation that,
when run, runs 'p', and when 'p' generates a result 'res, runs 'f res.
member Return : v:'a -> Async<'a> / return

105

106

CHAPTER 33. ASYNC WORKFLOWS


Specify an asynchronous computation that,
when run, returns the result 'v'

Parallel mapping can have a dramatic impact on the speed


of map operations. We can compare serial and parallel
mapping directly using the following:

In other words, let! executes an async workow and binds


its return value to an identier, return simply returns a result, and return! executes an async workow and returns
its return value as a result.

open System.Text.RegularExpressions open System.Net let download url = let webclient = new
System.Net.WebClient() webclient.DownloadString(url
: string) let extractLinks html = Regex.Matches(html,
@"http://\char"005C\relax{}S+") let downloadAndExtractLinks url = let links = (url |> download |>
extractLinks) url, links.Count let urls = [@"http:
//www.craigslist.com/";
@"http://www.msn.com/";
@"http://en.wikibooks.org/wiki/Main_Page"; @"http:
//www.wordpress.com/"; @"http://news.google.com/";]
let pmap f l = seq { for a in l -> async { return f
a } } |> Async.Parallel |> Async.Run let testSynchronous() = List.map downloadAndExtractLinks
urls let testAsynchronous() = pmap downloadAndExtractLinks urls let time msg f = let stopwatch =
System.Diagnostics.Stopwatch.StartNew() let temp =
f() stopwatch.Stop() printfn "(%f ms) %s: %A stopwatch.Elapsed.TotalMilliseconds msg temp let main() =
printfn Start... time Synchronous testSynchronous
time Asynchronous testAsynchronous printfn Done.
main()

These primitives allow us to compose async blocks within


one another. For example, we can improve on the code
above by downloading a web page asyncronously and extracting its urls asyncronously as well:
let extractLinksAsync html = async { return System.Text.RegularExpressions.Regex.Matches(html,
@"http://\char"005C\relax{}S+")
}
let
downloadAndExtractLinks url = async { let webClient
= new System.Net.WebClient() let html = webClient.DownloadString(url : string) let!
links =
extractLinksAsync html return url, links.Count }
Notice that let! takes an async<'a> and binds its return
value to an identier of type 'a. We can test this code in
fsi:

> let links = downloadAndExtractLinks "http:


//www.wordpress.com/";; val links : Async<string
* int> > Async.Run links;; val it : string * int = This program has the following types:
("http://www.wordpress.com/", 132)
val download : string -> string val extractLinks : string
-> MatchCollection val downloadAndExtractLinks :
string -> string * int val urls : string list val pmap : ('a ->
What does let! do?
'b) -> seq<'a> -> 'b array val testSynchronous : unit ->
let! runs an async<'a> object on its own thread, then (string * int) list val testAsynchronous : unit -> (string *
it immediately releases the current thread back to the int) array val time : string -> (unit -> 'a) -> unit val main
threadpool. When let! returns, execution of the workow : unit -> unit
will continue on the new thread, which may or may not
be the same thread that the workow started out on. As a
result, async workows tend to hop between threads, an This program outputs the following:
interesting eect demonstrated explicitly here, but this is Start...
(4276.190900 ms) Synchronous: [("http:
not generally regarded as a bad thing.
//www.craigslist.com/", 185); ("http://www.msn.com/",
262);
("http://en.wikibooks.org/wiki/Main_Page",
190);
("http://www.wordpress.com/",
132);
("http://news.google.com/",
296)]
(1939.117900
33.2 Async Extensions Methods
ms) Asynchronous:
[|("http://www.craigslist.com/",
185);
("http://www.msn.com/",
261);
("http:
//en.wikibooks.org/wiki/Main_Page",
190);
33.3 Async Examples
("http://www.wordpress.com/",
132);
("http:
//news.google.com/", 294)|] Done.

33.3.1

Parallel Map

Consider the function Seq.map. This function is synchronous, however there is no real reason why it needs
to be synchronous, since each element can be mapped in
parallel (provided we're not sharing any mutable state).
Using a module extension, we can write a parallel version
of Seq.map with minimal eort:
module Seq = let pmap f l = seq { for a in l -> async {
return f a } } |> Async.Parallel |> Async.Run

The code using pmap ran about 2.2x faster because web
pages are downloaded in parallel, rather than serially.

33.4 Concurrency with Functional


Programming

33.4. CONCURRENCY WITH FUNCTIONAL PROGRAMMING

33.4.1

Why Concurrency Matters

For the rst 50 years of software development, programmers could take comfort in the fact that computer hardware roughly doubled in power every 18 months. If a
program was slow today, one could just wait a few months
and the program would run at double the speed with no
change to the source code. This trend continued well into
the early 2000s, where commodity desktop machines in
2003 had more processing power than the fastest supercomputers in 1993. However, after the publication of a
well-known article, The Free Lunch is Over: A Fundamental Turn Toward Concurrency in Software by Herb
Sutter, processors have peaked at around 3.7 GHz in
2005. The theoretical cap in in computing speed is limited by the speed of light and the laws of physics, and
we've very nearly reached that limit. Since CPU designers are unable to design faster CPUs, they have turned
toward designing processors with multiple cores and better support for multithreading. Programmers no longer
have the luxury of their applications running twice as fast
with improving hardware -- the free lunch is over.
Clockrates are not getting any faster, however the amount
of data businesses process each year grows exponentially
(usually at a rate of 10-20% per year). To meet the
growing processing demands of business, the future of all
software development is tending toward the development
of highly parallel, multithreaded applications which take
advantage of multicores processors, distributed systems,
and cloud computing.

33.4.2

Problems with Mutable State

Multithreaded programming has a reputation for being


notoriously dicult to get right and having a rather steep
learning curve. Why does it have this reputation? To put
it simply, mutable shared state makes programs dicult
to reason about. When two threads are mutating the same
variables, it is very easy to put the variable in an invalid
state.
Race Conditions
As a demonstration, heres how to increment a global variable using shared state (non-threaded version):
let test() = let counter = ref 0m let IncrGlobalCounter
numberOfTimes = for i in 1 .. numberOfTimes
do counter := !counter + 1m IncrGlobalCounter
1000000 IncrGlobalCounter 1000000 !counter // returns
2000000M

107

open System.Threading let testAsync() = let counter =


ref 0m let IncrGlobalCounter numberOfTimes = for i
in 1 .. numberOfTimes do counter := !counter + 1m
let AsyncIncrGlobalCounter numberOfTimes = new
Thread(fun () -> IncrGlobalCounter(numberOfTimes))
let t1 = AsyncIncrGlobalCounter 1000000 let t2 =
AsyncIncrGlobalCounter 1000000 t1.Start() // runs t1
asyncronously t2.Start() // runs t2 asyncronously t1.Join()
// waits until t1 nishes t2.Join() // waits until t2 nishes
!counter
This program should do the same thing as the previous
program, only it should run in ~1/2 the time. Here are
the results of 5 test runs in fsi:
> [for a in 1 .. 5 -> testAsync()];; val it : decimal list
= [1498017M; 1509820M; 1426922M; 1504574M;
1420401M]
The program is computationally sound, but it produces a
dierent result everytime its run. What happened?
It takes several machine instructions increment a decimal
value. In particular, the .NET IL for incrementing a decimal looks like this:
// pushes static eld onto evaluation stack L_0004:
ldsd valuetype [mscorlib]System.Decimal ConsoleApplication1.Program::i
//
executes
Decimal.op_Increment method L_0009:
call valuetype
[mscorlib]System.Decimal
[mscorlib]System.Decimal::op_Increment(valuetype [mscorlib]System.Decimal) // replaces static eld with value
from evaluation stack L_000e: stsd valuetype [mscorlib]System.Decimal ConsoleApplication1.Program::i
Imagine that we have two threads calling this code (calls
made by Thread1 and Thread2 are interleaved):
Thread1: Loads value 100 onto its evaluation stack.
Thread1: Call add with 100 and 1 Thread2: Loads
value 100 onto its evaluation stack. Thread1: Writes
101 back out to static variable Thread2: Call add with
100 and 1 Thread2: Writes 101 back out to static
variable (Oops, we've incremented an old value and wrote
it back out) Thread1: Loads value 101 onto its evaluation stack. Thread2: Loads value 101 onto its evaluation stack. (Now we let Thread1 get a little further
ahead of Thread2) Thread1: Call add with 101 and
1 Thread1: Writes 102 back out to static variable.
Thread1: Loads value 102 to evaluation stack Thread1:
Call add with 102 and 1 Thread1: Writes 103 back
out to static variable. Thread2: Call add with 101
and 1 Thread2: Writes 102 back out to static variable
(Oops, now we've completely overwritten work done by
Thread1!)

This works, but some programmer might notice that both


calls to IncrGlobalCounter could be computed in parallel
since theres no real reason to wait for one call to nish
before the other. Using the .NET threading primitives This kind of bug is called a race condition and it occurs
in the System.Threading namespace, a programmer can all the time in multithreaded applications. Unlike normal
bugs, race-conditions are often non-deterministic, makre-write this as follows:

108

CHAPTER 33. ASYNC WORKFLOWS

33.4.3 Why Functional


Matters
Usually, programmers solve race conditions by introducing them extremely dicult to track down.

ing locks. When an object is locked, all other threads


are forced to wait until the object is unlocked before
they proceed. We can re-write the code above using a
block access to the counter variable while each thread
writes to it:
open System.Threading let testAsync() = let counter =
ref 0m let IncrGlobalCounter numberOfTimes = for i in
1 .. numberOfTimes do lock counter (fun () -> counter :=
!counter + 1m) (* lock is a function in F# library. It automatically unlocks counter when 'fun () -> ...' completes
*) let AsyncIncrGlobalCounter numberOfTimes = new
Thread(fun () -> IncrGlobalCounter(numberOfTimes))
let t1 = AsyncIncrGlobalCounter 1000000 let t2 =
AsyncIncrGlobalCounter 1000000 t1.Start() // runs t1
asyncronously t2.Start() // runs t2 asyncronously t1.Join()
// waits until t1 nishes t2.Join() // waits until t2 nishes
!counter
The lock guarantees each thread exclusive access to
shared state and forces each thread to wait on the other
while the code counter := !counter + 1m runs to completion. This function now produces the expected result.
Deadlocks
Locks force threads to wait until an object is unlocked.
However, locks often lead to a new problem: Lets say
we have ThreadA and ThreadB which operate on two
corresponding pieces of shared state, StateA and StateB.
ThreadA locks stateA and stateB, ThreadB locks stateB
and stateA. If the timing is right, when ThreadA needs
to access stateB, its waits until ThreadB unlocks stateB;
when ThreadB needs to access stateA, it can't proceed
either since stateA is locked by ThreadA. Both threads
mutually block one another, and they are unable to proceed any further. This is called a deadlock.
Heres some simple code which demonstrates a deadlock:
open System.Threading let testDeadlock() = let stateA
= ref Shared State A let stateB = ref Shared State
B let threadA = new Thread(fun () -> printfn threadA
started lock stateA (fun () -> printfn stateA: %s
!stateA Thread.Sleep(100) // pauses thread for 100
ms. Simimulates busy processing lock stateB (fun () ->
printfn stateB: %s !stateB)) printfn threadA nished)
let threadB = new Thread(fun () -> printfn threadB
started lock stateB (fun () -> printfn stateB: %s !stateB
Thread.Sleep(100) // pauses thread for 100 ms. Simimulates busy processing lock stateA (fun () -> printfn
stateA: %s !stateA)) printfn threadB nished)
printfn Starting... threadA.Start() threadB.Start()
threadA.Join() threadB.Join() printfn Finished...
These kinds of bugs occur all the time in multithreaded
code, although they usually aren't quite as explicit as the
code shown above.

Programming

To put it bluntly, mutable state is enemy of multithreaded


code. Functional programming often simplies multithreading tremendously: since values are immutable
by default, programmers don't need to worry about one
thread mutating the value of shared state between two
threads, so it eliminates a whole class of multithreading
bugs related to race conditions. Since there are no race
conditions, theres no reason to use locks either, so immutability eliminates another whole class of bugs related
to deadlocks as well.

Chapter 34

MailboxProcessor
F#'s MailboxProcessor class is essentially a dedicated
message queue running on its own logical thread of control. Any thread can send the MailboxProcessor a message asynchronously or synchronously, allowing threads
to communicate between one another through message
passing. The thread of control is actually a lightweight
simulated thread implemented via asynchronous reactions to messages. This style of message-passing concurrency is inspired by the Erlang programming language.

static
member
Start
:
initial:(MailboxProcessor<'msg> -> Async<unit>)
* ?asyncGroup:AsyncGroup -> MailboxProcessor<'msg>
Create and start an instance of a MailboxProcessor. The asynchronous computation executed by the processor is the one returned by
the 'initial' function.
member Post : message:'msg -> unit

34.1 Dening MailboxProcessors

Post a message to the message queue of the


MailboxProcessors
are
created
using
the
MailboxProcessor, asynchronously.
MailboxProcessor.Start method which has the type Start
: initial:(MailboxProcessor<'msg> -> Async<unit>) * member
PostAndReply
:
buildMes?asyncGroup:AsyncGroup -> MailboxProcessor<'msg>: sage:(AsyncReplyChannel<'reply> -> 'msg) *
let counter = MailboxProcessor.Start(fun inbox -> let ?timeout:int * ?exitContext:bool -> 'reply
rec loop n = async { do printfn n = %d, waiting... n
Post a message to the message queue of the
let! msg = inbox.Receive() return! loop(n+msg) } loop 0)
MailboxProcessor and await a reply on the
channel. The message is produced by a sinThe value inbox has the type MailboxProcessor<'msg>
gle call to the rst function which must build
and represents the message queue. The method ina message containing the reply channel. The
box.Receive() dequeues the rst message from the mesreceiving MailboxProcessor must process this
sage queue and binds it to the msg identier. If there are
message and invoke the Reply method on the
no messages in queue, Receive releases the thread back to
reply channel precisely once.
the threadpool and waits for further messages. No threads
are blocked while Receive waits for further messages.
member Receive : ?timeout:int -> Async<'msg>
We can experiment with counter in fsi:
> let counter = MailboxProcessor.Start(fun inbox ->
let rec loop n = async { do printfn n = %d, waiting...
n let! msg = inbox.Receive() return! loop(n+msg) }
loop 0);; val counter : MailboxProcessor<int> n = 0,
waiting... > counter.Post(5);; n = 5, waiting... val it : unit
= () > counter.Post(20);; n = 25, waiting... val it : unit
= () > counter.Post(10);; n = 35, waiting... val it : unit = ()

Return an asynchronous computation which


will consume the rst message in arrival order. No thread is blocked while waiting for
further messages. Raise a TimeoutException
if the timeout is exceeded.

34.3 Two-way Communication


Just as easily as we can send messages to MailboxProcessors, a MailboxProcessor can send replies back to consumers. For example, we can interrogate the value of
There are several useful methods in the MailboxProcessor a MailboxProcessor using the PostAndReply method as
class:
follows:

34.2 MailboxProcessor Methods

109

110

CHAPTER 34. MAILBOXPROCESSOR

type msg = | Incr of int | Fetch of AsyncReplyChannel<int> let counter = MailboxProcessor.Start(fun inbox
-> let rec loop n = async { let! msg = inbox.Receive()
match msg with | Incr(x) -> return! loop(n + x) |
Fetch(replyChannel) -> replyChannel.Reply(n) return!
loop(n) } loop 0)
The msg union wraps two types of messages: we
can tell the MailboxProcessor to increment, or have
it send its contents to a reply channel. The type
AsyncReplyChannel<'a> exposes a single method, member Reply : 'reply -> unit. We can use this class in fsi as
follows:
> counter.Post(Incr 7);; val it : unit = () >
counter.Post(Incr 50);; val it :
unit = () >
counter.PostAndReply(fun replyChannel -> Fetch
replyChannel);; val it : int = 57
Notice that PostAndReply is a syncronous method.

34.3.1

Encapsulating MailboxProcessors
with Objects

= inbox.Receive() match msg with | Die -> return () |


Next(reply) -> reply.Reply(n) return! loop(n + 1) } loop
init) let lter(c : MailboxProcessor<'a seqMsg>, pred)
= MailboxProcessor.Start(fun inbox -> let rec loop() =
async { let! msg = inbox.Receive() match msg with | Die
-> c.Post(Die) return() | Next(reply) -> let rec lter' n
= if pred n then async { return n } else async {let! m
= c.PostAndAsyncReply(Next) return! lter' m } let!
testItem = c.PostAndAsyncReply(Next) let! lteredItem
= lter' testItem reply.Reply(lteredItem) return! loop()
} loop() ) let processor = MailboxProcessor.Start(fun
inbox -> let rec loop (oldFilter : MailboxProcessor<int
seqMsg>) prime = async { let! msg = inbox.Receive()
match msg with | Die -> oldFilter.Post(Die) return()
| Next(reply) -> reply.Reply(prime) let newFilter
= lter(oldFilter, (fun x -> x % prime <> 0)) let!
newPrime = oldFilter.PostAndAsyncReply(Next) return! loop newFilter newPrime } loop (counter(3))
2) member this.Next() = processor.PostAndReply(
(fun reply -> Next(reply)), timeout = 2000) interface
System.IDisposable with member this.Dispose() =
processor.Post(Die) static member upto max = [ use p =
new primes() let lastPrime = ref (p.Next()) while !lastPrime <= max do yield !lastPrime lastPrime := p.Next() ]

Often, we don't want to expose the implementation details counter represents an innite list of numbers from
of our classes to consumers. For example, we can re-write n..innity.
the example above as a class which exposes a few select
lter is simply a lter for another MailboxProcessor. Its
methods:
analogous to Seq.lter.
type countMsg = | Die | Incr of int | Fetch of AsyncReplyChannel<int> type counter() = let innerCounter = processor is essentially an iterative lter: we seed our
MailboxProcessor.Start(fun inbox -> let rec loop n = prime list with the rst prime, 2 and a innite list of numasync { let! msg = inbox.Receive() match msg with | Die bers from 3..innity. Each time we process a message,
-> return () | Incr x -> return! loop(n + x) | Fetch(reply) we return the prime number, then replace our innite list
-> reply.Reply(n); return! loop n } loop 0) mem- with a new list which lters out all numbers divisible by
ber this.Incr(x) = innerCounter.Post(Incr x) member our prime. The head of each new ltered list is the next
this.Fetch() = innerCounter.PostAndReply((fun reply prime number.
-> Fetch(reply)), timeout = 2000) member this.Die() = So, the rst time we call Next, we get back a 2 and reinnerCounter.Post(Die)
place our innite list with all numbers not divisible by
two. We call next again, we get back the next prime, 3,
and lter our list again for all numbers divisible by 3. We
call next again, we get back the next prime, 5 (we skip 4
34.4 MailboxProcessor Examples since its divisible by 2), and lter all numbers divisible by
5. This process repeats indenitely. The end result is a
prime number sieve with an identical implementation to
34.4.1 Prime Number Sieve
the Sieve of Eratosthenes.
Rob Pike delivered a fascinating presentation at a Google
TechTalk on the NewSqueak programming language.
NewSqueaks approach to concurrency uses channels,
analogous to MailboxProcessors, for inter-thread communication. Toward the end of the presentation, he
demonstrates how to implement a prime number sieve using these channels. The following is an implementation
of prime number sieve based on Pikes NewSqueak code:
type 'a seqMsg = | Die | Next of AsyncReplyChannel<'a>
type primes() = let counter(init) = MailboxProcessor.Start(fun inbox -> let rec loop n = async { let! msg

We can test this class in fsi:


> let p = new primes();; val p : primes > p.Next();; val it
: int = 2 > p.Next();; val it : int = 3 > p.Next();; val it :
int = 5 > p.Next();; val it : int = 7 > p.Next();; val it : int
= 11 > p.Next();; val it : int = 13 > p.Next();; val it : int
= 17 > primes.upto 100;; val it : int list = [2; 3; 5; 7; 11;
13; 17; 19; 23; 29; 31; 37; 41; 43; 47; 53; 59; 61; 67; 71;
73; 79; 83; 89; 97]

Chapter 35

Lexing and Parsing


Lexing and parsing is a very handy way to convert 35.2 Extended Example: Parsing
source-code (or other human-readable input which has
SQL
a well-dened syntax) into an abstract syntax tree (AST)
which represents the source-code. F# comes with two
tools, FsLex and FsYacc, which are used to convert input The following code will demonstrate step-by-step how
to dene a simple lexer/parser for a subset of SQL. If
into an AST.
you're using Visual Studio, you should add a reference to
FsLex and FsYacc have more or less the same speciFSharp.PowerPack.dll to your project. If you're compilcation as OCamlLex and OCamlYacc, which in turn
ing on the commandline, use the -r ag to reference the
are based on the Lex and Yacc family of lexer/parser
aforemented F# powerpack assembly.
generators.
Virtually all material concerned with
OCamlLex/OCamlYacc can transfer seamlessly over to
FsLex/FsYacc. With that in mind, SooHyoung Ohs
OCamlYacc tutorial and companion OCamlLex Tutorial 35.2.1 Step 1: Dene the Abstract Syntax
Tree
are the single best online resources to learn how to use the
lexing and parsing tools which come with F# (and OCaml
We want to parse the following SQL statement:
for that matter!).

35.1 Lexing and Parsing from a


High-Level View

SELECT x, y, z FROM t1 LEFT JOIN t2 INNER JOIN


t3 ON t3.ID = t2.ID WHERE x = 50 AND y = 20
ORDER BY x ASC, y DESC, z

This is a pretty simple query, and while it doesn't demonTransforming input into a well-dened abstract syntax strate everything you can do with the language, its a good
start. We can model everything in this query using the
tree requires (at minimum) two transformations:
following denitions in F#:
1. A lexer uses regular expressions to convert each syn- // Sql.fs type value = | Int of int | Float of oat | String
tactical element from the input into a token, essen- of string type dir = Asc | Desc type op = Eq | Gt | Ge |
Lt | Le // =, >, >=, <, <= type order = string * dir type
tially mapping the input to a stream of tokens.
where = | Cond of (value * op * value) | And of where *
2. A parser reads in a stream of tokens and attempts to where | Or of where * where type joinType = Inner | Left
match tokens to a set of rules, where the end result | Right type join = string * joinType * where option //
maps the token stream to an abstract syntax tree.
table name, join, optional on clause type sqlStatement
= { Table : string; Columns : string list; Joins : join list;
It is certainly possible to write a lexer which generates the Where : where option; OrderBy : order list }
abstract syntax tree directly, but this only works for the
most simplistic grammars. If a grammar contains balanced parentheses or other recursive constructs, optional
tokens, repeating groups of tokens, operator precedence,
or anything which can't be captured by regular expressions, then it is easiest to write a parser in addition to a
lexer.

A record type neatly groups all of our related values into


a single object. When we nish our parser, it should be
able to convert a string in an object of type sqlStatement.

35.2.2 Step 2: Dene the parser tokens

With F#, it is possible to write custom le formats, domain specic languages, and even full-blown compilers A token is any single identiable element in a grammar.
Lets look at the string we're trying to parse:
for your new language.
111

112

CHAPTER 35. LEXING AND PARSING

SELECT x, y, z FROM t1 LEFT JOIN t2 INNER JOIN 35.2.3 Step 3: Dening the lexer rules
t3 ON t3.ID = t2.ID WHERE x = 50 AND y = 20
ORDER BY x ASC, y DESC, z
Lexers convert text inputs into a stream of tokens. We
can start with the following boiler plate code:
So far, we have several keywords (by convention, all keywords are uppercase): SELECT, FROM, LEFT, INNER, { // header: any valid F# can appear here. open Lexing
RIGHT, JOIN, ON, WHERE, AND, OR, ORDER, BY, } // regex macros let char = ['a'-'z' 'A'-'Z'] let digit =
['0'-'9'] let int = '-'?digit+ let oat = '-'?digit+ '.' digit+ let
ASC, DESC, and COMMA.
whitespace = [' ' '\t'] let newline = "\n\r | '\n' | '\r' // rules
There are also a few comparison operators: EQ, GT, GE, rule tokenize = parse | whitespace { tokenize lexbuf }
LT, LE.
| newline { lexbuf.EndPos <- lexbuf.EndPos.NextLine;
We also have non-keyword identiers composed of tokenize lexbuf; } | eof { () }
strings and numeric literals. which well represent using
the keyword ID, INT, FLOAT.
This is not real F# code, but rather a special language
Finally, there is one more token, EOF, which indicates used by FsLex.
the end of our input stream.
The let bindings at the top of the le are used to dene
Now we can create a basic parser le for FsYacc, name regular expression macros. eof is a special marker used
to identify the end of a string buer input.
the le SqlParser.fsp:
%{ open Sql %} %token <string> ID %token <int>
INT %token <oat> FLOAT %token AND OR %token
COMMA %token EQ LT LE GT GE %token JOIN
INNER LEFT RIGHT ON %token SELECT FROM
WHERE ORDER BY %token ASC DESC %token EOF
// start %start start %type <string> start %% start: | {
Nothing to see here } %%

rule ... = parse ... denes our lexing function, called tokenize above. Our lexing function consists of a series of
rules, which has two pieces: 1) a regular expression, 2)
an expression to evaluate if the regex matches, such as
returning a token. Text is read from the token stream one
character at a time until it matches a regular expression
and returns a token.

We can ll in the remainder of our lexer by adding more


This is boilerplate code with the section for tokens lled matching expressions:
in.
{ open Lexing // opening the SqlParser module to give us
Compile the parser using the following command line: access to the tokens it denes open SqlParser } let char =
['a'-'z' 'A'-'Z'] let digit = ['0'-'9'] let int = '-'?digit+ let oat
fsyacc SqlParser.fsp
= '-'?digit+ '.' digit+ let identier = char(char|digit|['-' '_'
'.'])* let whitespace = [' ' '\t'] let newline = "\n\r | '\n' | '\r'
Tips:
rule tokenize = parse | whitespace { tokenize lexbuf } |
newline { lexbuf.EndPos <- lexbuf.EndPos.NextLine; to If you haven't done so already, you should
kenize lexbuf; } | int { INT(Int32.Parse(lexeme lexbuf))
add the F# bin directory to your PATH
} | oat { FLOAT(Double.Parse(lexeme lexbuf)) } | ',' {
environment variable.
COMMA } | eof { EOF }
If you're using Visual Studio, you can
automatically generate your parser code
on each compile. Right-click on your
project le and choose Properties.
Navigate to the Build Events tab, and add
the following to the 'Pre-build event command line' and use the following: fsyacc "$(ProjectDir)SqlParser.fsp. Also
remember to exclude this le from the
build process: right-click the le, choose
Properties and select None against
Build Action.
If everything works, FsYacc will generate two les, SqlParser.fsi and SqlParser.fs. You'll need to add these les
to your project if they don't already exist. If you open the
SqlParser.fsi le, you'll notice the tokens you dened in
your .fsl le have been converted into a union type.

Notice the code between the {'s and }'s consists of plain
old F# code. Also notice we are returning the same tokens (INT, FLOAT, COMMA and EOF) that we dened
in SqlParser.fsp. As you can probably infer, the code lexeme lexbuf returns the string our parser matched. The
tokenize function will be converted into function which
has a return type of SqlParser.token.
We can ll in the rest of the lexer rules fairly easily:
{ open System open SqlParser open Lexing let keywords = [ SELECT, SELECT; FROM, FROM;
WHERE, WHERE; ORDER, ORDER; BY, BY;
JOIN, JOIN; INNER, INNER; LEFT, LEFT;
RIGHT, RIGHT; ASC, ASC; DESC, DESC;
AND, AND; OR, OR; ON, ON; ] |> Map.ofList
let ops = [ "=", EQ; "<", LT; "<=", LE; ">", GT; ">=",
GE; ] |> Map.ofList } let char = ['a'-'z' 'A'-'Z'] let
digit = ['0'-'9'] let int = '-'?digit+ let oat = '-'?digit+

35.2. EXTENDED EXAMPLE: PARSING SQL


'.' digit+ let identier = char(char|digit|['-' '_' '.'])* let
whitespace = [' ' '\t'] let newline = "\n\r | '\n' | '\r' let
operator = ">" | ">=" | "<" | "<=" | "=" rule tokenize
= parse | whitespace { tokenize lexbuf } | newline {
lexbuf.EndPos <- lexbuf.EndPos.NextLine; tokenize
lexbuf; } | int { INT(Int32.Parse(lexeme lexbuf)) }
| oat { FLOAT(Double.Parse(lexeme lexbuf)) } |
operator { ops.[lexeme lexbuf] } | identier { match
keywords.TryFind(lexeme lexbuf) with | Some(token) ->
token | None -> ID(lexeme lexbuf) } | ',' { COMMA } |
eof { EOF }

113
record.
The F# code contains "$1, "$2, :$3, etc. which vaguely
resembles regex replace syntax. Each "$#" corresponds
to the index (starting at 1) of the token in our matching
rule. The indexes become obvious when theyre annotated as follows:
(* $1 $2 *) start: SELECT columnList (* $3 $4 *)
FROM ID (* $5 *) joinList (* $6 *) whereClause (*
$7 *) orderByClause (* $8 *) EOF { { Table = $4;
Columns = $2; Joins = $5; Where = $6; OrderBy = $7 } }

So, the start rule breaks our tokens into a basic shape,
which we then use to map to our sqlStatement record.
You're probably wondering where the columnList, joinList, whereClause, and orderByClause come from -these are not tokens, but are rather additional parse rules
To compile this lexer, execute the following code on the which we'll have to dene. Lets start with the rst rule:
commandline: fslex SqlLexer.fsl. (Try adding this le to
your projects Build Events as well.) Then, add the le columnList: | ID { [$1]} | ID COMMA columnList { $1
SqlLexer.fs to the project. We can experiment with the :: $3 }
lexer now with some sample input:
open System open Sql let x = " SELECT x, y, z columnList matches text in the style of a, b, c, ... z
FROM t1 LEFT JOIN t2 INNER JOIN t3 ON t3.ID and returns the results as a list. Notice this rule is dened
= t2.ID WHERE x = 50 AND y = 20 ORDER BY x recursively (also notice the order of rules is not signiASC, y DESC, z " let lexbuf = Lexing.from_string x cant). FsYaccs match algorithm is greedy, meaning it
while not lexbuf.IsPastEndOfStream do printfn "%A will try to match as many tokens as possible. When FsY(SqlLexer.tokenize lexbuf) Console.WriteLine("(press acc receives an ID token, it will match the rst rule, but it
also matches part of the second rule as well. FsYacc then
any key)") Console.ReadKey(true) |> ignore
performs a one-token lookahead: it the next token is a
COMMA, then it will attempt to match additional tokens
This program will print out a list of tokens matched by until the full rule can be satised.
the string above.
Notice we've created a few maps, one for keywords and
one for operators. While we certainly can dene these as
rules in our lexer, its generally recommended to have a
very small number of rules to avoid a state explosion.

35.2.4

Step 4: Dene the parser rules

A parser converts a stream of tokens into an abstract syntax tree. We can modify our boilerplate parser as follows
(will not compile):

Note: The denition of columnList above is


not tail recursive, so it may throw a stack overow exception for exceptionally large inputs.
A tail recursive version of this rule can be dened as follows:

start: ... EOF { { Table = $4; Columns =


%{ open Sql %} %token <string> ID %token <int>
List.rev $2; Joins = $5; Where = $6; OrderBy
INT %token <oat> FLOAT %token AND OR %token
= $7 } } columnList: | ID { [$1]} | columnList
COMMA %token EQ LT LE GT GE %token JOIN
COMMA ID { $3 :: $1 }
INNER LEFT RIGHT ON %token SELECT FROM
WHERE ORDER BY %token ASC DESC %token
EOF // start %start start %type <Sql.sqlStatement> start
The tail-recursive version creates the list back%% start: SELECT columnList FROM ID joinList
wards, so we have to reverse when we return
whereClause orderByClause EOF { { Table = $4;
our nal output from the parser.
Columns = $2; Joins = $5; Where = $6; OrderBy = $7 }
} value: | INT { Int($1) } | FLOAT { Float($1) } | ID {
We can treat the JOIN clause in the same way, however
String($1) } %%
its a little more complicated:
Lets examine the start: function. You can immediately
see that we have a list of tokens which gives a rough outline of a select statement. In addition to that, you can
see the F# code contained between {'s and }'s which will
be executed when the code successfully matches -- in
this case, its returning an instance of the Sql.sqlStatement

// join clause joinList: | { [] } // empty rule, matches 0


tokens | joinClause { [$1] } | joinClause joinList { $1 ::
$2 } joinClause: | INNER JOIN ID joinOnClause { $3,
Inner, $4 } | LEFT JOIN ID joinOnClause { $3, Left,
$4 } | RIGHT JOIN ID joinOnClause { $3, Right, $4 }
joinOnClause: | { None } | ON conditionList { Some($2)

114

CHAPTER 35. LEXING AND PARSING

} conditionList: | value op value { Cond($1, $2, $3) }


| value op value AND conditionList { And(Cond($1,
$2, $3), $5) } | value op value OR conditionList {
Or(Cond($1, $2, $3), $5) }
joinList is dened in terms of several functions. This results because there are repeating groups of tokens (such
as multiple tables being joined) and optional tokens (the
optional ON clause). You've already seen that we handle repeating groups of tokens using recursive rules. To
handle optional tokens, we simply break the optional syntax into a separate function, and create an empty rule to
represent 0 tokens.
With this strategy in mind, we can write the remaining
parser rules:
%{ open Sql %} %token <string> ID %token <int>
INT %token <oat> FLOAT %token AND OR %token
COMMA %token EQ LT LE GT GE %token JOIN
INNER LEFT RIGHT ON %token SELECT FROM
WHERE ORDER BY %token ASC DESC %token EOF
// start %start start %type <Sql.sqlStatement> start %%
start: SELECT columnList FROM ID joinList whereClause orderByClause EOF { { Table = $4; Columns =
List.rev $2; Joins = $5; Where = $6; OrderBy = $7 } }
columnList: | ID { [$1]} | columnList COMMA ID {
$3 :: $1 } // join clause joinList: | { [] } | joinClause
{ [$1] } | joinClause joinList { $1 :: $2 } joinClause: |
INNER JOIN ID joinOnClause { $3, Inner, $4 } | LEFT
JOIN ID joinOnClause { $3, Left, $4 } | RIGHT JOIN
ID joinOnClause { $3, Right, $4 } joinOnClause: | {
None } | ON conditionList { Some($2) } conditionList:
| value op value { Cond($1, $2, $3) } | value op value
AND conditionList { And(Cond($1, $2, $3), $5) } |
value op value OR conditionList { Or(Cond($1, $2, $3),
$5) } // where clause whereClause: | { None } | WHERE
conditionList { Some($2) } op: EQ { Eq } | LT { Lt
} | LE { Le } | GT { Gt } | GE { Ge } value: | INT {
Int($1) } | FLOAT { Float($1) } | ID { String($1) } //
order by clause orderByClause: | { [] } | ORDER BY
orderByList { $3 } orderByList: | orderBy { [$1] } |
orderBy COMMA orderByList { $1 :: $3 } orderBy: |
ID { $1, Asc } | ID ASC { $1, Asc } | ID DESC { $1,
Desc} %%

35.2.5

Step 5:
gether

// start %start start %type <Sql.sqlStatement> start %%


start: SELECT columnList FROM ID joinList whereClause orderByClause EOF { { Table = $4; Columns =
List.rev $2; Joins = $5; Where = $6; OrderBy = $7 } }
columnList: | ID { [$1]} | columnList COMMA ID {
$3 :: $1 } // join clause joinList: | { [] } | joinClause
{ [$1] } | joinClause joinList { $1 :: $2 } joinClause: |
INNER JOIN ID joinOnClause { $3, Inner, $4 } | LEFT
JOIN ID joinOnClause { $3, Left, $4 } | RIGHT JOIN
ID joinOnClause { $3, Right, $4 } joinOnClause: | {
None } | ON conditionList { Some($2) } conditionList:
| value op value { Cond($1, $2, $3) } | value op value
AND conditionList { And(Cond($1, $2, $3), $5) } |
value op value OR conditionList { Or(Cond($1, $2, $3),
$5) } // where clause whereClause: | { None } | WHERE
conditionList { Some($2) } op: EQ { Eq } | LT { Lt
} | LE { Le } | GT { Gt } | GE { Ge } value: | INT {
Int($1) } | FLOAT { Float($1) } | ID { String($1) } //
order by clause orderByClause: | { [] } | ORDER BY
orderByList { $3 } orderByList: | orderBy { [$1] } |
orderBy COMMA orderByList { $1 :: $3 } orderBy: |
ID { $1, Asc } | ID ASC { $1, Asc } | ID DESC { $1,
Desc} %%
SqlLexer.fsl
{ open System open SqlParser open Lexing let keywords = [ SELECT, SELECT; FROM, FROM;
WHERE, WHERE; ORDER, ORDER; BY, BY;
JOIN, JOIN; INNER, INNER; LEFT, LEFT;
RIGHT, RIGHT; ASC, ASC; DESC, DESC;
AND, AND; OR, OR; ON, ON; ] |> Map.of_list
let ops = [ "=", EQ; "<", LT; "<=", LE; ">", GT; ">=",
GE; ] |> Map.of_list } let char = ['a'-'z' 'A'-'Z'] let
digit = ['0'-'9'] let int = '-'?digit+ let oat = '-'?digit+
'.' digit+ let identier = char(char|digit|['-' '_' '.'])* let
whitespace = [' ' '\t'] let newline = "\n\r | '\n' | '\r' let
operator = ">" | ">=" | "<" | "<=" | "=" rule tokenize
= parse | whitespace { tokenize lexbuf } | newline {
lexbuf.EndPos <- lexbuf.EndPos.NextLine; tokenize
lexbuf; } | int { INT(Int32.Parse(lexeme lexbuf)) }
| oat { FLOAT(Double.Parse(lexeme lexbuf)) } |
operator { ops.[lexeme lexbuf] } | identier { match
keywords.TryFind(lexeme lexbuf) with | Some(token) ->
token | None -> ID(lexeme lexbuf) } | ',' { COMMA } |
eof { EOF }

Piecing Everything To- Program.fs

open System open Sql let x = " SELECT x, y, z FROM


t1 LEFT JOIN t2 INNER JOIN t3 ON t3.ID = t2.ID
WHERE x = 50 AND y = 20 ORDER BY x ASC,
Here is the complete code for our lexer/parser:
y DESC, z " let lexbuf = Lexing.from_string x let
SqlParser.fsp
y = SqlParser.start SqlLexer.tokenize lexbuf printfn
%{ open Sql %} %token <string> ID %token <int> "%A y Console.WriteLine("(press any key)") ConINT %token <oat> FLOAT %token AND OR %token sole.ReadKey(true) |> ignore
COMMA %token EQ LT LE GT GE %token JOIN
INNER LEFT RIGHT ON %token SELECT FROM
Altogether, our minimal SQL lexer/parser is about 150
WHERE ORDER BY %token ASC DESC %token EOF

35.2. EXTENDED EXAMPLE: PARSING SQL

115

lines of code (including non-trivial lines of code and 35.2.6 Sample


whitespace). I'll leave it as an exercise for the reader to
https://github.com/obeleh/FsYacc-Example
implement the remainder of the SQL language spec.
2011-03-06: I tried the above instructions with VS2010 2011-07-07 (mkdu) Thanks for posting the sample.
and F# 2.0 and PowerPack 2.0. I had to make a few Here is what I did to the Program.fs le:
changes:
namespace FS module Parser = open System open Sql let
x = " SELECT x, y, z FROM t1 LEFT JOIN t2 INNER
Add module SqlLexer on the 2nd line of JOIN t3 ON t3.ID = t2.ID WHERE x = 50 AND y
SqlLexer.fsl
= 20 ORDER BY x ASC, y DESC, z " let ParseSql x
= let lexbuf = Lexing.LexBuer<_>.FromString x let
Change Map.of_list to Map.ofList
y = SqlParser.start SqlLexer.tokenize lexbuf y let y =
Add " --module SqlParser to the command line of (ParseSql x) printfn "%A y Console.WriteLine("(press
any key)") Console.ReadKey(true) |> ignore
fsyacc
Add FSharp.PowerPack to get Lexing module

I added a C# console project for testing and this is what


is in the Program.cs le:

2011-07-06: (Sjuul Janssen) These where the steps I had


using System;
using System.Collections.Generic;
to take in order to make this work.
using System.Linq; using System.Text; namespace
If you get the message Expecting a LexBuer<char> but
ConsoleApplication1 { class Program { static void
given a LexBuer<byte> The type 'char' does not match
Main(string[] args) { string query = @"SELECT x,
the type 'byte'"
y, z FROM t1 LEFT JOIN t2 INNER JOIN t3 ON
t3.ID = t2.ID WHERE x = 50 AND y = 20 ORDER
Add fslex.exe "$(ProjectDir)SqlLexer.fsl -- BY x ASC, y DESC, z"; Sql.sqlStatement stmnt =
unicode to the pre-build
FS.Parser.ParseSql(query); } }; }
in program.fs change let lexbuf
ing.from_string x to let lexbuf
ing.LexBuer<_>.FromString x

=
=

LexLex- I had to add the YaccSample project reference as well as a


reference to the FSharp.Core assembly to get this to work.

in SqlLexer.fsi change lexeme lexbuf


LexBuer<_>.LexemeString lexbuf

to

If anyone could help me gure out how to support table


aliases that would be awesome.

Thanks
If you get the message that some module doesn't exist or
2011-07-08 (Sjuul Janssen) Contact me through my
that some module is declared multiple times. Make sure
github account. I'm working on this and some other stu.
that in the solution explorer the les come in this order:
Sql.fs
SqlParser.fsp
SqlLexer.fsl
SqlParser.fsi
SqlParser.fs
SqlLexer.fs
Program.fs

If
you
get
the
message
Method
not
found:
'System.Object
Microsoft.FSharp.Text.Parsing.Tables`1.Interpret(Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Text.Lexing.LexBuer`1<By
... Go to http://www.microsoft.com/download/en/
details.aspx?id=15834 and reinstall Visual Studio 2010
F# 2.0 Runtime SP1 (choose for repair)
2011-07-06: (mkdu) Could someone please provide a
sample project. I have followed all of your changes but
still can not build. Thanks.

116

CHAPTER 35. LEXING AND PARSING

35.3 Text and image sources, contributors, and licenses


35.3.1

Text

F Sharp Programming/Preface Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Preface?oldid=2065645 Contributors:


Panic2k4, Awesome Princess, Adrignola and Avicennasis
F Sharp Programming/Introduction Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Introduction?oldid=3043465 Contributors: Howard Geraint Ricketts, Jguk, AdRiley, Awesome Princess, QuiteUnusual, Adrignola, Tboronczyk, Chris uvic, Codingtales,
Mtreit and Anonymous: 5
F Sharp Programming/Getting Set Up Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Getting_Set_Up?oldid=3007382
Contributors: Howard Geraint Ricketts, Jguk, AdRiley, Awesome Princess, QuiteUnusual, Toyvo~enwikibooks, Adrignola, Jupiter258,
Tiibiidii, Tuxcanty, Quasilord and Anonymous: 11
F Sharp Programming/Basic Concepts Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Basic_Concepts?oldid=2753323
Contributors: Howard Geraint Ricketts, Jguk, AdRiley, Awesome Princess, QuiteUnusual, Toyvo~enwikibooks, Adrignola, Nagnatron,
Myourshaw, Anatolius Nemorarius, Yuu eo, Icarot and Anonymous: 24
F Sharp Programming/Values and Functions Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Values_and_Functions?
oldid=2584252 Contributors: Panic2k4, Awesome Princess, Adrignola, Polybius~enwikibooks, Samal84 and Anonymous: 17
F Sharp Programming/Pattern Matching Basics Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Pattern_Matching_
Basics?oldid=2263874 Contributors: Matt Crypto, Awesome Princess, QuiteUnusual, Adrignola, NoldorinElf, Samal84 and Anonymous:
7
F Sharp Programming/Recursion Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Recursion?oldid=2675323 Contributors: Panic2k4, Matt Crypto, SocratesJedi, Jomegat, Awesome Princess, Adrignola, Samal84, Mariusschulz and Anonymous: 6
F Sharp Programming/Higher Order Functions Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Higher_Order_
Functions?oldid=3081780 Contributors: Panic2k4, Matt Crypto, Awesome Princess, Adrignola, NoldorinElf, Bohszy, SnoopDougDoug
and Anonymous: 14
F Sharp Programming/Option Types Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Option_Types?oldid=2364878
Contributors: Awesome Princess, Adrignola and Anonymous: 2
F Sharp Programming/Tuples and Records Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Tuples_and_Records?
oldid=3015334 Contributors: Jomegat, Awesome Princess, Adrignola, NoldorinElf, Crocpulsar, Avicennasis and Anonymous: 14
F Sharp Programming/Lists Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Lists?oldid=2834688 Contributors:
Panic2k4, Jomegat, Xharze, Awesome Princess, Adrignola, Ymihere, Avicennasis, Arthirsch, Powergold1 and Anonymous: 10
F Sharp Programming/Sequences Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Sequences?oldid=3081863 Contributors: Leonariso, Awesome Princess, Adrignola, Gommer~enwikibooks, Diogobernini, Mariusschulz, Gilbertbw and Anonymous: 9
F Sharp Programming/Sets and Maps Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Sets_and_Maps?oldid=2672411
Contributors: Xharze, Awesome Princess, Adrignola, Ymihere, Jamesdgb, Mariusschulz and Anonymous: 8
F Sharp Programming/Discriminated Unions Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Discriminated_Unions?
oldid=2674438 Contributors: Panic2k4, Awesome Princess, QuiteUnusual, Van der Hoorn, Adrignola, Mariusschulz and Anonymous: 5
F Sharp Programming/Mutable Data Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Mutable_Data?oldid=2478488
Contributors: Awesome Princess, Adrignola, NoldorinElf, Avicennasis, Joechakra and Anonymous: 7
F Sharp Programming/Control Flow Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Control_Flow?oldid=3081972
Contributors: Ravichandar84, Awesome Princess, Adrignola and Anonymous: 4
F Sharp Programming/Arrays Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Arrays?oldid=3070478 Contributors:
Awesome Princess, Adrignola, Mariusschulz, Gilbertbw and Anonymous: 6
F Sharp Programming/Mutable Collections Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Mutable_Collections?
oldid=1992022 Contributors: Awesome Princess, Adrignola and Anonymous: 2
F Sharp Programming/Input and Output Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Input_and_Output?oldid=
3082031 Contributors: Awesome Princess, Adrignola and Anonymous: 3
F Sharp Programming/Exception Handling Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Exception_Handling?
oldid=2773079 Contributors: Awesome Princess and Anonymous: 6
F Sharp Programming/Operator Overloading Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Operator_Overloading?
oldid=2515260 Contributors: Awesome Princess, Lost~enwikibooks, NoldorinElf and Anonymous: 2
F Sharp Programming/Classes Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Classes?oldid=2450590 Contributors:
Awesome Princess, QuiteUnusual, Adrignola, NoldorinElf, Yuu eo, HMman and Anonymous: 12
F Sharp Programming/Inheritance Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Inheritance?oldid=2527479 Contributors: Awesome Princess, Adrignola, Preza8 and Anonymous: 1
F Sharp Programming/Interfaces Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Interfaces?oldid=2172625 Contributors: Awesome Princess, Adrignola and Anonymous: 3
F Sharp Programming/Events Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Events?oldid=3046243 Contributors:
Awesome Princess, Adrignola and Anonymous: 5
F Sharp Programming/Modules and Namespaces Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Modules_and_
Namespaces?oldid=2631539 Contributors: Awesome Princess, Adrignola, Samal84 and Anonymous: 3
F Sharp Programming/Units of Measure Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Units_of_Measure?oldid=
2606933 Contributors: Awesome Princess, *nix and Anonymous: 2

35.3. TEXT AND IMAGE SOURCES, CONTRIBUTORS, AND LICENSES

117

F Sharp Programming/Caching Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Caching?oldid=2367579 Contributors:


Awesome Princess, Adrignola, Fishpi, ProjSHiNKiROU and Anonymous: 5
F Sharp Programming/Active Patterns Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Active_Patterns?oldid=2161907
Contributors: Jomegat, Awesome Princess, Adrignola, Avicennasis, Jessitron and Anonymous: 7
F Sharp Programming/Advanced Data Structures Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Advanced_Data_
Structures?oldid=2971962 Contributors: Awesome Princess, Preza8, Jorgeoranelli and Anonymous: 5
F Sharp Programming/Reection Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Reflection?oldid=2065646 Contributors: Awesome Princess, Avicennasis and Anonymous: 1
F Sharp Programming/Computation Expressions Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Computation_
Expressions?oldid=3004044 Contributors: Greenrd, Recent Runes, Awesome Princess, Adrignola, Avicennasis, Pteromys42 and Anonymous: 15
F Sharp Programming/Async Workows Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Async_Workflows?oldid=
2468739 Contributors: Awesome Princess, Adrignola, Jhuang~enwikibooks, Tsyselsky and Anonymous: 7
F Sharp Programming/MailboxProcessor Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/MailboxProcessor?oldid=
2762465 Contributors: Awesome Princess, Adrignola, Atcovi, Jameycc1 and Anonymous: 5
F Sharp Programming/Lexing and Parsing Source: https://en.wikibooks.org/wiki/F_Sharp_Programming/Lexing_and_Parsing?oldid=
2678474 Contributors: Awesome Princess, Adrignola, Banksar, Avicennasis, Mkdu and Anonymous: 8

35.3.2

Images

File:Data_stack.svg Source: https://upload.wikimedia.org/wikipedia/commons/2/29/Data_stack.svg License: Public domain Contributors: made in Inkscape, by myself User:Boivie. Based on Image:Stack-sv.png, originally uploaded to the Swedish Wikipedia in 2004 by
sv:User:Shrimp Original artist: User:Boivie
File:Queue_algorithmn.jpg Source: https://upload.wikimedia.org/wikipedia/commons/4/45/Queue_algorithmn.jpg License: Public domain Contributors: Own work Original artist: Leon22
File:Red-black_tree_example.svg Source: https://upload.wikimedia.org/wikipedia/commons/6/66/Red-black_tree_example.svg License: CC-BY-SA-3.0 Contributors: This vector image was created with Inkscape. Original artist: en:User:Cburnett
File:Symbol_thumbs_down.svg Source: https://upload.wikimedia.org/wikipedia/commons/8/84/Symbol_thumbs_down.svg License:
Public domain Contributors: ? Original artist: ?
File:Symbol_thumbs_up.svg Source: https://upload.wikimedia.org/wikipedia/commons/8/87/Symbol_thumbs_up.svg License: Public
domain Contributors: Own work Original artist: Pratheepps (talk contribs)

35.3.3

Content license

Creative Commons Attribution-Share Alike 3.0

You might also like