The F# 2.0 Language Specification
Note: This documentation is the specification of the 2.0 release of F# made by Microsoft Research and theMicrosoft Developer Division in April 2010.
Discrepancies may exist between this specification and the 2.0 implementation. Some of these are noted ascomments in this document. If you find further discrepancies please contact us and we will gladly address theissue in future releases of this specification. The F# team is always very grateful for feedback on thisspecification, and on both the design and implementation of F#. You can submit feedback by emailingfsbugs@microsoft.com.
The latest version of this specification can be found at www.fsharp.net. Many thanks to the F# user community
for their helpful feedback on the document so far.
Certain parts of this specification refer to the C# 4.0, Unicode, and IEEE specifications.
Authors: Don Syme, with assistance from Anar Alimov,Keith Battocchi, Jomo Fisher, Michael Hale, Jack Hu,
Luke Hoban, Tao Liu, Dmitry Lomov, James Margetson, Brian McNamara, Joe Pamer, Penny Orwick, Daniel
Quirk, Chris Smith, Matteo Taveggia and others.
Notice© 2005-2012 Microsoft Corporation. Made available under the Apache 2.0 License as part of F# 2.0 source code.
Microsoft, Windows, and Visual F# are either registered trademarks or trademarks of Microsoft Corporation in the
U.S.A. and/or other countries/regions.
Other product and company names mentioned herein may be the trademarks of their respective owners.
Document Updates:
Updated with glossary, index, and style corrections, August 2010

Updated with glossary, index, and style corrections, February 2011

Updated with grammar summary, December 2011

Updated with formatting changes, April 2012

Table of Contents
INTRODUCTION .....................................................................................................................................................11
1.1

1.2

A FIRST PROGRAM......................................................................................................................................................... 11
1.1.1

Lightweight Syntax ........................................................................................................................................ 11

1.1.2

Making Data Simple ...................................................................................................................................... 12

1.1.3

Making Types Simple ..................................................................................................................................... 12

1.1.4

Functional Programming ............................................................................................................................... 13

1.1.5

Imperative Programming .............................................................................................................................. 14

1.1.6.NET Interoperability and CLI Fidelity............................................................................................................. 14

1.1.7

Parallel and Asynchronous Programming ..................................................................................................... 15

1.1.8

Strong Typing for Floating-Point Code........................................................................................................... 15

1.1.9

Object-Oriented Programming and Code Organization................................................................................. 16

NOTATIONAL CONVENTIONS IN THIS SPECIFICATION .............................................................................................................. 17

PROGRAM STRUCTURE..........................................................................................................................................19
LEXICAL ANALYSIS .................................................................................................................................................21
3.1

WHITESPACE ................................................................................................................................................................ 21

3.2

COMMENTS ................................................................................................................................................................. 21

3.3

CONDITIONAL COMPILATION ............................................................................................................................................ 21

3.4

IDENTIFIERS AND KEYWORDS ............................................................................................................................................ 22

3.5

STRINGS AND CHARACTERS .............................................................................................................................................. 23

3.6

SYMBOLIC KEYWORDS .................................................................................................................................................... 24

3.7

SYMBOLIC OPERATORS ................................................................................................................................................... 25

3.8

NUMERIC LITERALS ........................................................................................................................................................ 25
3.8.1

Post-filtering of Adjacent Prefix Tokens ......................................................................................................... 26

3.8.2

Post-filtering of Integers Followed by Adjacent “..” ....................................................................................... 26

3.8.3

Reserved Numeric Literal Forms .................................................................................................................... 26

3.9LINE DIRECTIVES ............................................................................................................................................................ 27

3.10

HIDDEN TOKENS ...................................................................................................................................................... 27

3.11

IDENTIFIER REPLACEMENTS ......................................................................................................................................... 27

BASIC GRAMMAR ELEMENTS.................................................................................................................................29
4.1

OPERATOR NAMES ........................................................................................................................................................ 29

4.2

LONG IDENTIFIERS ......................................................................................................................................................... 31

4.3

CONSTANTS ................................................................................................................................................................. 31

4.4

OPERATORS AND PRECEDENCE.......................................................................................................................................... 32
4.4.1

Categorization of Symbolic Operators ........................................................................................................... 32

4.4.2

Precedence of Symbolic Operators and Pattern/Expression Constructs ........................................................ 33

TYPES AND TYPE CONSTRAINTS .............................................................................................................................35
5.1

5.2

CHECKING SYNTACTIC TYPES ............................................................................................................................................ 36
5.1.1

Named Types ................................................................................................................................................. 37

5.1.2

Variable Types ............................................................................................................................................... 37

5.1.3

Tuple Types .................................................................................................................................................... 38

5.1.4

Array Types .................................................................................................................................................... 38

5.1.5

Constrained Types ......................................................................................................................................... 38

TYPE CONSTRAINTS ........................................................................................................................................................ 39
5.2.1

Subtype Constraints ....................................................................................................................................... 39

5.2.2

Nullness Constraints ...................................................................................................................................... 39

5.2.3

Member Constraints ...................................................................................................................................... 40

5.2.4

Default Constructor Constraints .................................................................................................................... 40

5.2.5

Value Type Constraints .................................................................................................................................. 40

5.2.6

Reference Type Constraints ........................................................................................................................... 41

5.2.7

Enumeration Constraints ............................................................................................................................... 41

5.2.8

Delegate Constraints ..................................................................................................................................... 41

5.2.9

Unmanaged Constraints ................................................................................................................................ 42

5.2.10Equality and Comparison Constraints ............................................................................................................ 42

5.3

TYPE PARAMETER DEFINITIONS ......................................................................................................................................... 42

5.4

LOGICAL PROPERTIES OF TYPES ......................................................................................................................................... 43
5.4.1

Characteristics of Type Definitions ................................................................................................................ 43

5.4.2

Expanding Abbreviations and Inference Equations ....................................................................................... 44

5.4.3

Type Variables and Definition Sites ............................................................................................................... 45

5.4.4

Base Type of a Type ....................................................................................................................................... 452

5.4.5Interfaces Types of a Type ............................................................................................................................. 46

5.4.6

Type Equivalence ........................................................................................................................................... 46

5.4.7

Subtyping and Coercion ................................................................................................................................. 46

5.4.8

Nullness ......................................................................................................................................................... 47

5.4.9

Default Initialization ...................................................................................................................................... 48

5.4.10

Dynamic Conversion Between Types ............................................................................................................. 48

EXPRESSIONS ........................................................................................................................................................51
6.1

SOME CHECKING AND INFERENCE TERMINOLOGY .................................................................................................................. 53

6.2

ELABORATION AND ELABORATED EXPRESSIONS ..................................................................................................................... 54

6.3

DATA EXPRESSIONS ........................................................................................................................................................ 55

6.4

6.5

6.3.1

Simple Constant Expressions ......................................................................................................................... 56

6.3.2

Tuple Expressions .......................................................................................................................................... 56

6.3.3

List Expressions .............................................................................................................................................. 57

6.3.4

Array Expressions .......................................................................................................................................... 58

6.3.5

Record Expressions ........................................................................................................................................ 58

6.3.6

Copy-and-update Record Expressions............................................................................................................ 59

6.3.7

Function Expressions ..................................................................................................................................... 60

6.3.8

Object Expressions ......................................................................................................................................... 60

6.3.9

Delayed Expressions ...................................................................................................................................... 62

6.3.10Computation Expressions .............................................................................................................................. 62

6.3.11

Sequence Expressions .................................................................................................................................... 66

6.3.12Range Expressions ......................................................................................................................................... 66

6.3.13

Lists via Sequence Expressions ....................................................................................................................... 67

6.3.14

Arrays Sequence Expressions ......................................................................................................................... 67

6.3.15Null Expressions ............................................................................................................................................. 68

6.3.16'printf' Formats .............................................................................................................................................. 68

APPLICATION EXPRESSIONS .............................................................................................................................................. 69
6.4.1

Basic Application Expressions ........................................................................................................................ 69

6.4.2

Object Construction Expressions .................................................................................................................... 70

6.4.3

Operator Expressions ..................................................................................................................................... 71

6.4.4

Dynamic Operator Expressions ...................................................................................................................... 72

6.4.5

The AddressOf Operators .............................................................................................................................. 73

6.4.6

Lookup Expressions ........................................................................................................................................ 73

6.4.7

Slice Expressions ............................................................................................................................................ 74

6.4.8

Member Constraint Invocation Expressions .................................................................................................. 74

6.4.9

Assignment Expressions................................................................................................................................. 75

CONTROL FLOW EXPRESSIONS .......................................................................................................................................... 76
6.5.1

Parenthesized and Block Expressions ............................................................................................................ 76

6.5.2

Sequential Execution Expressions .................................................................................................................. 77

6.5.3

Conditional Expressions ................................................................................................................................. 77

6.5.4

Shortcut Operator Expressions ...................................................................................................................... 77

6.5.5

Pattern-Matching Expressions and Functions ............................................................................................... 78

6.5.6

Sequence Iteration Expressions ..................................................................................................................... 78

6.5.7

Simple for-Loop Expressions .......................................................................................................................... 79

6.5.8

While Expressions .......................................................................................................................................... 79

6.5.9

Try-with Expressions ...................................................................................................................................... 80

6.5.10

Reraise Expressions........................................................................................................................................ 80

6.5.11

Try-finally Expressions ................................................................................................................................... 80

3

6.5.12
6.6

6.7

6.8

6.9

Assertion Expressions .................................................................................................................................... 81

DEFINITION EXPRESSIONS ................................................................................................................................................ 81
6.6.1

Value Definition Expressions .......................................................................................................................... 82

6.6.2

Function Definition Expressions ..................................................................................................................... 82

6.6.3

Recursive Definition Expressions.................................................................................................................... 83

6.6.4

Deterministic Disposal Expressions ................................................................................................................ 83

TYPE-RELATED EXPRESSIONS ............................................................................................................................................ 84
6.7.1

Type-Annotated Expressions ......................................................................................................................... 84

6.7.2

Static Coercion Expressions ........................................................................................................................... 84

6.7.3

Dynamic Type-Test Expressions ..................................................................................................................... 84

6.7.4

Dynamic Coercion Expressions....................................................................................................................... 84

QUOTED EXPRESSIONS .................................................................................................................................................... 85
6.8.1

Strongly Typed Quoted Expressions ............................................................................................................... 86

6.8.2

Weakly Typed Quoted Expressions ................................................................................................................ 86

6.8.3

Expression Splices .......................................................................................................................................... 86

EVALUATION OF ELABORATED FORMS ................................................................................................................................ 87
6.9.1

Values and Execution Context ....................................................................................................................... 87

6.9.2

Parallel Execution and Memory Model .......................................................................................................... 88

6.9.3

Zero Values .................................................................................................................................................... 89

6.9.4

Taking the Address of an Elaborated Expression ........................................................................................... 89

6.9.5

Evaluating Value References ......................................................................................................................... 90

6.9.6

Evaluating Function Applications .................................................................................................................. 90

6.9.7

Evaluating Method Applications.................................................................................................................... 90

6.9.8

Evaluating Union Cases ................................................................................................................................. 91

6.9.9

Evaluating Field Lookups ............................................................................................................................... 91

6.9.10Evaluating Array Expressions ......................................................................................................................... 91

6.9.11

Evaluating Record Expressions ...................................................................................................................... 91

6.9.12

Evaluating Function Expressions.................................................................................................................... 91

6.9.13

Evaluating Object Expressions ....................................................................................................................... 91

6.9.14

Evaluating Definition Expressions .................................................................................................................. 92

6.9.15

Evaluating Integer For Loops ......................................................................................................................... 92

6.9.16

Evaluating While Loops ................................................................................................................................. 92

6.9.17

Evaluating Static Coercion Expressions.......................................................................................................... 92

6.9.18

Evaluating Dynamic Type-Test Expressions ................................................................................................... 92

6.9.19Evaluating Dynamic Coercion Expressions ..................................................................................................... 93

6.9.20Evaluating Sequential Execution Expressions ................................................................................................ 93

6.9.21

Evaluating Try-with Expressions .................................................................................................................... 93

6.9.22

Evaluating Try-finally Expressions ................................................................................................................. 94

6.9.23

Evaluating AddressOf Expressions ................................................................................................................. 94

6.9.24

Values with Underspecified Object Identity and Type Identity ...................................................................... 94

PATTERNS .............................................................................................................................................................97
7.1

SIMPLE CONSTANT PATTERNS........................................................................................................................................... 98

7.2

NAMED PATTERNS ......................................................................................................................................................... 98
7.2.1

Union Case Patterns ...................................................................................................................................... 99

7.2.2

Literal Patterns .............................................................................................................................................. 99

7.2.3

Active Patterns ............................................................................................................................................ 100

7.3“AS” PATTERNS .......................................................................................................................................................... 101

7.4

WILDCARD PATTERNS ................................................................................................................................................... 102

4

7.5

DISJUNCTIVE PATTERNS ................................................................................................................................................ 102

7.6

CONJUNCTIVE PATTERNS ............................................................................................................................................... 102

7.7

LIST PATTERNS ............................................................................................................................................................ 102

7.8

TYPE-ANNOTATED PATTERNS ......................................................................................................................................... 103

7.9

DYNAMIC TYPE-TEST PATTERNS ...................................................................................................................................... 103

7.10RECORD PATTERNS ................................................................................................................................................. 104

7.11

ARRAY PATTERNS ................................................................................................................................................... 104

7.12

NULL PATTERNS ..................................................................................................................................................... 105

7.13

GUARDED PATTERN RULES ....................................................................................................................................... 105

TYPE DEFINITIONS ............................................................................................................................................... 107
8.1

TYPE DEFINITION GROUP CHECKING AND ELABORATION ....................................................................................................... 110

8.2

TYPE KIND INFERENCE .................................................................................................................................................. 112

8.3

TYPE ABBREVIATIONS ................................................................................................................................................... 113

8.4

RECORD TYPE DEFINITIONS ............................................................................................................................................ 114
8.4.1

Members in Record Types............................................................................................................................ 114

8.4.2

Name Resolution and Record Field Labels ................................................................................................... 114

8.4.3

Structural Hashing, Equality, and Comparison for Record Types ................................................................. 115

8.4.4

With/End in Record Type Definitions ........................................................................................................... 115

8.5

UNION TYPE DEFINITIONS ............................................................................................................................................. 115
8.5.1

Members in Union Types ............................................................................................................................. 116

8.5.2

Structural Hashing, Equality, and Comparison for Union Types .................................................................. 116

8.5.3

With/End in Union Type Definitions............................................................................................................. 116

8.5.4

Compiled Form of Union Types for Use from Other CLI Languages ............................................................. 116

8.6

CLASS TYPE DEFINITIONS ............................................................................................................................................... 117
8.6.1

Primary Constructors in Classes ................................................................................................................... 117

8.6.2

Members in Classes ..................................................................................................................................... 120

8.6.3

Additional Object Constructors in Classes.................................................................................................... 120

8.6.4

Additional Fields in Classes .......................................................................................................................... 122

8.7

INTERFACE TYPE DEFINITIONS ......................................................................................................................................... 123

8.8

STRUCT TYPE DEFINITIONS ............................................................................................................................................. 123

8.9

ENUM TYPE DEFINITIONS .............................................................................................................................................. 125

8.10

DELEGATE TYPE DEFINITIONS .................................................................................................................................... 126

8.11

EXCEPTION DEFINITIONS .......................................................................................................................................... 126

8.12

TYPE EXTENSIONS ................................................................................................................................................... 127

8.12.1
8.13

Imported CLI C# Extensions Members ......................................................................................................... 128
MEMBERS ............................................................................................................................................................ 129

8.13.1

Property Members ....................................................................................................................................... 130

8.13.2

Method Members ........................................................................................................................................ 131

8.13.3Curried Method Members ........................................................................................................................... 131

8.13.4

Named Arguments to Method Members ..................................................................................................... 131

8.13.5

Optional Arguments to Method Members .................................................................................................. 132

8.13.6Type-directed Conversions at Member Invocations ..................................................................................... 134

8.13.7

Overloading of Methods .............................................................................................................................. 135

8.13.8

Naming Restrictions for Members ............................................................................................................... 136

8.13.9

Members Represented as Events ................................................................................................................. 136

8.13.108.14
8.14.1

Members Represented as Static Members ............................................................................................. 137

ABSTRACT MEMBERS AND INTERFACE IMPLEMENTATIONS ............................................................................................... 137
Abstract Members ....................................................................................................................................... 138

5

8.14.2

Members that Implement Abstract Members ............................................................................................. 138

8.14.3

Interface Implementations .......................................................................................................................... 140

8.15

EQUALITY, HASHING, AND COMPARISON ..................................................................................................................... 142

8.15.1Equality Attributes ....................................................................................................................................... 143

8.15.2Comparison Attributes ................................................................................................................................. 143

8.15.3Behavior of the Generated Object.Equals Implementation ......................................................................... 144

8.15.4Behavior of the Generated CompareTo Implementations ........................................................................... 145

8.15.5Behavior of the Generated GetHashCode Implementations ........................................................................ 145

8.15.6

Behavior of Hash, =, and Compare .............................................................................................................. 145

UNITS OF MEASURE ............................................................................................................................................ 149
9.1

MEASURES................................................................................................................................................................. 150

9.2

CONSTANTS ANNOTATED BY MEASURES ........................................................................................................................... 151

9.3

RELATIONS ON MEASURES ............................................................................................................................................. 151
9.3.1

Constraint Solving ........................................................................................................................................ 152

9.3.2

Generalization of Measure Variables .......................................................................................................... 152

9.4

MEASURE DEFINITIONS ................................................................................................................................................. 152

9.5

MEASURE PARAMETER DEFINITIONS ................................................................................................................................ 153

9.6

MEASURE PARAMETER ERASURE..................................................................................................................................... 153

9.7

TYPE DEFINITIONS WITH MEASURES IN THE F# CORE LIBRARY................................................................................................ 153
NAMESPACES AND MODULES ............................................................................................................................. 155

10.1

NAMESPACE DECLARATION GROUPS ........................................................................................................................... 155

10.2

MODULE DEFINITIONS ............................................................................................................................................. 157

10.2.1

Function and Value Definitions in Modules ................................................................................................. 158

10.2.2

Literal Definitions in Modules ...................................................................................................................... 159

10.2.3

Type Function Definitions in Modules .......................................................................................................... 159

10.2.4

Active Pattern Definitions in Modules ......................................................................................................... 160

10.2.5“do” statements in Modules ........................................................................................................................ 160

10.3

IMPORT DECLARATIONS ........................................................................................................................................... 161

10.4

MODULE ABBREVIATIONS ........................................................................................................................................ 161

10.5

ACCESSIBILITY ANNOTATIONS .................................................................................................................................... 161

NAMESPACE AND MODULE SIGNATURES ............................................................................................................ 165
11.1

SIGNATURE ELEMENTS............................................................................................................................................. 166

11.1.1

Value Signatures .......................................................................................................................................... 166

11.1.2Type Definition and Member Signatures ..................................................................................................... 166

11.2

SIGNATURE CONFORMANCE...................................................................................................................................... 167

11.2.1Signature Conformance for Functions and Values ....................................................................................... 167

11.2.2Signature Conformance for Members ......................................................................................................... 169

PROGRAM STRUCTURE AND EXECUTION ............................................................................................................. 171
12.1

IMPLEMENTATION FILES ........................................................................................................................................... 172

12.2

SIGNATURE FILES ................................................................................................................................................... 173

12.3

SCRIPT FILES ......................................................................................................................................................... 173

12.4

COMPILER DIRECTIVES ............................................................................................................................................. 174

12.5

PROGRAM EXECUTION............................................................................................................................................. 175

12.5.1

Execution of Static Initializers ...................................................................................................................... 175

12.5.2

Explicit Entry Point ....................................................................................................................................... 177

CUSTOM ATTRIBUTES AND REFLECTION .............................................................................................................. 179

6

13.1
13.1.1
13.2

CUSTOM ATTRIBUTES .............................................................................................................................................. 179
Custom Attributes and Signatures ............................................................................................................... 180
REFLECTED FORMS OF DECLARATION ELEMENTS ............................................................................................................ 181

INFERENCE PROCEDURES .................................................................................................................................... 183
14.1

NAME RESOLUTION ................................................................................................................................................ 183

14.1.1Name Environments .................................................................................................................................... 183

14.1.2Name Resolution in Module and Namespace Paths .................................................................................... 183

14.1.3

Opening Modules and Namespace Declaration Groups .............................................................................. 184

14.1.4

Name Resolution in Expressions .................................................................................................................. 185

14.1.5

Name Resolution for Members .................................................................................................................... 187

14.1.6

Name Resolution in Patterns ....................................................................................................................... 188

14.1.7

Name Resolution for Types .......................................................................................................................... 188

14.1.8Name Resolution for Type Variables ............................................................................................................ 189

14.1.914.2

Field Label Resolution .................................................................................................................................. 189
RESOLVING APPLICATION EXPRESSIONS ....................................................................................................................... 190

14.2.1

Unqualified Lookup ...................................................................................................................................... 190

14.2.2Item-Qualified Lookup ................................................................................................................................. 191

14.2.3

Expression-Qualified Lookup........................................................................................................................ 192

14.3

FUNCTION APPLICATION RESOLUTION ......................................................................................................................... 194

14.4

METHOD APPLICATION RESOLUTION .......................................................................................................................... 194

14.4.1

Conditional Compilation of Member Calls ................................................................................................... 199

14.4.2

Implicit Insertion of Flexibility for Uses of Functions and Members ............................................................ 199

14.5

CONSTRAINT SOLVING ............................................................................................................................................. 200

14.5.1

Solving Equational Constraints .................................................................................................................... 200

14.5.2

Solving Subtype Constraints ........................................................................................................................ 201

14.5.3

Solving Nullness, Struct, and Other Simple Constraints ............................................................................... 201

14.5.4

Solving Member Constraints........................................................................................................................ 202

14.5.5
14.6

Over-constrained User Type Annotations .................................................................................................... 203
CHECKING AND ELABORATING FUNCTION, VALUE, AND MEMBER DEFINITIONS ..................................................................... 203

14.6.1

Ambiguities in Function and Value Definitions ............................................................................................ 204

14.6.2

Mutable Value Definitions ........................................................................................................................... 204

14.6.3

Processing Value Definitions........................................................................................................................ 204

14.6.4

Processing Function Definitions ................................................................................................................... 205

14.6.5

Processing Recursive Groups of Definitions ................................................................................................. 205

14.6.6

Recursive Safety Analysis ............................................................................................................................. 206

14.6.7

Generalization ............................................................................................................................................. 208

14.6.8Condensation of Generalized Types ............................................................................................................. 210

14.7

DISPATCH SLOT INFERENCE ....................................................................................................................................... 211

14.8

DISPATCH SLOT CHECKING ....................................................................................................................................... 212

14.9

BYREF SAFETY ANALYSIS .......................................................................................................................................... 213

14.10

ARITY INFERENCE ................................................................................................................................................... 214

14.11

ADDITIONAL CONSTRAINTS ON CLI METHODS .............................................................................................................. 215

LEXICAL FILTERING .............................................................................................................................................. 217
15.1

LIGHTWEIGHT SYNTAX ............................................................................................................................................. 217

15.1.1

Basic Lightweight Syntax Rules by Example ................................................................................................ 217

15.1.2Inserted Tokens............................................................................................................................................ 218

15.1.3

Grammar Rules Including Inserted Tokens .................................................................................................. 218

15.1.4

Offside Lines ................................................................................................................................................ 219
7

15.1.5

The Pre-Parse Stack ..................................................................................................................................... 220

15.1.6

Full List of Offside Contexts.......................................................................................................................... 220

15.1.7

Balancing Rules ........................................................................................................................................... 221

15.1.8

Offside Tokens, Token Insertions, and Closing Contexts .............................................................................. 222

15.1.9Exceptions to the Offside Rules.................................................................................................................... 223

15.1.10Permitted Undentations ......................................................................................................................... 224

15.2

HIGH PRECEDENCE APPLICATION ............................................................................................................................... 225

15.3

LEXICAL ANALYSIS OF TYPE APPLICATIONS .................................................................................................................... 226

SPECIAL ATTRIBUTES AND TYPES ......................................................................................................................... 227
16.1

CUSTOM ATTRIBUTES RECOGNIZED BY F# .................................................................................................................... 227

16.2

CUSTOM ATTRIBUTES EMITTED BY F# ......................................................................................................................... 234

16.3

CUSTOM ATTRIBUTES NOT RECOGNIZED BY F# ............................................................................................................. 234

16.4

EXCEPTIONS THROWN BY F# LANGUAGE PRIMITIVES ...................................................................................................... 235

THE F# LIBRARY FSHARP.CORE.DLL ...................................................................................................................... 237
17.1

BASIC TYPES (MICROSOFT.FSHARP.CORE) ................................................................................................................... 237

17.1.1

Basic Type Abbreviations ............................................................................................................................. 237

17.1.2

Basic Types that Accept Unit of Measure Annotations ................................................................................ 238

17.1.3The nativeptr<_> Type ................................................................................................................................. 238

17.2

BASIC OPERATORS AND FUNCTIONS (MICROSOFT.FSHARP.CORE.OPERATORS) .................................................................... 238

17.2.1

Basic Arithmetic Operators .......................................................................................................................... 238

17.2.2

Generic Equality and Comparison Operators............................................................................................... 239

17.2.3

Bitwise Operators ........................................................................................................................................ 239

17.2.4

Math Operators ........................................................................................................................................... 240

17.2.5Function Pipelining and Composition Operators ......................................................................................... 241

17.2.6

Object Transformation Operators ............................................................................................................... 241

17.2.7

Pair Operators ............................................................................................................................................. 241

17.2.8

Exception Operators .................................................................................................................................... 241

17.2.9Input/Output Handles.................................................................................................................................. 242

17.2.10Overloaded Conversion Functions .......................................................................................................... 242

17.3

CHECKED ARITHMETIC OPERATORS ............................................................................................................................ 243

17.4

LIST AND OPTION TYPES .......................................................................................................................................... 244

17.4.1

The List Type ................................................................................................................................................ 244

17.4.2The Option Type........................................................................................................................................... 244

17.5

LAZY COMPUTATIONS (LAZY) .................................................................................................................................... 244

17.6

ASYNCHRONOUS COMPUTATIONS (ASYNC) .................................................................................................................. 244

17.7

AGENTS (MAILBOXPROCESSOR) ................................................................................................................................ 244

17.8

EVENT TYPES ......................................................................................................................................................... 245

17.9

IMMUTABLE COLLECTION TYPES (MAP, SET) ................................................................................................................ 245

17.10

TEXT FORMATTING (PRINTF) ..................................................................................................................................... 245

17.11

REFLECTION .......................................................................................................................................................... 245

17.12

QUOTATIONS ........................................................................................................................................................ 245

17.13

NATIVE POINTER OPERATIONS .................................................................................................................................. 245

17.13.1

Stack Allocation ...................................................................................................................................... 246

FEATURES FOR ML COMPATIBILITY ..................................................................................................................... 247
18.1

CONDITIONAL COMPILATION FOR ML COMPATIBILITY .................................................................................................... 247

18.2

EXTRA SYNTACTIC FORMS FOR ML COMPATIBILITY ........................................................................................................ 247

18.3

EXTRA OPERATORS ................................................................................................................................................. 248

8

18.4

FILE EXTENSIONS AND LEXICAL MATTERS ..................................................................................................................... 248

APPENDIX A: F# GRAMMAR SUMMARY ........................................................................................................................ 249
REFERENCES .................................................................................................................................................................. 269
GLOSSARY ..................................................................................................................................................................... 270
INDEX ............................................................................................................................................................................ 281

9Introduction
F# is a scalable, succinct, type-safe, type-inferred, efficiently executing functional/imperative/object-oriented
programming language. It aims to be the premier typed functional programming language for the .NET frameworkand other implementations of the Ecma 335 Common Language Infrastructure (CLI) specification. F# was partly
inspired by the OCaml language and shares some common core constructs with it.

1.1 A First Program
Over the next few sections, we will look at some small F# programs, describing some important aspects of F#
along the way. As an introduction to F#, consider the following program:let numbers = [ 1 .. 10 ]let square x = x * x
let squares = List.map square numbers
printfn "N^2 = %A" squares
To explore this program, you can:Compile it as a project in a development environment such as Visual Studio.



Manually invoke the F# command line compiler fsc.exe.

Use F# Interactive, the dynamic compiler that is part of the F# distribution.

1.1.1 Lightweight Syntax
The F# language uses simplified, indentation-aware syntactic constructs known as lightweight syntax. The linesof the sample program in the previous section form a sequence of declarations and are aligned on the same
column. For example, the two lines in the following code are two separate declarations:
let squares = List.map square numbers
printfn "N^2 = %A" squares
Lightweight syntax applies to all the major constructs of the F# syntax. In the next example, the code isincorrectly aligned. The declaration starts in the first line and continues to the second and subsequent lines, so
those lines must be indented to the same column under the first line:
let computeDerivative f x =let p1 = f (x - 0.05)
let p2 = f (x + 0.05)(p2 - p1) / 0.1
The following shows the correct alignment:
let computeDerivative f x =let p1 = f (x - 0.05)
let p2 = f (x + 0.05)(p2 - p1) / 0.1
The use of lightweight syntax is the default for all F# code in files with the extension .fs, .fsx, .fsi, or.fsscript.

1.1.2 Making Data Simple
The first line in our sample simply declares a list of numbers from one through ten.
let numbers = [1 .. 10]
An F# list is an immutable linked list, which is a type of data used extensively in functional programming. Some
operators that are related to lists include :: to add an item to the front of a list and @ to concatenate two lists. If
we try these operators in F# Interactive, we see the following results:> let vowels = ['e'; 'i'; 'o'; 'u'];;val vowels: char list = ['e'; 'i'; 'o'; 'u']
> ['a'] @ vowels;;
val it: char list = ['a'; 'e'; 'i'; 'o'; 'u']> vowels @ ['y'];;val it: char list = ['e'; 'i'; 'o'; 'u'; 'y']Note that double semicolons delimit lines in F# Interactive, and that F# Interactive prefaces the result with val toindicate that the result is an immutable value, rather than a variable.
F# supports several other highly effective techniques to simplify the process of modeling and manipulating datasuch as tuples, options, records, unions, and sequence expressions. A tuple is an ordered collection of values
that is treated as an atomic unit. In many languages, if you want to pass around a group of related values as asingle entity, you need to create a named type, such as a class or record, to store these values. A tuple allows
you to keep things organized by grouping related values together, without introducing a new type.
To define a tuple, you separate the individual components with commas.
> let tuple = (1, false, "text");;val tuple : int * bool * string = (1, false, "text")> let getNumberInfo (x : int) = (x, x.ToString(), x * x);;val getNumberInfo : int -> int * string * int> getNumberInfo 42;;
val it : int * string * int = (42, "42", 1764)
A key concept in F# is immutability. Tuples and lists are some of the many types in F# that are immutable, andindeed most things in F# are immutable by default. Immutability means that once a value is created and given a
name, the value associated with the name cannot be changed. Immutability has several benefits. Most notably, it
prevents many classes of bugs, and immutable data is inherently thread-safe, which makes the process ofparallelizing code simpler.

1.1.3 Making Types Simple
The next line of the sample program defines a function called square, which squares its input.
let square x = x * x
Most statically-typed languages require that you specify type information for a function declaration. However, F#
typically infers this type information for you. This process is referred to as type inference.
From the function signature, F# knows that square takes a single parameter named x and that the function
returns x * x. The last thing evaluated in an F# function body is the return value; hence there is no “return”
keyword here. Many primitive types support the multiplication (*) operator (such as byte, uint64, and double);however, for arithmetic operations, F# infers the type int (a signed 32-bit integer) by default.

12

Although F# can typically infer types on your behalf, occasionally you must provide explicit type annotations in F#
code. For example, the following code uses a type annotation for one of the parameters to tell the compiler thetype of the input.
> let concat (x : string) y = x + y;;val concat : string -> string -> string
Because x is stated to be of type string, and the only version of the + operator that accepts a left-handargument of type string also takes a string as the right-hand argument, the F# compiler infers that theparameter y must also be a string. Thus, the result of x + y is the concatenation of the strings. Without the type
annotation, the F# compiler would not have known which version of the + operator was intended and would haveassumed int data by default.
The process of type inference also applies automatic generalization to declarations. This automatically makes
code generic when possible, which means the code can be used on many types of data. For example, thefollowing code defines a function that returns a new tuple in which the two values are swapped:> let swap (x, y) = (y, x);;val swap : 'a * 'b -> 'b * 'a
> swap (1, 2);;
val it : int * int = (2, 1)> swap ("you", true);;val it : bool * string = (true,"you")
Here the function swap is generic, and 'a and 'b represent type variables, which are placeholders for types ingeneric code. Type inference and automatic generalization greatly simplify the process of writing reusable codefragments.

1.1.4 Functional Programming
Continuing with the sample, we have a list of integers named numbers, and the square function, and we want tocreate a new list in which each item is the result of a call to our function. This is called mapping our function overeach item in the list. The F# library function List.map does just that:let squares = List.map square numbers
Consider another example:> List.map (fun x -> x % 2 = 0) [1 .. 5];;val it : bool list
= [false; true; false; true; false]
The code (fun x -> x % 2 = 0) defines an anonymous function, called a function expression, that takes asingle parameter x and returns the result x % 2 = 0, which is a Boolean value that indicates whether x is even.
The -> symbol separates the argument list (x) from the function body (x % 2 = 0).
Both of these examples pass a function as a parameter to another function—the first parameter to List.map isitself another function. Using functions as function values is a hallmark of functional programming.
Another tool for data transformation and analysis is pattern matching. This powerful switch construct allows you
to branch control flow and to bind new values. For example, we can match an F# list against a sequence of list
elements.
let checkList alist =match alist with| [] -> 0| [a] -> 1
| [a; b] -> 213

| [a; b; c] -> 3
| _ -> failwith "List is too big!"In this example, alist is compared with each potentially matching pattern of elements. When alist matches apattern, the result expression is evaluated and is returned as the value of the match expression. Here,the -> operator separates a pattern from the result that a match returns.
Pattern matching can also be used as a control construct—for example, by using a pattern that performs adynamic type test:
let getType (x : obj) =match x with
| :? string-> "x is a string"| :? int-> "x is an int"
| :? System.Exception -> "x is an exception"
The :? operator returns true if the value matches the specified type, so if x is a string, getType returns“x is a string”.
Function values can also be combined with the pipeline operator, |>. For example, given these functions:
let square x= x * xlet toStr (x : int) = x.ToString()
let reverse (x : string) = new System.String(Array.rev (x.ToCharArray()))
We can use the functions as values in a pipeline:> let result = 32 |> square |> toStr |> reverse;;
val it : string = "4201"
Pipelining demonstrates one way in which F# supports compositionality, a key concept in functional
programming. The pipeline operator simplifies the process of writing compositional code where the result of one
function is passed into the next.

1.1.5 Imperative Programming
The next line of the sample program prints text in the console window.
printfn "N^2 = %A" squares
The F# library function printf is a simple and type-safe way to print text in the console window. Consider thisexample, which prints an integer, a floating-point number, and a string:> printfn "%d * %f = %s" 5 0.75 ((5.0 * 0.75).ToString());;
5 * 0.750000 = 3.75
val it : unit = ()The format specifiers %d, %f, and %s are placeholders for integers, floats, and strings. The %A format can be used
to print arbitrary data types (including lists).
The printfn function is an example of imperative programming, which means calling functions for their side
effects. Other commonly used imperative programming techniques include arrays and dictionaries (also calledhash tables). F# programs typically use a mixture of functional and imperative techniques.

1.1.6 .NET Interoperability and CLI Fidelity
The last line in the sample program calls the common language infrastructure (CLI) function
System.Console.ReadKey to pause the program before the console window closes.
System.Console.ReadKey(true)
Because F# is built on top of CLI implementations, you can call any CLI library from F#. Furthermore, other CLI
languages can easily use any F# components.

14

1.1.7 Parallel and Asynchronous Programming
F# is both a parallel and a reactive language. During execution, F# programs can have multiple parallel active
evaluations and multiple pending reactions, such as callbacks and agents that wait to react to events andmessages.
One way to write parallel and reactive F# programs is to use F# async expressions. For example, the code below
is similar to the original program in §1.1 except that it computes the Fibonacci function (using a technique that willtake some time) and schedules the computation of the numbers in parallel:
let rec fib x = if x <= 2 then 1 else fib(x-1) + fib(x-2)
let fibs =Async.Parallel [ for i in 0..40 -> async { return fib(i) } ]|> Async.RunSynchronously
printfn "N^2 = %A" fibs
System.Console.ReadKey(true)
The preceding code sample shows multiple, parallel, CPU-bound computations.
F# is also a reactive language. The following example requests multiple web pages in parallel, reacts to the
responses for each request, and finally returns the collected results.
open Systemopen System.IO
open System.Net
let http url =async { let req = WebRequest.Create(Uri url)
use! resp = req.AsyncGetResponse()
use stream = resp.GetResponseStream()use reader = new StreamReader(stream)
let contents = reader.ReadToEnd()return contents }let sites = ["http://www.bing.com"; "http://www.google.com";"http://www.yahoo.com"; "http://www.search.com"]
let htmlOfSites =Async.Parallel [for site in sites -> http site ]|> Async.RunSynchronously
By using asynchronous workflows together with other CLI libraries, F# programs can implement parallel tasks,
parallel I/O operations, and message-receiving agents.

1.1.8 Strong Typing for Floating-Point CodeF# applies type checking and type inference to floating-point-intensive domains through units of measureinference and checking. This feature allows you to type-check programs that manipulate floating-point numbersthat represent physical and abstract quantities in a stronger way than other typed languages, without losing any
performance in your compiled code. You can think of this feature as providing a type system for floating-point
code.
Consider the following example:[<Measure>] type kg[<Measure>] type m[<Measure>] type s15

let gravityOnEarth = 9.81<m/s^2>
let heightOfTowerOfPisa = 55.86<m>let speedOfImpact = sqrt(2.0 * gravityOnEarth * heightOfTowerOfPisa)
The Measure attribute tells F# that kg, s, and m are not really types in the usual sense of the word, but are used
to build units of measure. Here speedOfImpact is inferred to have type float<m/s>.

1.1.9 Object-Oriented Programming and Code Organization
The sample program shown at the start of this chapter is a script. Although scripts are excellent for rapid
prototyping, they are not suitable for larger software components. F# supports the transition from scripting tostructured code through several techniques.
The most important of these is object-oriented programming through the use of class type definitions, interface
type definitions, and object expressions. Object-oriented programming is a primary application programming
interface (API) design technique for controlling the complexity of large software projects. For example, here is aclass definition for an encoder/decoder object.
open System/// Build an encoder/decoder object that maps characters to an/// encoding and back. The encoding is specified by a sequence/// of character pairs, for example, [('a','Z'); ('Z','a')]
type CharMapEncoder(symbols: seq<char*char>) =let swap (x, y) = (y, x)/// An immutable tree map for the encoding
let fwd = symbols |> Map.ofSeq/// An immutable tree map for the decoding
let bwd = symbols |> Seq.map swap |> Map.ofSeq
let encode (s:string) =String [| for c in s -> if fwd.ContainsKey(c) then fwd.[c] else c |]let decode (s:string) =String [| for c in s -> if bwd.ContainsKey(c) then bwd.[c] else c |]/// Encode the input string
member x.Encode(s) = encode s/// Decode the given string
member x.Decode(s) = decode s
You can instantiate an object of this type as follows:
let rot13 (c:char) =char(int 'a' + ((int c - int 'a' + 13) % 26))
let encoder =CharMapEncoder( [for c in 'a'..'z' -> (c, rot13 c)])And use the object as follows:> "F# is fun!" |> encoder.Encode ;;val it : string = "F# vf sha!"> "F# is fun!" |> encoder.Encode |> encoder.Decode ;;val it : String = "F# is fun!"An interface type can encapsulate a family of object types:
open System

16

type IEncoding =abstract Encode : string -> string
abstract Decode : string -> string
In this example, IEncoding is an interface type that includes both Encode and Decode object types.
Both object expressions and type definitions can implement interface types. For example, here is an object
expression that implements the IEncoding interface type:
let nullEncoder ={ new IEncoding withmember x.Encode(s) = s
member x.Decode(s) = s }Modules are a simple way to encapsulate code during rapid prototyping when you do not want to spend the time
to design a strict object-oriented type hierarchy. In the following example, we place a portion of our original script
in a module.
module ApplicationLogic =let numbers n = [1 .. n]
let square x = x * x
let squares n = numbers n |> List.map squareprintfn "Squares up to 5 = %A" (ApplicationLogic.squares 5)
printfn "Squares up to 10 = %A" (ApplicationLogic.squares 10)
System.Console.ReadKey(true)
Modules are also used in the F# library design to associate extra functionality with types. For example, List.map
is a function in a module.
Other devices aimed at supporting software engineering include signatures, which can be used to give explicit
types to components, and namespaces, which serve as a way of organizing the name hierarchies for larger APIs.

1.2 Notational Conventions in This Specification
This specification describes the F# language by using a mixture of informal and semiformal techniques. All
examples in this specification use lightweight syntax, unless otherwise specified.
Regular expressions are given in the usual notation, as shown in the table:
Notation

Meaning

regexp+One or more occurrences

regexp*Zero or more occurrences

regexp?

Zero or one occurrences[ char - char ]Range of ASCII characters[ ^ char - char ]Any characters except those in the range

Unicode character classes are referred to by their abbreviation—for example, \Lu refers to any uppercase letter.
The following characters are referred to using the indicated notation:
Character

Name

Notation\bbackspace

ASCII/UTF-8/UTF-16/UTF-32 code 08\nnewlineASCII/UTF-8/UTF-16/UTF-32 code 10

17

Character

Name

Notation\r

returnASCII/UTF-8/UTF-16/UTF-32 code 13

\t

tab

ASCII/UTF-8/UTF-16/UTF-32 code 09

Strings of characters that are clearly not a regular expression are written verbatim. Therefore, the following string
abstract
matches precisely the characters abstract.
Where appropriate, apostrophes and quotation marks enclose symbols that are used in the specification of thegrammar itself, such as '<' and '|'. For example, the following regular expression matches (+) or (-):'(' (+|-) ')'This regular expression matches precisely the characters #if:"#if"
Regular expressions are typically used to specify tokens.
token token-name =regexpIn the grammar rules, the notation element-nameopt indicates an optional element. The notation ... indicates
repetition of the preceding non-terminal construct and the separator token. For example, expr ',' ... ','expr means a sequence of one or more expr elements separated by commas.

18

Program Structure
The inputs to the F# compiler or the F# Interactive dynamic compiler consist of:

Source code files, with extensions .fs, .fsi, .fsx, or .fsscript.
Files with extension .fs must conform to grammar element implementation-file in §12.1.

Files with extension .fsi must conform to grammar element signature-file in §12.2.



Files with extension .fsx or .fsscript must conform to grammar element script-file in §12.3.

Script fragments (for F# Interactive). These must conform to grammar element script-fragment. Script
fragments can be separated by ;; tokens.



Assembly references that are specified by command line arguments or interactive directives.



Compilation parameters that are specified by command line arguments or interactive directives.



Compiler directives such as #time.

The COMPILED compilation symbol is defined for input that the F# compiler has processed. The INTERACTIVE
compilation symbol is defined for input that F# Interactive has processed.
Processing the source code portions of these inputs consists of the following steps:
1.

Decoding. Each file and source code fragment is decoded into a stream of Unicode characters, as
described in the C# specification, sections 2.3 and 2.4. The command-line options may specify a code pagefor this process.

2.

Tokenization. The stream of Unicode characters is broken into a token stream by the lexical analysis
described in §3.

3.

Lexical Filtering. The token stream is filtered by a state machine that implements the rules describedin §15. Those rules describe how additional (artificial) tokens are inserted into the token stream and how
some existing tokens are replaced with others to create an augmented token stream.

4.

Parsing. The augmented token stream is parsed according to the grammar specification in this document.

5.

Importing. The imported assembly references are resolved to F# or CLI assembly specifications, which arethen imported. From the F# perspective, this results in the pre-definition of numerous namespace declarationgroups (§12.1) and types. The namespace declaration groups are then combined to form an initial nameresolution environment (§14.1).

6.

Checking. The results of parsing are checked one by one. Checking involves such procedures as Name
Resolution (§14.1), Constraint Solving (§14.5), and Generalization (§14.6.7), as well as the application ofother rules described in this specification.
Type inference uses variables to represent unknowns in the type inference problem. The various checking
processes maintain tables of context information including a name resolution environment and a set ofcurrent inference constraints. After the processing of a file or program fragment is complete, all suchvariables have been either generalized or resolved and the type inference environment is discarded.

7.

Elaboration. One result of checking is an elaborated program fragment that contains elaborated
declarations, expressions, and types. For most constructs, such as constants, control flow, and data
expressions, the elaborated form is simple. Elaborated forms are used for evaluation, CLI reflection, and theF# expression trees that are returned by quoted expressions (§6.8).

8.

Execution. Elaborated program fragments that are successfully checked are added to a collection ofavailable program fragments. Each fragment has a static initializer. Static initializers are executed as
described in (§12.5).

20

Lexical Analysis
Lexical analysis converts an input stream of Unicode characters into a stream of tokens by iteratively processing
the stream. If more than one token can match a sequence of characters in the source file, lexical processing
always forms the longest possible lexical element. Some tokens, such as block-comment-start, are discarded
after processing as described later in this section.

3.1 Whitespace
Whitespace consists of spaces and newline characters.
regexp whitespace = ' '+regexp newline = '\n' | '\r' '\n'token whitespace-or-newline = whitespace | newline
Whitespace tokens whitespace-or-newline are discarded from the returned token stream.

3.2 Comments
Block comments are delimited by (* and *) and may be nested. Single-line comments begin with // and extend
to the end of the line.
token block-comment-start = "(*"
token block-comment-end = "*)"
token end-of-line-comment = "//" [^'\n' '\r']*
When the input stream matches a block-comment-start token, the subsequent text is tokenized recursively
against the tokens that are described in §3 until a block-comment-end token is found. The intermediate tokens
are discarded.
For example, comments can be nested, and strings that are embedded within comments are tokenized by therules for string and verbatim-string. In particular, strings that are embedded in comments are tokenized intheir entirety, without considering closing *) marks. As a result of this rule, the following is a valid comment:(* Here's a code snippet: let s = "*)" *)For the purposes of this specification, comment tokens are discarded from the returned lexical stream. In
practice, XML documentation tokens are end-of-line-comments that begin with ///. The delimiters are retained
and are associated with the remaining elements to generate XML documentation.

3.3 Conditional Compilation
The lexical preprocessing directives #if ident/#else/#endif delimit conditional compilation sections. The
following describes the grammar for such sections:token if-directive = "#if" whitespace ident-text
token else-directive = "#else"
token endif-directive = "#endif"
A preprocessing directive always occupies a separate line of source code and always begins with a # character
followed immediately by a preprocessing directive name, with no intervening whitespace. However, whitespacecan appear before the # character. A source line that contains the #if, #else, or #endif directive can end withwhitespace and a single-line comment. Multiple-line comments are not permitted on source lines that containpreprocessing directives.
If an if-directive token is matched during tokenization, text is recursively tokenized until a corresponding
else-directive or endif-directive. If the compilation environment defines the associated ident-text (forexample, by using the command line option –define), the token stream includes the tokens between the ifdirective and the corresponding else-directive or endif-directive. Otherwise, the tokens are discarded.
The converse applies to the text between any corresponding else-directive and the endif-directive.


In skipped text, #if ident/#else/#endif sections can be nested.

Strings and comments are not treated as special

3.4 Identifiers and Keywords
Identifiers follow the specification below. Any sequence of characters that is enclosed in double-backtick marks(`` ``), excluding newlines, tabs, and double-backtick pairs themselves, is treated as an identifier.
regexp digit-char = [0-9]regexp letter-char = '\Lu' | '\Ll' | '\Lt' | '\Lm' | '\Lo' | '\Nl'regexp connecting-char = '\Pc'
regexp combining-char = '\Mn' | '\Mc'
regexp formatting-char = '\Cf'
regexp ident-start-char =| letter-char| _regexp ident-char =| letter-char| digit-char| connecting-char| combining-char| formatting-char| '| _regexp ident-text = ident-start-char ident-char*token ident =| ident-text
For example, myName1| `` [^ '\n' '\r' '\t']+ | [^ '\n' '\r' '\t'] ``For example, ``value.with odd#name``
All input files are currently assumed to be encoded as UTF-8. See the C# specification for a list of the Unicodecharacters that are accepted for the Unicode character classes \Lu, \Li, \Lt, \Lm, \Lo, \Nl, \Pc, \Mn, \Mc, and \Cf.
The following identifiers are treated as keywords of the F# language:
token ident-keyword =abstract and as assert base begin class default delegate do done
downcast downto elif else end exception extern false finally forfun function global if in inherit inline interface internal lazy let
match member module mutable namespace new null of open or
override private public rec return sig static struct then totrue try type upcast use val void when while with yield
The following identifiers are reserved for future use:
token reserved-ident-keyword =atomic break checked component const constraint constructor

22

continue eager fixed fori functor include
measure method mixin object parallel params process protected purerecursive sealed tailcall trait virtual volatile
A future revision of the F# language may promote any of these identifiers to be full keywords.
The following token forms are reserved, except when they are part of a symbolic keyword (§3.6).
token reserved-ident-formats =| ident-text ( '!' | '#')In the remainder of this specification, we refer to the token that is generated for a keyword simply by using thetext of the keyword itself.

3.5 Strings and Characters
String literals may be specified for two types:
Unicode strings, type string = System.StringUnsigned byte arrays, type byte[] = bytearray

Literals may also be specified by using C#-like verbatim forms that interpret \ as a literal character rather than an
escape sequence. In a UTF-8-encoded file, you can directly embed the following in a string in the same way as inC#:Unicode characters, such as “\u0041bc”

Identifiers, as described in the previous section, such as “abc”

Trigraph specifications of Unicode characters, such as “\067” which represents “C”
regexp escape-char = '\' ["\'ntbr]
regexp non-escape-chars = '\' [^"\'ntbr]
regexp simple-char-char =| (any char except '\n' '\t' '\r' '\b' ' \ ")regexp unicodegraph-short = '\' 'u' hexdigit hexdigit hexdigit hexdigit
regexp unicodegraph-long = '\' 'U' hexdigit hexdigit hexdigit hexdigit
hexdigit hexdigit hexdigit hexdigit
regexp trigraph = '\' digit-char digit-char digit-char
regexp char-char =| simple-char-char| escape-char| trigraph| unicodegraph-shortregexp string-char =| simple-string-char| escape-char| non-escape-chars| trigraph| unicodegraph-short| unicodegraph-long| newline
regexp string-elem =| string-char| '\' newline whitespace* string-elem
token chartoken string==' char-char '" string-char* "23

regexp verbatim-string-char =| simple-string-char| non-escape-chars| newline| \| ""token verbatim-string= @" verbatim-string-char* "token bytechar= ' simple-or-escape-char 'B
token bytearray= " string-char* "Btoken verbatim-bytearray = @" verbatim-string-char* "Btoken simple-or-escape-char = escape-char | simple-chartoken simple-char = any char except newline,return,tab,backspace,',\,"To translate a string token to a string value, the F# parser concatenates all the Unicode characters for thestring-char elements within the string. Strings may include \n as a newline character. However, if a line ends
with \, the newline character and any leading whitespace elements on the subsequent line are ignored. Thus, thefollowing gives s the value "abcdef":
let s = "abc\def"Without the backslash, the resulting string includes the newline and whitespace characters. For example:
let s = "abcdef"
In this case, s has the value "abc\010

def" where \010 is the embedded control character for \n, which

has Unicode UTF-16 value 10.
Verbatim strings may be specified by using the @ symbol preceding the string as in C#. For example, thefollowing assigns the value "abc\def" to s.
let s = @"abc\def"String-like and character-like literals can also be specified for unsigned byte arrays (type byte[]). These tokens
cannot contain Unicode characters that have surrogate-pair UTF-16 encodings or UTF-16 encodings greater than127.

3.6 Symbolic Keywords
The following symbolic or partially symbolic character sequences are treated as keywords:
token symbolic-keyword =let! use! do! yield! return!
| -> <- . : ( ) [ ] [< >] [| |] { }' # :?> :? :> .. :: := ;; ; =_ ? ?? (*) <@ @> <@@ @@>The following symbols are reserved for future use:
token reserved-symbolic-sequence =~ `24

3.7 Symbolic Operators
User-defined and library-defined symbolic operators are sequences of characters as shown below, except where
the sequence of characters is a symbolic keyword (§3.6).
regexp first-op-char = !%&*+-./<=>@^|~
regexp op-char= first-op-char | ?
token quote-op-left| <@ <@@=

token quote-op-right| @> @@>=

token symbolic-op =| ?
| ?<| first-op-char op-char*| quote-op-left| quote-op-right
For example, &&& and ||| are valid symbolic operators. Only the operators ? and ?<- may start with ?.
The quote-op-left and quote-op-right operators are used in quoted expressions (§6.8).
For details about the associativity and precedence of symbolic operators in expression forms, see §4.4.

3.8 Numeric Literals
The lexical specification of numeric literals is as follows:
regexp digit= [0-9]regexp hexdigit= digit | [A-F] | [a-f]regexp octaldigit= [0-7]regexp bitdigit= [0-1]regexp int =| digit+For example, 34

regexp xint =| int| 0 (x|X) hexdigit+| 0 (o|O) octaldigit+| 0 (b|B) bitdigit+
token sbyte
token byte
token int16
token uint16
token int32
token uint32= xint 'y'= xint 'uy'
= xint 's'= xint 'us'
= xint 'l'= xint 'ul'
| xint 'u'
token nativeint = xint 'n'token unativeint = xint 'un'token int64= xint 'L'
token uint64= xint 'UL'
| xint 'uL'
token ieee32=| float [Ff]| xint 'lf'
token ieee64=| float

For example, 34
For example, 0x22
For example, 0o42
For example, 0b10010
For example, 34y
For example, 34uy
For example, 34s
For example, 34us
For example, 34l
For example, 34ul
For example, 34u
For example, 34n
For example, 34un
For example, 34L
For example, 34UL
For example, 34uL

For example, 3.0F or 3.0f
For example, 0x00000000lf
For example, 3.025

| xint 'LF'
token bignum

For example, 0x0000000000000000LF= int ('Q' | 'R' | 'Z' | 'I' | 'N' | 'G')
For example, 34742626263193832612536171N

token decimal = (float|int) [Mm]token float =digit+ . digit*
digit+ (. digit* )? (e|E) (+|-)? digit+

3.8.1 Post-filtering of Adjacent Prefix Tokens
Negative integers are specified using the – token; for example, -3. The token steam is post-filtered according to
the following rules:

If the token stream contains the adjacent tokens – token:
If token is a constant numeric literal, the pair of tokens is merged. For example, adjacent tokens - and 3
becomes the single token “-3”. Otherwise, the tokens remain separate. However the “-” token is marked as
an ADJACENT_PREFIX_OP token.
This rule does not apply to the sequence token1 - token2, if all three tokens are adjacent and token1 is aterminating token from expression forms that have lower precedence than the grammar production
expr = MINUS expr.
For example, the – and b tokens in the following sequence are not merged if all three tokens are adjacent:a-bOtherwise, the usual grammar rules apply to the uses of – and +, with an addition for ADJACENT_PREFIX_OP:
expr = expr MINUS expr
| MINUS expr
| ADJACENT_PREFIX_OP expr

3.8.2 Post-filtering of Integers Followed by Adjacent “..”Tokens of the form
token intdotdot = int..
such as 34.. are post-filtered to two tokens: one int and one symbolic-keyword, “..”.
This rule allows “..” to immediately follow an integer. This construction is used in expressions of the form [for x
in 1..2 -> x + x ]. Without this rule, the longest-match rule would consider this sequence to be a floatingpoint number followed by a “.”.

3.8.3 Reserved Numeric Literal Forms
The following token forms are reserved for future numeric literal formats:token reserved-literal-formats =| (xint | ieee32 | ieee64) ident-char+26

3.9 Line Directives
Line directives adjust the source code filenames and line numbers that are reported in error messages, recorded
in debugging symbols, and propagated to quoted expressions. F# supports the following line directives:
token line-directive =# int# int string# int verbatim-string#line int
#line int string#line int verbatim-string
A line directive applies to the line that immediately follows the directive. If no line directive is present, the first lineof a file is numbered 1.

3.10 Hidden Tokens
Some hidden tokens are inserted by lexical filtering (§15) or are used to replace existing tokens. See §15 for a
full specification and for the augmented grammar rules that take these into account.

3.11 Identifier Replacements
The following table lists identifiers that are automatically replaced by expressions.
Identifier

Replacement__SOURCE_DIRECTORY__

A literal verbatim string that specifies the name of the directory that contains thecurrent file. For example:
C:\source
The name of the current file is derived from the most recent line directive in the file. If
no line directive has appeared, the name is derived from the name that was
specificed to the command-line compiler in combination with
System.IO.Path.GetFullPath.
In F# Interactive, the name stdin is used. When F# Interactive is used from tools
such as Visual Studio, a line directive is implicitly added before the interactive
execution of each script fragment.

__SOURCE_FILE__

A literal verbatim string that contains the name of the current file. For example:
file.fs__LINE__A literal string that specifies the line number in the source file, after taking into
account adjustments from line directives.

27Basic Grammar Elements
This section defines grammar elements that are used repeatedly in later sections.

4.1 Operator Names
Several places in the grammar refer to an ident-or-op rather than an ident:
ident-or-op :=| ident| ( op-name )| (*)
op-name :=| symbolic-op| range-op-name| active-pattern-op-namerange-op-name :=| ..
| .. ..
active-pattern-op-name :=| | ident | ... | ident || | ident | ... | ident | _ |In operator definitions, the operator name is placed in parentheses. For example:
let (+++) x y = (x, y)This example defines the binary operator +++. The text (+++) is an ident-or-op that acts as an identifier withassociated text +++. Likewise, for active pattern definitions (§7), the active pattern case names are placed inparentheses, as in the following example:
let (|A|B|C|) x = if x < 0 then A elif x = 0 then B else C
Because an ident-or-op acts as an identifier, such names can be used in expressions. For example:
List.map ((+) 1) [ 1; 2; 3 ]The three character token (*)defines the * operator:let (*) x y = (x + y)
To define other operators that begin with *, whitespace must follow the opening parenthesis; otherwise (* isinterpreted as the start of a comment:let ( *+* ) x y = (x + y)Symbolic operators and some symbolic keywords have a compiled name that is visible in the compiled form of F#
programs. The compiled names are shown below.
[]
op_Nil::op_ColonColon+
op_Addition
op_Subtraction*
op_Multiply/
op_Division**
op_Exponentiation@
op_Append^
op_Concatenate%
op_Modulus&&& op_BitwiseAnd||| op_BitwiseOr^^^ op_ExclusiveOr<<< op_LeftShift~~~ op_LogicalNot>>> op_RightShift~+op_UnaryPlus~op_UnaryNegation=
op_Equality<>
op_Inequality<=
op_LessThanOrEqual>=
op_GreaterThanOrEqual<
op_LessThan>
op_GreaterThan?
op_Dynamic?<- op_DynamicAssignment|>
op_PipeRight||> op_PipeRight2|||> op_PipeRight3<|
op_PipeLeft<|| op_PipeLeft2<||| op_PipeLeft3!
op_Dereference>>
op_ComposeRight<<
op_ComposeLeft<@ @> op_Quotation<@@ @@> op_QuotationUntyped~%
op_Splice~%% op_SpliceUntyped~&
op_AddressOf~&& op_IntegerAddressOf||
op_BooleanOr&&
op_BooleanAnd+=
op_AdditionAssignment-=
op_SubtractionAssignment*=
op_MultiplyAssignment/=
op_DivisionAssignment..
op_Range.. .. op_RangeStep30

Compiled names for other symbolic operators are op_N1...Nn where N1 to Nn are the names for the characters asshown in the table below. For example, the symbolic identifier <* has the compiled name op_LessMultiply:
>
<
+
*
=
~
%
.
&
|
@
#
^
!
?
/
.
:
(
,
)
[
]

Greater
Less
Plus
Minus
Multiply
EqualsTwiddlePercent
Dot
Amp
Bar
At
Hash
Hat
Bang
Qmark
Divide
Dot
Colon
LParenComma
RParen
LBrackRBrack

4.2 Long IdentifiersLong identifiers long-ident are sequences of identifiers that are separated by ‘.’ and optional whitespace.
Long identifiers long-ident-or-op are long identifiers that may terminate with an operator name.
long-ident := ident '.' ... '.' ident
long-ident-or-op :=| long-ident '.' ident-or-op| ident-or-op

4.3 Constants
The constants in the following table may be used in patterns and expressions. The individual lexical formats forthe different constants are defined in §3.
const :=| sbyte| int16| int32| int64-- 8, 16, 32 and 64-bit signed integers| byte| uint16| uint32| int-- 32-bit signed integer| uint64-- 8, 16, 32 and 64-bit unsigned integers| ieee32-- 32-bit number of type "float32"| ieee64-- 64-bit number of type "float"| bignum-- User or library-defined integral literal type| char-- Unicode character of type "char"| string-- String of type "string" (System.String)| verbatim-string -- String of type "string" (System.String)31

| bytestring-- String of type "byte[]"| verbatim-bytearray-- String of type "byte[]"| bytechar-- Char of type "byte"| false | true-- Boolean constant of type "bool"| ()-- unit constant of type "unit"

4.4 Operators and Precedence
4.4.1 Categorization of Symbolic Operators
The following symbolic-op tokens can be used to form prefix and infix expressions. The marker OP represents
all symbolic-op tokens that begin with the indicated prefix, except for tokens that appear elsewhere in the table.

infix-or-prefix-op :=+, -, +., -., %, &, &&prefix-op :=infix-or-prefix-op~ ~~ ~~~!OP(and any repetitions of ~)(except !=)infix-op :=infix-or-prefix-op-OP +OP || <OP >OP = |OP &OP ^OP *OP /OP %OP !=(or any of these preceded by one or more ‘.’)
:=
::
$
or
?
The operators +, -, +., -., %, &, && can be used as both prefix and infix operators. When these operators areused as prefix operators, the tilde character is prepended internally to generate the operator name so that theparser can distinguish such usage from an infix use of the operator. For example, -x is parsed as an applicationof the operator ~- to the identifier x. This generated name is also used in definitions for these prefix operators.
Consequently, the definitions of the following prefix operators include the ~ character:// To completely redefine the prefix + operator:let (~+) x = x// To completely redefine the infix + operator to be addition modulo-7
let (+) a b = (a + b) % 7// To define the operator on a type:
type C(n:int) =let n = n % 7
member x.N = nstatic member (~+) (x:C) = x
static member (~-) (x:C) = C(-n)
static member (+) (x1:C,x2:C) = C(x1.N+x2.N)
static member (-) (x1:C,x2:C) = C(x1.N-x2.N)

The:: operator is special. It represents the union case for the addition of an element to the head of an immutablelinked list, and cannot be redefined, although it may be used to form infix expressions.It always accepts
arguments in tupled form—as do all union cases—rather than in curried form.

32

4.4.2 Precedence of Symbolic Operators and Pattern/Expression Constructs
Rules of precedence control the order of evaluation for ambiguous expression and pattern constructs. Higher
precedence items are evaluated before lower precedence items.
The following table shows the order of precedence, from highest to lowest, and indicates whether the operator or
expression is associated with the token to its left or right. The OP marker represents the symbolic-op tokens thatbegin with the specified prefix, except those listed elsewhere in the table. For example, +OP represents any token
that begins with a plus sign, unless the token appears elsewhere in the table.
Operator or expression

Associativity

Commentsf<types>

Left

High-precedence type application; see §15.3

f(x)

Left

High-precedence application; see §15.2

.

Left

prefix-op

Left

Applies to prefix uses of these symbols"| rule"Right

Pattern matching rules

"f x"

Left

"lazy x""assert x"
**OP

Right

*OP /OP %OP

Left-OP +OP

Left:?

Not associative

::

Right

^OP

Right

!=OP <OP >OP = |OP &OP $Left:> :?>

Right

& &&

Left

or ||

Left

,

Not associative

:=

Right

->

Right

if

Not associative

function, fun, match, try

Not associativelet

Not associative

;

Right

|

Left

when

Right

as

Right

Applies to infix uses of these symbols

33

If ambiguous grammar rules (such as the rules from §6) involve tokens in the table, a construct that appears
earlier in the table has higher precedence than a construct that appears later in the table. The associativity
indicates whether the operator or construct applies to the item to the left or the right of the operator.
For example, consider the following token stream:a + b * cIn this expression, the expr infix-op expr rule for b * c takes precedence over the expr infix-op expr
rule for a + b, because the * operator has higher precedence than the + operator. Thus, this expression can be
pictured as follows:a + b * crather thana + b * c
Likewise, given the tokens
a * b * cthe left associativity of * means we can picture the resolution of the ambiguity as:a * b * cIn the preceding table, leading . characters are ignored when determining precedence for infix operators. For
example, .* has the same precedence as *. This rule ensures that operators such as .*, which is frequently
used for pointwise-operation on matrices, have the expected precedence.
The table entries marked as “High-precedence application” and “High-precedence type application” are the resultof the augmentation of the lexical token stream, as described in §15.2 and §15.3.

34

Types and Type Constraints
The notion of type is central to both the static checking of F# programs and to dynamic type tests and reflection
at runtime. The word is used with four distinct but related meanings:
Type definitions, such as the actual CLI or F# definitions of System.String or
Microsoft.FSharp.Collections.Map<_,_>.



Syntactic types, such as the text option<_> that might occur in a program text. Syntactic types areconverted to static types during the process of type checking and inference.



Static types, which result from type checking and inference, either by the translation of syntactic types thatappear in the source text, or by the application of constraints that are related to particular language
constructs. For example, option<int> is the fully processed static type that is inferred for an expression
Some(1+1). Static types may contain type variables as described later in this section.

Runtime types, which are objects of type System.Type and represent some or all of the information thattype definitions and static types convey at runtime. The obj.GetType() method, which is available on all F#
values, provides access to the runtime type of an object. An object’s runtime type is related to the static typeof the identifiers and expressions that correspond to the object. Runtime types may be tested by built-in
language operators such as :? and :?>, the expression form downcast expr, and pattern matching type
tests. Runtime types of objects do not contain type variables. Runtime types that System.Reflection
reports may contain type variables that are represented by System.Type values.

The following describes the syntactic forms of types as they appear in programs:
type :=( type )type -> type
type * ... * typetypar
long-ident
long-ident<types>
long-ident< >type long-ident
type[ , ... , ]type lazy
type typar-defns
typar :> type
#type
types :=-- function type-- tuple type-- variable type-- named type, such as int-- named type, such as list<int>-- named type, such as IEnumerable< >-- named type, such as int list-- array type-- lazy type-- type with constraints-- variable type with subtype constraint-- anonymous type with subtype constraint

type, ..., typeatomic-type :=type : one of#type typar ( type ) long-ident long-ident<types>
typar :=_
'ident^ident-- anonymous variable type-- type variable-- static head-type type variable

constraint :=typar :> type-- coercion constraint
typar : null-- nullness constraint
static-typars : (member-sig ) -- member "trait" constraint
typar : (new : unit -> 'T)-- CLI default constructor constraint
typar : struct-- CLI non-Nullable structtypar : not struct -- CLI reference typetypar : enum<type> -- enum decomposition constraint

typar : unmanaged-- unmanaged constraint
typar : delegate<type, type> -- delegate decomposition constraint
typar : equality
typar : comparison

typar-defn := attributesopt typar
typar-defns:= < typar-defn, ..., typar-defn typar-constraintsopt >typar-constraints := when constraint and ... and constraint
static-typars :=^ident(^ident or ... or ^ident)
member-sig := <see Section 10>
In a type instantiation, the type name and the opening angle bracket must be syntactically adjacent with nointervening whitespace, as determined by lexical filtering (§15). Specifically:array<int>
and notarray <

int>

5.1 Checking Syntactic Types
Syntactic types are checked and converted to static types as they are encountered. Static types are a
specification device used to describeThe process of type checking and inference.



The connection between syntactic types and the execution of F# programs.

Every expression in an F# program is given a unique inferred static type, possibly involving one or more explicit
or implicit generic parameters.
For the remainder of this specification we use the same syntax to represent syntactic types and static types. For
example int32 * int32 is used to represent the syntactic type that appears in source code and the static typethat is used during checking and type inference.
The conversion from syntactic types to static types happens in the context of a name resolution environment(§14.1), a floating type variable environment, which is a mapping from names to type variables, and a type
inference environment (§14.5).
The phrase “fresh type” means a static type that is formed from a fresh type inference variable. Type inference
variables are either solved or generalized by type inference (§14.5). During conversion and throughout thechecking of types, expressions, declarations, and entire files, a set of current inference constraints is maintained.
That is, each static type is processed under input constraints Χ, and results in output constraints Χ’. Typeinference variables and constraints are progressively simplified and eliminated based on these equations throughconstraint solving (§14.5).

36

5.1.1 Named Types
Named types have several forms, as listed in the following table.

Form

Description

long-ident<ty1,…,tyn>

Named type with one or more suffixed type arguments.

long-ident

Named type with no type arguments

type long-ident

Named type with one type argument; processed the same as long-ident<type>ty lazy

Shorthand for the named type Microsoft.FSharp.Control.Lazy<ty>.

ty1 -> ty2

A function type, where: ty1 is the domain of the function values associated with the type
 ty2 is the range.
In compiled code it is represented by the named type
Microsoft.FSharp.Core.FastFunc<ty1,ty2>.

Named types are converted to static types as follows:

Name Resolution for Types (§14.1) resolves long-ident to a type definition with formal generic parameters<typar1,…, typarn> and formal constraints C. The number of type arguments n is used during the nameresolution process to distinguish between similarly named types that take different numbers of typearguments.

Fresh type inference variables <ty'1,…,ty'n> are generated for each formal type parameter. The formal
constraints C are added to the current inference constraints for the new type inference variables; andconstraints tyi = ty'i are added to the current inference constraints.

5.1.2 Variable Types
A type of the form 'ident is a variable type. For example, the following are all variable types:
'a
'T
'Key
During checking, Name Resolution (§14.1) is applied to the identifier.


If name resolution succeeds, the result is a variable type that refers to an existing declared type parameter.



If name resolution fails, the current floating type variable environment is consulted, although only in thecontext of a syntactic type that is embedded in an expression or pattern. If the type variable name isassigned a type in that environment, F# uses that mapping. Otherwise, a fresh type inference variable iscreated (see §14.5) and added to both the type inference environment and the floating type variable
environment.

A type of the form _ is an anonymous variable type. A fresh type inference variable is created and added to thetype inference environment (see §14.5) for such a type.

37

A type of the form ^ident is a statically resolved type variable. A fresh type inference variable is created andadded to the type inference environment (see §14.5). This type variable is tagged with an attribute that indicatesthat it can be generalized only at inline definitions (see §14.6.7). The same restriction on generalization applies
to any type variables that are contained in any type that is equated with the ^ident type in a type inference
equation.
Note: this specification generally uses uppercase identifiers such as 'T or 'Key for user-declared
generic type parameters, and uses lowercase identifiers such as 'a or 'b for compiler-inferred
generic parameters.

5.1.3 Tuple Types
A tuple type has the following form:ty1 * ... * tyn
The elaborated form of a tuple type is shorthand for a use of the family of F# library types
System.Tuple<_,...,_>. See §6.3.2 for the details of this encoding.
When considered as static types, tuple types are distinct from their encoded form. However, the encoded form oftuple types is visible in the F# type system through runtime types. For example, typeof<int * int> isequivalent to typeof<System.Tuple<int,int>>.

5.1.4 Array Types
Array types have the following forms:ty[]ty[ , ... , ]A type of the form ty[] is a single-dimensional array type, and a type of the form ty[ , ... , ] is amultidimensional array type. For example, int[,,] is an array of integers of rank 3.
Except where specified otherwise in this document, these array types are treated as named types, as if they are
an instantiation of a fictitious type definition System.Arrayn<ty> where n corresponds to the rank of the array
type.
Note: The type int[][,] in F# is the same as the type int[,][] in C# although the dimensions
are swapped. This ensures consistency with other postfix type names in F# such as int list
list.
F# 2.0 supports multidimensional array types only up to rank 4.

5.1.5 Constrained Types
A type with constraints has the following form:
type when constraints
During checking, type is first checked and converted to a static type, then constraints are checked and added
to the current inference constraints. The various forms of constraints are described in§5.2.
A type of the form typar :> type is a type variable with a subtype constraint and is equivalent to typar whentypar :> type.
A type of the form #type is an anonymous type with a subtype constraint and is equivalent to 'a when 'a :>type, where 'a is a fresh type inference variable.

385.2 Type Constraints
A type constraint limits the types that can be used to create an instance of a type parameter or type variable. F#
supports the following type constraints:

Subtype constraints



Nullness constraints

Member constraints

Default constructor constraintsValue type constraints



Reference type constraints

Enumeration constraints

Delegate constraints



Unmanaged constraints



Equality and comparison constraints

5.2.1 Subtype Constraints
An explicit subtype constraint has the following form:
typar :> type
During checking, typar is first checked as a variable type, type is checked as a type, and the constraint is
added to the current inference constraints. Subtype constraints affect type coercion as specified in §5.4.7.
Note that subtype constraints also result implicitly from:

Expressions of the form expr :> type.



Patterns of the form pattern :> type.



The use of generic values, types, and members with constraints.



The implicit use of subsumption when using values and members (§14.4.2).

A type variable cannot be constrained by two distinct instantiations of the same named type. If two such
constraints arise during constraint solving, the type instantiations are constrained to be equal. For example,
during type inference, if a type variable is constrained by both IA<int> and IA<string>, an error occurs when
the type instantiations are constrained to be equal. This limitation is specifically necessary to simplify type
inference, reduce the size of types shown to users, and help ensure the reporting of useful error messages.

5.2.2 Nullness Constraints
An explicit nullness constraint has the following form:
typar: null
During checking, typar is checked as a variable type and the constraint is added to the current inference
constraints. The conditions that govern when a type satisfies a nullness constraint are specified in §5.4.8.
In addition:


The typar must be a statically resolved type variable of the form ^ident. This limitation ensures that theconstraint is resolved at compile time, and means that generic code may not use this constraint unless thatcode is marked inline (§14.6.7).

39

Note: Nullness constraints are primarily for use during type checking and are used relatively rarely
in F# code.
Nullness constraints also arise from expressions of the form null.

5.2.3 Member Constraints
An explicit member constraint has the following form:(typar or ... or typar) : (member-sig)
For example, the F# library defines the + operator with the following signature:val inline (+) : ^a -> ^b -> ^cwhen (^a or ^b) : (static member (+) : ^a * ^b -> ^c)
This definition indicates that each use of the + operator results in a constraint on the types that correspond to
parameters ^a, ^b, and ^c. If these are named types, then either the named type for ^a or the named type for ^bmust support a static member called + that has the given signature.
In addition:
Each typar must be a statically resolved type variable (§5.1.2) in the form ^ident. This ensures that theconstraint is resolved at compile time against a corresponding named type. It also means that generic code
cannot use this constraint unless that code is marked inline (§14.6.7).



The member-sig cannot be generic; that is, it cannot include explicit type parameter definitions.



The conditions that govern when a type satisfies a member constraint are specified in §14.5.4 .
Note: Member constraints are primarily used to define overloaded functions in the F# library and areused relatively rarely in F# code.
Uses of overloaded operators do not result in generalized code unless definitions are marked as
inline. For example, the function
let f x = x + x
results in a function f that can be used only to add one type of value, such as int or float. The
exact type is determined by later constraints.

A type variable may not be involved in the support set of more than one member constraint that has the same
name, staticness, argument arity, and support set (§14.5.4). If it is, the argument and return types in the two
member constraints are themselves constrained to be equal.This limitation is specifically necessary to simplify
type inference, reduce the size of types shown to users, and ensure the reporting of useful error messages.

5.2.4 Default Constructor Constraints
An explicit default constructor constraint has the following form:
typar : (new : unit -> 'T)
During constraint solving (§14.5), the constraint type : (new : unit -> 'T) is met if type has aparameterless object constructor.
Note: This constraint form exists primarily to provide the full set of constraints that CLI
implementations allow. It is rarely used in F# programming.

5.2.5 Value Type Constraints
An explicit value type constraint has the following form:
typar : struct40

During constraint solving (§14.5), the constraint type : struct is met if type is a value type other than the CLItype System.Nullable<_>.
Note: This constraint form exists primarily to provide the full set of constraints that CLI
implementations allow. It is rarely used in F# programming.
The restriction on System.Nullable is inherited from C# and other CLI languages, which give thistype a special syntactic status. In F#, the type option<_> is similar to some uses of
System.Nullable<_>. For various technical reasons the two types cannot be equated, notably
because types such as System.Nullable<System.Nullable<_>> andSystem.Nullable<string> are not valid CLI types.

5.2.6 Reference Type Constraints
An explicit reference type constraint has the following form:
typar : not struct
During constraint solving (§14.5), the constraint type : not struct is met if type is a reference type.
Note: This constraint form exists primarily to provide the full set of constraints that CLI
implementations allow. It is rarely used in F# programming.

5.2.7 Enumeration Constraints
An explicit enumeration constraint has the following form:
typar : enum<underlying-type>
During constraint solving (§14.5), the constraint type : enum<underlying-type> is met if type is a CLI or F#
enumeration type that has constant literal values of type underlying-type.
Note: This constraint form exists primarily to allow the definition of library functions such as enum. It
is rarely used directly in F# programming.
The enum constraint does not imply anything about subtypes. For example, an enum constraint does
not imply that the type is a subtype of System.Enum.

5.2.8 Delegate Constraints
An explicit delegate constraint has the following form:
typar : delegate<tupled-arg-type, return-type>
During constraint solving (§14.5), the constraint type : delegate<tupled-arg-type, return-types> is met
if type is a delegate type D with declaration type D = delegate of object * arg1 * ... * argN andtupled-arg-type = arg1 * ... * argN. That is, the delegate must match the CLI design pattern where thesender object is the first argument to the event.
Note: This constraint form exists primarily to allow the definition of certain F# library functions thatare related to event programming. It is rarely used directly in F# programming.
The delegate constraint does not imply anything about subtypes. In particular, a ‘delegate’constraint does not imply that the type is a subtype of System.Delegate.
The delegate constraint applies only to delegate types that follow the usual form for CLI event
handlers, where the first argument is a “sender” object. The reason is that the purpose of theconstraint is to simplify the presentation of CLI event handlers to the F# programmer.

41

5.2.9 Unmanaged Constraints
An unmanaged constraint has the following form:
typar : unmanaged
During constraint solving (§14.5), the constraint type : unmanaged is met if type is unmanaged as specified
below:
Types sbyte, byte, char, nativeint, unativeint, float32, float, int16, uint16, int32, uint32,
int64, uint64, decimal are unmanaged.

Type nativeptr<type> is unmanaged.



A non-generic struct type whose fields are all unmanaged types is unmanaged.

5.2.10 Equality and Comparison Constraints
Equality constraints and comparison constraints have the following forms, respectively:
typar : equality
typar : comparison
During constraint solving (§14.5), the constraint type : equality is met if both of the following conditions aretrue:The type is a named type, and the type definition does not have, and is not inferred to have, the NoEquality
attribute.

The type has equality dependencies ty1, ..., tyn, each of which satisfies tyi : equality.

The constraint type : comparison is a comparison constraint. Such a constraint is met if all the following
conditions hold:

If the type is a named type, then the type definition does not have, and is not inferred to have, theNoComparison attribute, and the type definition implements System.IComparable or is an array type or is
System.IntPtr or is System.UIntPtr.



If the type has comparison dependencies ty1, ..., tyn, then each of these must satisfy tyi : comparison

An equality constraint is a relatively weak constraint, because with two exceptions, all CLI types satisfy thisconstraint. The exceptions are F# types that are annotated with the NoEquality attribute and structural types
that are inferred to have the NoEquality attribute. The reason is that in other CLI languages, such as C#, itpossible to use reference equality on all reference types.
A comparison constraint is a stronger constraint, because it usually implies that a type must implement
System.IComparable.

5.3 Type Parameter Definitions
Type parameter definitions can occur in the following locations:

Value definitions in modules



Member definitions

Type definitions



Corresponding specifications in signatures

For example, the following defines the type parameter ‘T in a function definition:
let id<'T> (x:'T) = x

42

Likewise, in a type definition:
type Funcs<'T1,'T2> ={ Forward: 'T1 -> 'T2;
Backward : 'T2 -> 'T2 }Likewise, in a signature file:val id<'T> : 'T -> 'T
Explicit type parameter definitions can include explicit constraint declarations. For example:
let dispose2<'T when 'T :> System.IDisposable> (x: 'T, y: 'T) =x.Dispose()y.Dispose()
The constraint in this example requires that 'T be a type that supports the IDisposable interface.
However, in most circumstances, declarations that imply subtype constraints on arguments can be written moreconcisely:
let throw (x: Exception) = raise x
Multiple explicit constraint declarations use and:let multipleConstraints<'T when 'T :> System.IDisposable and'T :> System.IComparable > (x: 'T, y: 'T) =if x.CompareTo(y) < 0 then x.Dispose() else y.Dispose()
Explicit type parameter definitions can declare custom attributes on type parameter definitions (§13.1).

5.4 Logical Properties of Types
During type checking and elaboration, syntactic types and constraints are processed into a reduced form
composed of:

Named types op<types>, where each op consists of a specific type definition, an operator to form function
types, an operator to form array types of a specific rank, or an operator to form specific n-tuple types.

Type variables 'ident.

5.4.1 Characteristics of Type Definitions
Type definitions include CLI type definitions such as System.String and types that are defined in F# code (§8).
The following terms are used to describe type definitions:Type definitions may be generic, with one or more type parameters; for example,
System.Collections.Generic.Dictionary<'Key,'Value>.

The generic parameters of type definitions may have associated formal type constraints.

Type definitions may have custom attributes (§13.1), some of which are relevant to checking and inference.

Type definitions may be type abbreviations (§8.3). These are eliminated for the purposes of checking andinference (see §5.4.2).

Type definitions have a kind which is one of the following:


Class



InterfaceDelegate43Struct

Record



Union



Enum



Measure

Abstract

The kind is determined at the point of declaration by Type Kind Inference (§8.2) if it is not specified explicitly
as part of the type definition. The kind of a type refers to the kind of its outermost named type definition, after
expanding abbreviations. For example, a type is a class type if it is a named type C<types> where C is ofkind class. Thus, System.Collections.Generic.List<int> is a class type.


Type definitions may be sealed. Record, union, function, tuple, struct, delegate, enum, and array types are
all sealed, as are class types that are marked with the SealedAttribute attribute.

Type definitions may have zero or one base type declarations. Each base type declaration represents anadditional type that is supported by any values that are formed using the type definition. Furthermore, some
aspects of the base type are used to form the implementation of the type definition.

Type definitions may have one or more interface declarations. These represent additional encapsulated
types that are supported by values that are formed using the type.

Class, interface, delegate, function, tuple, record, and union types are all reference type definitions. A type is areference type if its outermost named type definition is a reference type, after expanding type definitions.
Struct types are value types.

5.4.2 Expanding Abbreviations and Inference Equations
Two static types are considered equivalent and indistinguishable if they are equivalent after taking into accountboth of the following:

The inference equations that are inferred from the current inference constraints (§14.5).

The expansion of type abbreviations (§8.3).

For example, static types may refer to type abbreviations such as int, which is an abbreviation for
System.Int32and is declared by the F# library:type int = System.Int32
This means that the types int32 and System.Int32 are considered equivalent, as are System.Int32 -> int
and int -> System.Int32.
Likewise, consider the process of checking this function:
let checkString (x:string) y =(x = y), y.Contains("Hello")
During checking, fresh type inference variables are created for values x and y; let’s call them ty1 and ty2.
Checking imposes the constraints ty1 = string and ty1 = ty2. The second constraint results from the use of
the generic = operator. As a result of constraint solving, ty2 = string is inferred, and thus the type of y isstring.

44

All relations on static types are considered after the elimination of all equational inference constraints and typeabbreviations. For example, we say int is a struct type because System.Int32 is a struct type.
Note: Implementations of F# should attempt to preserve type abbreviations when reporting types
and errors to users. This typically means that type abbreviations should be preserved in the logicalstructure of types throughout the checking process.

5.4.3 Type Variables and Definition Sites
Static types may be type variables. During type inference, static types may be partial, in that they contain type
inference variables that have not been solved or generalized. Type variables may also refer to explicit typeparameter definitions, in which case the type variable is said to be rigid and have a definition site.
For example, in the following, the definition site of the type parameter 'T is the type definition of C:
type C<'T> = 'T * 'T
Type variables that do not have a binding site are inference variables. If an expression is composed of multiple
sub-expressions, the resulting constraint set is normally the union of the constraints that result from checking all
the sub-expressions. However, for some constructs (notably function, value and member definitions), thechecking process applies generalization (§14.6.7). Consequently, some intermediate inference variables andconstraints are factored out of the intermediate constraint sets and new implicit definition site(s) are assigned forthese variables.
For example, given the following declaration, the type inference variable that is associated with the value x is
generalized and has an implicit definition site at the definition of function id:
let id x = x
Occasionally in this specification we use a more fully annotated representation of inferred and generalized type
information. For example:
let id<'a> x'a = x'aHere, 'a represents a generic type parameter that is inferred by applying type inference and generalization to theoriginal source code (§14.6.7), and the annotation represents the definition site of the type variable.

5.4.4 Base Type of a TypeThe base type for the static types is shown in the table. These types are defined in the CLI specifications andcorresponding implementation documentation.
Static Type

Base Type

Abstract types

System.Object

All array types

System.ArrayClass types

The declared base type of the type definition if the type has one; otherwise,
System.Object. For generic types C<type-inst>, substitute the formal genericparameters of C for type-inst.

Delegate types

System.MulticastDelegate

Enum types

System.Enum

Exception types

System.Exception

Interface types

System.Object

Record types

System.Object

45

Static Type

Base Type

Struct types

System.ValueType

Union types

System.Object

Variable types

System.Object

5.4.5 Interfaces Types of a TypeThe interface types of a named type C<type-inst> are defined by the transitive closure of the interface
declarations of C and the interface types of the base type of C, where formal generic parameters are substituted
for the actual type instantiation type-inst.
The interface types for single dimensional array types ty[] include the transitive closure that starts from theinterface System.Collections.Generic.IList<ty>, which includes
System.Collections.Generic.ICollection<ty> and System.Collections.Generic.IEnumerable<ty>.

5.4.6 Type Equivalence
Two static types ty1 and ty2 are definitely equivalent (with respect to a set of current inference constraints) if
either of the following is true:

ty1 has form op<ty11, ..., ty1n>, ty2 has form op<ty21, ..., ty2n> and each ty1i is definitely
equivalent to ty2i for all 1 <= i <= n.

—OR—ty1 and ty2 are both variable types, and they both refer to the same definition site or are the same type
inference variable.

This means that the addition of new constraints may make types definitely equivalent where previously they were
not. For example, given Χ = { 'a = int }, we have list<int> = list<'a>.
Two static types ty1 and ty2 are feasibly equivalent if ty1 and ty2 may become definitely equivalent if further
constraints are added to the current inference constraints. Thus list<int> and list<'a> are feasibly
equivalent for the empty constraint set.

5.4.7 Subtyping and Coercion
A static type ty2 coerces to static type ty1 (with respect to a set of current inference constraints X), if ty1 is in thetransitive closure of the base types and interface types of ty2. Static coercion is written with the :> symbol:
ty2 :> ty1,
Variable types 'T coerce to all types ty if the current inference constraints include a constraint of the form 'T :>ty2, and ty is in the inclusive transitive closure of the base and interface types of ty2.
A static type ty2 feasibly coerces to static type ty1 if ty2 coerces to ty1 may hold through the addition of further
constraints to the current inference constraints. The result of adding constraints is defined in Constraint Solving(§14.5).

46

5.4.8 Nullness
The design of F# aims to greatly reduce the use of null literals in common programming tasks, because theygenerally result in error-prone code. However:

The use of some null literals is required for interoperation with CLI libraries.



The appearance of null values during execution cannot be completely precluded for technical reasons
related to the CLI and CLI libraries.

As a result, F# types differ in their treatment of the null literal and null values. All named types and typedefinitions fall into one of the following categories:

Types with the null literal. These types have null as an “extra” value. The following types are in thiscategory:

All CLI reference types that are defined in other CLI languages.

All types that are defined in F# and annotated with the AllowNullLiteral attribute.

For example, System.String and other CLI reference types satisfy this constraint, and these types permit
the direct use of the null literal.
Types with null as an abnormal value. These types do not permit the null literal, but do have null as anabnormal value. The following types are in this category:

All F# list, record, tuple, function, class, and interface types.



All F# union types except those that have null as a normal value, as discussed in the next bullet point.

For types in this category, the use of the null literal is not directly allowed. However, strictly speaking, it ispossible to generate a null value for these types by using certain functions such as
Unchecked.defaultof<type>. For these types, null is considered an abnormal value. Operations differ intheir use and treatment of null values; for details about evaluation of expressions that might include nullvalues, see §6.9.
Types with null as a representation value. These types do not permit the null literal but use the nullvalue as a representation.
For these types, the use of the null literal is not directly permitted. However, one or all of the “normal”
values of the type is represented by the null value. The following types are in this category:

The unit type. The null value is used to represent all values of this type.



Any union type that has theMicrosoft.FSharp.Core.CompilationRepresentation(CompilationRepresentationFlags.UseN
ullAsTrueValue) attribute flag and a single null union case. The null value represents this case. In
particular, null represents None in the F# option<_> type.



Types without null. These types do not permit the null literal and do not have the null value. All value
types are in this category, including primitive integers, floating-point numbers, and any value of a CLI or F#
struct type.

A static type ty satisfies a nullness constraint ty : null if it:Has an outermost named type that has the null literal.

Is a variable type with a typar : null constraint.

47

5.4.9 Default Initialization
Related to nullness is the default initialization of values of some types to zero values. This technique is common
in some programming languages, but the design of F# deliberately de-emphasizes it. However, default
initialization is allowed in some circumstances:

Checked default initialization may be used when a type is known to have a valid and “safe” default zero
value. For example, the types of fields that are labeled with DefaultValue(true) are checked to ensurethat they allow default initialization.



CLI libraries sometimes perform unchecked default initialization, as do the F# library primitives
Unchecked.defaultof<_> and Array.zeroCreate.

The following types permit default initialization:


Any type that satisfies the nullness constraint.

Primitive value types.

Struct types whose field types all permit default initialization.

5.4.10 Dynamic Conversion Between Types
A runtime type vty dynamically converts to a static type ty if any of the following are true:

vty coerces to ty.

vty is int32[]and ty is uint32[](or conversely). Likewise for sbyte[]/byte[], int16[]/uint16[],
int64[]/uint64[], and nativeint[]/unativeint[].



vty is enum[] where enum has underlying type underlying, and ty is underlying[] (or conversely), or the(un)signed equivalent of underlying[] by the immediately preceding rule.



vty is elemty1[], ty is elemty2[], elemty1 is a reference type, and elemty1 converts to elemty2.



ty is System.Nullable<vty>.

Note that this specification does not define the full algebra of the conversions of runtime types to static types
because the information that is available in runtime types is implementation dependent. However, thespecification does state the conditions under which objects are guaranteed to have a runtime type that iscompatible with a particular static type.
Note: This specification covers the additional rules of CLI dynamic conversions, all of which apply
to F# types. For example:
let x = box [| System.DayOfWeek.Monday |]let y = x :? int32[]
printf "%b" y // true
In the previous code, the type System.DayOfWeek.Monday[] does not statically coerce toint32[], but the expression x :? int32[] evaluates to true.
let x = box [| 1 |]let y = x :? uint32 []printf "%b" y // true
In the previous code, the type int32[] does not statically coerce to uint32[], but theexpression x :? uint32 [] evaluates to true.
let x = box [| "" |]let y = x :? obj []printf "%b" y // true48

In the previous code, the type string[] does not statically coerce to obj[], but theexpression x :? obj []evaluates to true.
let x = box 1
let y = x :? System.Nullable<int32>
printf "%b" y // true
In the previous code, the type int32 does not coerce to System.Nullable<int32>, but theexpression x :? System.Nullable<int32> evaluates to true.

49

Expressions
The expression forms and related elements are as follows:
expr :=
const-- a constant value( expr )-- block expression
begin expr end-- block expression
long-ident-or-op-- lookup expression
expr '.' long-ident-or-op-- dot lookup expression
expr expr-- application expression
expr(expr)-- high precedence application
expr<types>-- type application expression
expr infix-op expr-- infix application expression
prefix-op expr-- prefix application expression
expr.[expr]-- indexed lookup expression
expr.[slice-range]-- slice expression (1D)
expr.[slice-range, slice-range] -- slice expression (2D)
expr <- expr-- assignment expression
expr , ... , expr-- tuple expression
new type expr-- simple object expression{ new base-call object-members interface-impls } -- object expression{ field-initializers }-- record expression{ expr with field-initializers }-- record cloning expression[ expr ; ... ; expr ]-- list expression[| expr ; ... ; expr |]-- array expression
expr { comp-or-range-expr } -- computation expression[ comp-or-range-expr]-- computed list expression[| comp-or-range-expr |]-- computed array expression
lazy expr-- delayed expressionnull-- the "null" value for a reference type
expr : type-- type annotation
expr :> type-- static upcast coercion
expr :? type-- dynamic type test
expr :?> type-- dynamic downcast coercion
upcast expr-- static upcast expression
downcast expr-- dynamic downcast expression
let function-defn in expr–- function definition expression
let value-defn in expr–- value definition expression
let rec function-or-value-defns in expr -- recursive definition expression
use ident = expr in expr–- deterministic disposal expression
fun argument-pats -> expr-- function expressionfunction rules-- matching function expression
expr ; expr-- sequential execution expression
match expr with rules-- match expression
try expr with rules-- try/with expression
try expr finally expr-- try/finally expression
if expr then expr elif-branchesopt else-branchopt -- conditional expression
while expr do expr done-- while loop
for ident = expr to expr do expr done-- simple for loop
for pat in expr-or-range-expr do expr done-- enumerable for loop
assert expr-- assert expression<@ expr @>-- quoted expression<@@ expr @@>-- quoted expression%expr%%expr-- expression splice-- weakly typed expression splice(static-typars : (member-sig) expr) -– static member invocation

Expressions are defined in terms of patterns and other entities that are discussed later in this specification. The
following constructs are also used:exprs := expr ',' ... ',' expr
expr-or-range-expr :=exprrange-expr
elif-branches := elif-branch ... elif-branchelif-branch := elif expr then expr
else-branch := else expr
function-or-value-defn :=function-defn
value-defnfunction-defn :=inlineopt accessopt ident-or-op typar-defnsopt argument-pats return-typeopt = expr
value-defn :=mutableopt accessopt pat typar-defnsopt return-typeopt = expr
return-type :=: typefunction-or-value-defns :=function-or-value-defn and ... and function-or-value-defn
argument-pats:= atomic-pat ... atomic-pat
field-initializer :=long-ident = expr-- field initialization

field-initializers := field-initializer ; ... ; field-initializer
object-construction :=type expr -- construction expression
type-- interface construction expression
base-call :=object-construction-- anonymous base construction
object-construction as ident -- named base construction
interface-impls := interface-impl ... interface-impl
interface-impl :=interface type object-membersopt -- interface implementation
object-members := with member-defns end
member-defns :=member-defn ... member-defn

52Computation and range expressions are defined in terms of the following productions:comp-or-range-expr :=comp-expr
short-comp-expr
range-expr
comp-expr :=let! pat = expr in comp-expr
do! expr in comp-expr
use! pat = expr in comp-expr
yield! expr
yield expr
return! expr
return expr
expr-- binding computation-- sequential computation-- auto cleanup computation-- yield computation-- yield result-- return computation-- return result-- control flow or imperative action

short-comp-expr :=for pat in expr-or-range-expr -> expr -- yield result
range-expr :=expr .. expr
expr .. expr .. expr-- range sequence-- range sequence with skip

slice-range :=expr..
..exprexpr..expr'*'-- slice from index to end-- slice from start to index-- slice from index to index-- slice from start to end

6.1 Some Checking and Inference Terminology
The rules applied to check individual expressions are described in the following subsections. Where necessary,these sections reference specific inference procedures such as Name Resolution (§14.1) and Constraint Solving(§14.5).
All expressions are assigned a static type through type checking and inference. During type checking, each
expression is checked with respect to an initial type. The initial type establishes some of the information available
to resolve method overloading and other language constructs. We also use the following terminology:The phrase “the type ty1 is asserted to be equal to the type ty2” or simply “ty1 = ty2 is asserted” indicatesthat the constraint “ty1 = ty2” is added to the current inference constraints.



The phrase “ty1 is asserted to be a subtype of ty2” or simply “ty1 :> ty2 is asserted” indicates that theconstraint ty1 :> ty2 is added to the current inference constraints.



The phrase “type ty is known to ...” indicates that the initial type satisfies the given property given the current
inference constraints.



The phrase “the expression expr has type ty” means the initial type of the expression is asserted to beequal to ty.

Additionally:The addition of constraints to the type inference constraint set fails if it causes an inconsistent set of
constraints (§14.5). In this case either an error is reported or, if we are only attempting to assert thecondition, the state of the inference procedure is left unchanged and the test fails.

53

6.2 Elaboration and Elaborated Expressions
Checking an expression generates an elaborated expression in a simpler, reduced language that effectively
contains a fully resolved and annotated form of the expression. The elaborated expression provides more explicit
information than the source form. For example, the elaborated form of System.Console.WriteLine("Hello")
indicates exactly which overloaded method definition the call has resolved to. Elaborated forms are underlined inthis specification, for example, let x = 1 in x + x.
Except for this extra resolution information, elaborated forms are syntactically a subset of syntactic expressions,and in some cases (such as constants) the elaborated form is the same as the source form. This specification
uses the following elaborated forms:
ConstantsResolved value references: path

Lambda expressions: (fun ident -> expr)Primitive object expressions



Data expressions (tuples, union cases, array creation, record creation)

Default initialization expressions



Local definitions of values: let ident = expr in expr

Local definitions of functions:
let rec ident = expr and ... and ident = expr in expr

Applications of methods and functions (with static overloading resolved)Dynamic type coercions: expr :?> type

Dynamic type tests: expr :? typeFor-loops: for ident in ident to ident do expr doneWhile-loops: while expr do expr doneSequencing: expr; exprTry-with: try expr with exprTry-finally: try expr finally expr



The constructs required for the elaboration of pattern matching (§7).
Null tests



Switches on integers and other types



Switches on union cases



Switches on the runtime types of objects

The following constructs are used in the elaborated forms of expressions that make direct assignments to local
variables and arrays and generate “byref” pointer values. The operations are loosely named after theircorresponding primitive constructs in the CLI.
Assigning to a byref-pointer: expr <-stobj expr

Generating a byref-pointer by taking the address of a mutable value: &path.



Generating a byref-pointer by taking the address of a record field: &(expr.field)

Generating a byref-pointer by taking the address of an array element: &(expr.[expr])

Elaborated expressions form the basis for evaluation (see §6.9) and for the expression trees that quoted
expressions return(see §6.8).

54

By convention, when describing the process of elaborating compound expressions, we omit the process ofrecursively elaborating sub-expressions.

6.3 Data Expressions
This section describes the following data expressions:

Simple constant expressions



Tuple expressions



List expressions



Array expressions



Record expressions

Copy-and-update record expressions

Function expressions

Object expressions



Delayed expressions

Computation expressions

Sequence expressions



Range expressions



Lists via sequence expressions



Arrays via sequence expressions



Null expressions

'printf' formats

55

6.3.1 Simple Constant Expressions
Simple constant expressions are numeric, string, Boolean and unit constants. For example:
3y
32uy
17s
18us
86
99u
99999999L
10328273UL
1.
1.01
1.01e101.0f
1.01f
1.01e10f
99999999n
10328273un
99999999I'a'"3""c:\\home"@"c:\home""ASCII"B
()
false
true

// sbyte// byte// int16// uint16// int/int32// uint32// int64// uint64// float/double// float/double// float/double// float32/single// float32/single// float32/single// nativeint// unativeint// bigint// char// string// string// string// byte[]// unit// bool// bool(System.IntPtr)(System.UIntPtr)(System.Numerics.BigInteger or user-specified)(System.Char)(String)(System.String)(Verbatim Unicode, System.String)(Microsoft.FSharp.Core.Unit)(System.Boolean)(System.Boolean)

Simple constant expressions have the corresponding simple type and elaborate to the corresponding simple
constant value.
Integer literals with the suffixes Q, R, Z, I, N, G are processed using the following syntactic translation:xxxx<suffix>
For xxxx = 0→ NumericLiteral<suffix>.FromZero()For xxxx = 1→ NumericLiteral<suffix>.FromOne()For xxxx in the Int32 range→ NumericLiteral<suffix>.FromInt32(xxxx)

For xxxx in the Int64 range→ NumericLiteral<suffix>.FromInt64(xxxx)

For other numbers→ NumericLiteral<suffix>.FromString("xxxx")

For example, defining a module NumericLiteralZ as below enables the use of the literal form 32Z to generate asequence of 32 ‘Z’ characters. No literal syntax is available for numbers outside the range of 32-bit integers.
module NumericLiteralZ =let FromZero() = ""let FromOne() = "Z"
let FromInt32 n = String.replicate n "Z"
F# compilers may optimize on the assumption that calls to numeric literal functions always terminate, areidempotent, and do not have observable side effects.

6.3.2 Tuple Expressions
An expression of the form expr1, ..., exprn is a tuple expression. For example:
let three = (1,2,"3")
let blastoff = (10,9,8,7,6,5,4,3,2,1,0)56

The expression has the type (ty1 * ... * tyn) for fresh types ty1 … tyn, and each individual expression ei ischecked using initial type tyi.
Tuple types and expressions are translated into applications of a family of F# library types named System.Tuple.
Tuple types ty1 * ... * tyn are translated as follows:

For n <= 7 the elaborated form is Tuple<ty1,...,tyn>.

For larger n, tuple types are shorthand for applications of the additional F# library type System.Tuple<_> asfollows:
For n = 8 the elaborated form is Tuple<ty1,...,ty7,Tuple<ty8>>.

For 9 <= n the elaborated form is Tuple<ty1,...,ty7,tyB> where tyB is the converted form of the type(ty8 *...* tyn).

Tuple expressions (expr1,...,exprn) are translated as follows:

For n <= 7 the elaborated form new Tuple<ty1,…,tyn>(expr1,...,exprn).

For n = 8 the elaborated form new Tuple<ty1,…,ty7,Tuple<ty8>>(expr1,...,expr7, new
Tuple<ty8>(expr8).



For 9 <= n the elaborated form new Tuple<ty1,...ty7,ty8n>(expr1,..., expr7, new ty8n(e8n) where
ty8n is the type (ty8*...* tyn) and expr8n is the elaborated form of the expression
expr8,..., exprn.

When considered as static types, tuple types are distinct from their encoded form. However, the encoded form oftuple values and types is visible in the F# type system through runtime types. For example, typeof<int * int>
is equivalent to typeof<System.Tuple<int,int>>, and (1,2) has the runtime type System.Tuple<int,int>.
Likewise, (1,2,3,4,5,6,7,8,9) has the runtime type
Tuple<int,int,int,int,int,int,int,Tuple<int,int>>.
Note: The above encoding is invertible and the substitution of types for type variables preserves thisinversion. This means, among other things, that the F# reflection library can correctly report tuple
types based on runtime System.Type values. The inversion is defined by:

For the runtime type Tuple<ty1,...,tyN> when n <= 7, the corresponding F# tuple type is
ty1 * ... * tyNFor the runtime type Tuple<ty1,..., Tuple<tyN>> when n = 8, the corresponding F# tuple
type is ty1 * ... * ty8

For the runtime type Tuple<ty1,..., ty7,tyBn> , if tyBn corresponds to the F# tuple type ty8* ... * tyN, then the corresponding runtime type is ty1 * ... * tyN.

Runtime types of other forms do not have a corresponding tuple type. In particular, runtime types
that are instantiations of the eight-tuple type Tuple<_,_,_,_,_,_,_,_> must always have
Tuple<_> in the final position. Syntactic types that have some other form of type in this position are
not permitted, and if such an instantiation occurs in F# code or CLI library metadata that isreferenced by F# code, an F# implementation may report an error.

6.3.3 List Expressions
An expression of the form [expr1;...; exprn] is a list expression. The initial type of the expression is asserted
to be Microsoft.FSharp.Collections.List<ty> for a fresh type ty.
If ty is a named type, each expression expri is checked using a fresh type ty' as its initial type, with theconstraint ty' :> ty. Otherwise, each expression expri is checked using ty as its initial type.

57

List expressions elaborate to uses of Microsoft.FSharp.Collections.List<_> as
op_Cons(expr1,(op_Cons(expr2... op_Cons (exprn, op_Nil)...) where op_Cons and op_Nil are theunion cases with symbolic names :: and [] respectively.

6.3.4 Array Expressions
An expression of the form [|expr1;...; exprn |] is an array expression. The initial type of the expression isasserted to be ty[] for a fresh type ty.
If this assertion determines that ty is a named type, each expression expri is checked using a fresh type ty' as
its initial type, with the constraint ty' :> ty. Otherwise, each expression expri is checked using ty as its initial
type.
Array expressions are a primitive elaborated form.
Note: The F# 2.0 implementation ensures that large arrays of constants of type bool, char, byte,
sbyte, int16, uint16, int32, uint32, int64 and uint64 are compiled to an efficient binary
representation based on a call to
System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray.

6.3.5 Record Expressions
An expression of the form { field-initializer1 ; … ; field-initializern } is a record construction
expression. For example:
type Data = { Count : int; Name : string }let data1 = { Count = 3; Name = "Hello"; }let data2 = { Name = "Hello"; Count= 3 }In the following example, data4 uses a long identifier to indicate the relevant field:
module M =type Data = { Age : int; Name : string; Height : float }let data3 = { M.Age = 17; M.Name = "John"; M.Height = 186.0 }let data4 = { data3 with M.Name = "Bill"; M.Height = 176.0 }Fields may also be referenced by using the name of the containing type:
module M2 =type Data = { Age : int; Name : string; Height : float }let data5 = { M2.Data.Age = 17; M2.Data.Name = "John"; M2.Data.Height = 186.0 }let data6 = { data5 with M2.Data.Name = "Bill"; M2.Data.Height=176.0 }open M2
let data7 = { Data.Age = 17; Data.Name = "John"; Data.Height = 186.0 }let data8 = { data5 with Data.Name = "Bill"; Data.Height=176.0 }Each field-initializeri has the form field-labeli = expri. Each field-labeli is a long-ident, whichmust resolve to a field Fi in a unique record type R as follows:

If field-labeli is a single identifier fld and the initial type is known to be a record type R<_,...,_> thathas field Fi with name fld, then the field label resolves to Fi.

If field-labeli is not a single identifier or if the initial type is a variable type, then the field label is resolved
by performing Field Label Resolution (see §14.1) on field-labeli. This procedure results in a set of fields
FSeti. Each element of this set has a corresponding record type, thus resulting in a set of record types
RSeti. The intersection of all RSeti must yield a single record type R, and each field then resolves to thecorresponding field in R.
58

The set of fields must be complete. That is, each field in record type R must have exactly one field definition.
Each referenced field must be accessible (see §10.5), as must the type R.
After all field labels are resolved, the overall record expression is asserted to be of type R<ty1,...,tyN> for freshtypes ty1,...,tyN. Each expri is then checked in turn. The initial type is determined as follows:1.

Assume the type of the corresponding field Fi in R<ty1,...,tyN> is ftyi

2.

If the type of Fi prior to taking into account the instantiation <ty1,...,tyN> is a named type, then the initial
type is a fresh type inference variable fty'i with a constraint fty'i :> ftyi.

3.

Otherwise the initial type is ftyi.

Primitive record constructions are an elaborated form in which the fields appear in the same order as in therecord type definition. Record expressions themselves elaborate to a form that may introduce local value
definitions to ensure that expressions are evaluated in the same order that the field definitions appear in theoriginal expression. For example:
type R = {b : int; a : int }{ a = 1 + 1; b = 2 }The expression on the last line elaborates to let v = 1 + 1 in { b = 2; a = v }.
Records expressions are also used for object initializations in additional object constructor definitions (§8.6.3).
For example:
type C =val x : int
val y : int
new() = { x = 1; y = 2 }Note: The following record initialization form is deprecated:{ new type with Field1 = expr1 and … and Fieldn = exprn }The F# 2.0 implementation allows the use of this formonly with uppercase identifiers.
F# code should not use this expression form. A future version of the F# language will issue adeprecation warning.

6.3.6 Copy-and-update Record Expressions
A copy-and-update record expression has the following form:{ expr with field-initializers }where field-initializers is of the following form:
field-label1 = expr1 ; … ; field-labeln = exprn
Each field-labeli is a long-ident. In the following example, data2 is defined by using such an expression:
type Data = { Age : int; Name : string; Height : float }let data1 = { Age = 17; Name = "John"; Height = 186.0 }let data2 = { data1 with Name = "Bill"; Height = 176.0 }The expression expr is first checked with the same initial type as the overall expression. Next, the fielddefinitions are resolved by using the same technique as for record expressions. Each field label must resolve to a
field Fi in a single record type R, all of whose fields are accessible. After all field labels are resolved, the overall
record expression is asserted to be of type R<ty1,...,tyN> for fresh types ty1,...,tyN. Each expri is thenchecked in turn with initial type that results from the following procedure:
1.

Assume the type of the corresponding field Fi in R<ty1,...,tyN> is ftyi.

59

2.

If the type of Fi before considering the instantiation <ty1,...,tyN> is a named type, then the initial type is a
fresh type inference variable fty'i with a constraint fty'i :> ftyi.

3.

Otherwise, the initial type is ftyi.

A copy-and-update record expression elaborates as if it were a record expression written as follows:
let v = expr in { field-label1 = expr1 ; … ; field-labeln = exprn; F1 = v.F1; ... ; FM = v.FM }where F1 ... FM are the fields of R that are not defined in field-initializers and v is a fresh variable.

6.3.7 Function Expressions
An expression of the form fun pat1 ... patn -> expr is a function expression. For example:(fun x -> x + 1)
(fun x y -> x + y)(fun [x] -> x) // note, incomplete match(fun (x,y) (z,w) -> x + y + z + w)
Function expressions that involve only variable patterns are a primitive elaborated form. Function expressions
that involve non-variable patterns elaborate as if they had been written as follows:
fun v1 ... vn ->let pat1 = v1...
let patn = vn
expr
No pattern matching is performed until all arguments have been received. For example, the following does notraise a MatchFailureException exception:
let f = fun [x] y -> ylet g = f [] // ok
However, if a third line is added, a MatchFailureException exception is raised:
let z = g 3 // MatchFailureException is raised

6.3.8 Object Expressions
An expression of the following form is an object expression:{ new ty0 args-expropt object-members
interface ty1 object-members1…
interface tyn object-membersn }In the case of the interface declarations, the object-members are optional and are considered empty if absent.
Each set of object-members has the form:
with member-defns endopt
Lexical filtering inserts simulated $end tokens when lightweight syntax is used.
Each member of an object expression members can use the keyword member, override, or default. The
keyword member can be used even when overriding a member or implementing an interface.
For example:
let obj1 ={ new System.Collections.Generic.IComparer<int> withmember x.Compare(a,b) = compare (a % 7) (b % 7) }let obj2 =60

{ new System.Object() withmember x.ToString () = "Hello" }let obj3 ={ new System.Object() withmember x.ToString () = "Hello, base.ToString() = " + base.ToString() }let obj4 ={ new System.Object() withmember x.Finalize() = printfn "Finalize";
interface System.IDisposable withmember x.Dispose() = printfn "Dispose"; }An object expression can specify additional interfaces beyond those required to fulfill the abstract slots of the typebeing implemented. For example, obj4 in the preceding examples has static type System.Object but the object
additionally implements the interface System.IDisposable. The additional interfaces are not part of the static
type of the overall expression, but can be revealed through type tests.
Object expressions are statically checked as follows.
1.

First, ty0 to tyn are checked to verify that they are named types. The overall type of the expression is ty0
and is asserted to be equal to the initial type of the expression. However, if ty0 is type equivalent to
System.Object and ty1 exists, then the overall type is instead ty1.

2.

The type ty0 must be a class or interface type. The base construction argument args-expr must appear if
and only if ty0 is a class type. The type must have one or more accessible constructors; the call to these
constructors is resolved and elaborated using Method Application Resolution (see §14.4). Except for ty0,
each tyi must be an interface type.

3.

The F# compiler attempts to associate each member with a unique dispatch slot by using dispatch slot
inference (§14.7). If a unique matching dispatch slot is found, then the argument types and return type of themember are constrained to be precisely those of the dispatch slot.

4.

The arguments, patterns, and expressions that constitute the bodies of all implementing members are next
checked one by one to verify the following:For each member, the “this” value for the member is in scope and has type ty0.

Each member of an object expression can initially access the protected members of ty0.



If the variable base-ident appears, it must be named base, and in each member a base variable withthis name is in scope. Base variables can be used only in the member implementations of an object
expression, and are subject to the same limitations as byref values described in §14.9.

The object must satisfy dispatch slot checking (§14.8) which ensures that a one-to-one mapping exists between
dispatch slots and their implementations.
Object expressions elaborate to a primitive form. At execution, each object expression creates an object whose
runtime type is compatible with all of the tyi that have a dispatch map that is the result of dispatch slot checking(§14.8).
The following example shows how to both implement an interface and override a method from System.Object.
The overall type of the expression is INewIdentity.
type public INewIdentity =abstract IsAnonymous : bool
let anon ={ new System.Object() withmember i.ToString() = "anonymous"

61

interface INewIdentity withmember i.IsAnonymous = true }6.3.9 Delayed Expressions
An expression of the form lazy expr is a delayed expression. For example:
lazy (printfn "hello world")
is syntactic sugar for
new System.Lazy (fun () -> expr)
The behavior of the System.Lazy library type ensures that expression expr is evaluated on demand in response
to a .Value operation on the lazy value.

6.3.10 Computation Expressions
The following expression forms are all computation expressions:expr { for ... }expr { let ... }expr { let! ... }expr { use ... }expr { while ... }expr { yield ... }expr { yield! ... }expr { try ... }expr { return ... }expr { return! ... }More specifically, computation expressions are of the following form:builder-expr { cexpr }where cexpr is, syntactically, the grammar of expressions with the additional constructs that are defined in compexpr. Computation expressions are used for sequences and other non-standard interpretations of the F#
expression syntax. The expressionbuilder-expr { cexpr }translates tolet b = builder-expr in b.Run (b.Delay(fun () -> {| cexpr |}C))
for a fresh variable b. The type of b must be a named type after the checking of. If a Run method does not exist
on the inferred type of b when builder-expr is checked, the call to Run is omitted. Likewise, if no Delay method
exists on the type of b when the expression is checked, that call is omitted. The resulting expression is thenchecked.
The translation {|_ |}C is defined recursively according to the following rules:{| let binds in cexpr |}C{| let! pat = expr in cexpr |}C{| do expr in cexpr |}C{| do! expr in cexpr |}C= let binds in {| cexpr |}C)
= b.Bind(expr, (fun pat -> {| cexpr |}C))= expr; {| cexpr |}C= b.Bind(expr, (fun () -> {| cexpr |}C)){| yield expr |}C{| yield! expr |}C= b.Yield(expr)= b.YieldFrom(expr){| return expr |}C{| return! expr |}C= b.Return(expr)= b.ReturnFrom(expr){| use pat = expr in cexpr |}C{| use! v = expr in cexpr |}C= b.Using(expr, (fun pat -> {| cexpr |}C))= b.Bind(expr, (fun v ->62

b.Using(v,(fun v -> {| cexpr |}C))){| if expr then cexpr0 |}C= if expr then {| cexpr0 |}C else b.Zero(){| if expr then cexpr0 else cexpr1 |}C = if expr then {| cexpr0 |}C else {| cexpr1 |}C{| match expr with pati -> cexpri |}C= match expr with pati -> {| cexpri |}C{| for pat in expr do cexpr |}C= b.For({| expr |}E, (fun pat -> {| cexpr |}C)){| for ident = expr1 to expr2 do expr3 |}C= b.For({| seq { expr1 .. expr2 }|}E, (fun pat -> {| cexpr |}C)){| while expr do cexpr |}C= b.While((fun () -> expr), {| cexpr |}Delayed){| try cexpr with pati -> cexpri |}C = b.TryWith({| cexpr |}Delayed, (fun v ->match v with| (pati:exn) -> {| cexpri |}C| _ -> reraise exn){| try cexpr finally expr |}= b.TryFinally( {| cexpr |}Delayed, (fun () -> expr)){| trans-cexpr0; cexpr1 |}= b.Combine({| trans-cexpr0 |}C, {| cexpr1 |}Delayed){| other-expr0 ; cexpr1 |}= other-expr; {| cexpr1 |}C{| other-expr |}= other-expr; b.Zero()

Where


The auxiliary translation {| cexpr |}Delayed is b.Delay(fun () -> {| cexpr |}C).



A trans-cexpr0 is any syntactic expression form that receives an explicit translation by any of these rules,
except for the final rule.



The auxiliary translation {|_ |}E converts expr to an expression with the type

System.Collections.Generic.IEnumerable<ty>, for some type ty, by using enumerable extraction§6.5.6.
This translation implicitly places type constraints on the expected form of the builder methods. For example, forthe async builder found in the Microsoft.FSharp.Control library, these correspond to implementing a builder
of a type that has the following member signatures:type AsyncBuilder withmember For: seq<'T> * ('T -> Async<unit>) -> Async<unit>
member Zero : unit -> Async<unit>
member Combine : Async<unit> * Async<'T> -> Async<'T>
member While : (unit -> bool) * Async<unit> -> Async<unit>
member Return : 'T -> Async<'T>
member Delay : (unit -> Async<'T>) -> Async<'T>
member Using: 'T * ('T -> Async<'U>) -> Async<'U>
when 'U :> System.IDisposable
member Bind: Async<'T> * ('T -> Async<'U>) -> Async<'U>
member TryFinally: Async<'T> * (unit -> unit) -> Async<'T>
member TryWith: Async<'T> * (exn -> Async<'T>) -> Async<'T>
The following example shows a common approach to implementing a new computation expression builder for amonad. The example uses computation expressions to define computations that can be "partially run" byexecuting them step-by-step, for example, up to a time limit.
/// Computations that can cooperatively yield by returning a continuation
type Eventually<'T> =| Done of 'T| NotYetDone of (unit -> Eventually<'T>)[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
63

module Eventually =/// The bind for the computations. Stitch 'k' on to the end of the computation.
/// Note combinators like this are usually written in the reverse way,/// for example,///
e |> bind k
let rec bind k e =match e with
| Done x -> NotYetDone (fun () -> k x)
| NotYetDone work -> NotYetDone (fun () -> bind k (work()))/// The return for the computations.
let result x = Done x
type OkOrException<'T> =| Ok of 'T| Exception of System.Exception/// The catch for the computations. Stitch try/with throughout/// the computation and return the overall result as an OkOrException.
let rec catch e =match e with
| Done x -> result (Ok x)| NotYetDone work ->NotYetDone (fun () ->let res = try Ok(work()) with | e -> Exception ematch res with
| Ok cont -> catch cont // note, a tailcall| Exception e -> result (Exception e))/// The delay operator.
let delay f = NotYetDone (fun () -> f())/// The stepping action for the computations.
let step c =match c with
| Done _ -> c
| NotYetDone f -> f ()// The rest of the operations are boiler plate/// The tryFinally operator.
/// This is boiler-plate in terms of "result", "catch" and "bind".
let tryFinally e compensation =catch (e)|> bind (fun res -> compensation();match res with
| Ok v -> result v| Exception e -> raise e)/// The tryWith operator.
/// This is boiler-plate in terms of "result", "catch" and "bind".
let tryWith e handler =catch e|> bind (function Ok v -> result v | Exception e -> handler e)/// The whileLoop operator.
/// This is boiler-plate in terms of "result" and "bind".
let rec whileLoop gd body =if gd() then body |> bind (fun v -> whileLoop gd body)64

else result ()/// The sequential composition operator/// This is boiler-plate in terms of "result" and "bind".
let combine e1 e2 =e1 |> bind (fun () -> e2)/// The using operator.
let using (resource: #System.IDisposable) f =tryFinally (f resource) (fun () -> resource.Dispose())/// The forLoop operator.
/// This is boiler-plate in terms of "catch", "result" and "bind".
let forLoop (e:seq<_>) f =let ie = e.GetEnumerator()
tryFinally (whileLoop (fun () -> ie.MoveNext())(delay (fun () -> let v = ie.Current in f v)))(fun () -> ie.Dispose())// Give the mapping for F# computation expressions.
type EventuallyBuilder() =member x.Bind(e,k)= Eventually.bind k emember x.Return(v)= Eventually.result v
member x.ReturnFrom(v)= vmember x.Combine(e1,e2)= Eventually.combine e1 e2
member x.Delay(f)= Eventually.delay fmember x.Zero()= Eventually.result ()member x.TryWith(e,handler)= Eventually.tryWith e handler
member x.TryFinally(e,compensation) = Eventually.tryFinally e compensation
member x.For(e:seq<_>,f)= Eventually.forLoop e fmember x.Using(resource,e)= Eventually.using resource e
let eventually = new EventuallyBuilder()After the computations are defined, they can be built by using eventually { ... }:let comp =eventually { for x in 1 .. 2 doprintfn " x = %d" x
return 3 + 4 }These computations can now be “stepped.” For example:
let step x = Eventually.step x
comp |> step// returns "NotYetDone <closure>"comp |> step |> step// prints "x = 1"// returns "NotYetDone <closure>"comp |> step |> step |> step |> step |> step |> step// prints "x = 1"// prints "x = 2"// returns “NotYetDone <closure>”comp |> step |> step |> step |> step |> step |> step |> step |> step// prints "x = 1"// prints "x = 2"// returns "Done 7"65

6.3.11 Sequence Expressions
An expression in one of the following forms is a sequence expression:
seq { comp-expr }seq { short-comp-expr }For example:
seq { for x in [ 1; 2; 3 ] do for y in [5; 6] do yield x + y }seq { for x in [ 1; 2; 3 ] do yield x + x }seq { for x in [ 1; 2; 3 ] -> x + x }Logically speaking, sequence expressions can be thought of as computation expressions with a builder of type
Microsoft.FSharp.Collections.SeqBuilder. This type can be considered to be defined as follows:
type SeqBuilder() =member x.Yield (v) = Seq.singleton v
member x.YieldFrom (s:seq<_>) = s
member x.Return (():unit) = Seq.empty
member x.Combine (xs1,xs2) = Seq.append xs1 xs2
member x.For (xs,g) = Seq.collect f xs
member x.While (guard,body) = SequenceExpressionHelpers.EnumerateWhile guard bodymember x.TryFinally (xs,compensation) =SequenceExpressionHelpers.EnumerateThenFinally xs compensation
member x.Using (resource,xs) = SequenceExpressionHelpers.EnumerateUsing resource xs
However, this builder type is not actually defined in the F# library. Instead, sequence expressions are elaborated
directly as follows: Seq.singleton expr
 expr Seq.append {| expr1 |} {| expr2 |} Seq.map (fun pat -> {| expr2 |}) expr1
 Seq.collect (fun pat -> {| expr2 |}) expr1
 RuntimeHelpers.EnumerateWhile(fun () -> expr1){| expr2 |}){| try expr1 finally expr2 |} RuntimeHelpers.EnumerateThenFinally(| expr1 |})(fun () -> expr2){| use v = expr1 in expr2 |} let v = expr1 in
RuntimeHelpers.EnumerateUsing v {| expr2 |}{| let v = expr1 in expr2 |} let v = expr1 in {| expr2 |}{| match expr with pati -> expri |} .match expr with pati -> {| cexpri |}{| expr1 |} expr1 ; Seq.empty{| if expr then expr0 |}C
 if expr then {| expr0 |}C else Seq.empty{| if expr then expr0 else expr1 |}  if expr then {| expr0 |}C else {| expr1 |}C{| yield expr |}{| yield! expr |}{| expr1 ; expr2 |}{| for pat in expr1 -> expr2 |}{| for pat in expr1 do expr2 |}{| while expr1 do expr2 |}Here the use of Seq and RuntimeHelpers refers to the corresponding functions in
Microsoft.FSharp.Collections.Seq and Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers
respectively. This means that a sequence expression generates an object of type
System.Collections.Generic.IEnumerable<ty> for some type ty. Such an object has a GetEnumerator
method that returns a System.Collections.Generic.IEnumerator<ty> whose MoveNext, Current andDispose methods implement an on-demand evaluation of the sequence expressions.

6.3.12 Range Expressions
Expressions of the following forms are range expressions.
{ e1 .. e2 }{ e1 .. e2 .. e3 }seq { e1 .. e2 }seq { e1 .. e2 .. e3 }66

Range expressions generate sequences over a specified range. For example:
seq { 1 .. 10 } // 1; 2; 3; 4; 5; 6; 7; 8; 9; 10
seq { 1 .. 2 .. 10 } // 1; 3; 5; 7; 9
Range expressions involving expr1 .. expr2 are translated to uses of the (..) operator, and those involving
expr1 .. expr1 .. expr3 are translated to uses of the (.. ..) operator:
seq { e1 .. e2 }→ (..) e1 e2
seq { e1 .. e2 .. e3 } → (.. ..) e1 e2 e3
The default definition of these operators is in Microsoft.FSharp.Core.Operators. The (..) operator
generates an IEnumerable<_> for the range of values between the start (expr1) and finish (expr2) values, using
an increment of 1 (as defined by Microsoft.FSharp.Core.LanguagePrimitives.GenericOne). The (.. ..)operator generates an IEnumerable<_> for the range of values between the start (expr1) and finish (expr3)
values, using an increment of expr2.
The seq keyword, which denotes the type of computation expression, can be omitted for simple range
expressions, but this is not recommended and might be deprecated in a future release. It is always preferable toexplicitly mark the type of a computation expression.
Range expressions also occur as part of the translated form of expressions, including the following:[ expr1 .. expr2 ][| expr1 .. expr2 |]

for var in expr1 .. expr2 do expr3

A sequence iteration expression of the form for var in expr1 .. expr2 do expr3 done is sometimes
elaborated as a simple for loop-expression (§6.5.7).

6.3.13 Lists via Sequence Expressions
A list sequence expression is an expression in one of the following forms[ comp-expr ][ short-comp-expr ][ range-expr ]In all cases [ cexpr ] elaborates to Microsoft.FSharp.Collections.Seq.toList(seq { cexpr }).
For example:
let x2 = [ yield 1; yield 2 ]let x3 = [ yield 1
if System.DateTime.Now.DayOfWeek = System.DayOfWeek.Monday thenyield 2]

6.3.14 Arrays Sequence Expressions
An expression in one of the following forms is an array sequence expression:[| comp-expr |][| short-comp-expr |][| range-expr |]In all cases [| cexpr |] elaborates to Microsoft.FSharp.Collections.Seq.toArray(seq { cexpr }).

67

For example:
let x2 = [| yield 1; yield 2 |]let x3 = [| yield 1
if System.DateTime.Now.DayOfWeek = System.DayOfWeek.Monday thenyield 2 |]6.3.15 Null Expressions
An expression in the form null is a null expression. A null expression imposes a nullness constraint (§5.2.2,§5.4.8) on the initial type of the expression. The constraint ensures that the type directly supports the value null.
Null expressions are a primitive elaborated form.

6.3.16 'printf' Formats
Format strings are strings with % markers as format placeholders. Format strings are analyzed at compile timeand annotated with static and runtime type information as a result of that analysis. They are typically used with
one of the functions printf, fprintf, sprintf, or bprintf in the Microsoft.FSharp.Core.Printf module.
Format strings receive special treatment in order to type check uses of these functions more precisely.
More concretely, a constant string is interpreted as a printf-style format string if it is expected to have the type
Microsoft.FSharp.Core.PrintfFormat<'Printer,'State,'Residue,'Result,'Tuple>. The string isstatically analyzed to resolve the generic parameters of the PrintfFormat type, of which 'Printer and 'Tuple
are the most interesting:'Printer is the function type that is generated by applying a printf-like function to the format string.

'Tuple is the type of the tuple of values that are generated by treating the string as a generator (forexample, when the format string is used with a function similar to scanf in other languages).

A format placeholder has the following shape:%[flags][width][.precision][type]where:flags
Are 0, -, +, and the space character. The # flag is invalid and results in a compile-time error.
width
Is an integer that specifies the minimum number of characters in the result.
precision
Is the number of digits to the right of the decimal point for a floating-point type. .
typeIs as shown in the following table.
Placeholder string

Type

%b

bool

%s

string%cchar%d, %iOne of the basic integer types:
byte, sbyte, int16, uint16, int32, uint32, int64, uint64, nativeint or
unativeint%u

Basic integer type formatted as an unsigned integer

68Placeholder string

Type

%x

Basic integer type formatted as an unsigned hexadecimal integer with lowercase
letters a through f.

%X

Basic integer type formatted as an unsigned hexadecimal integer with uppercase
letters A through F.

%oBasic integer type formatted as an unsigned octal integer.

%e, %E, %f, %F, %g, %G

float or float32%M

System.Decimal%O

System.Object%A

Fresh variable type 'T%aFormatter of type 'State -> 'T -> 'Residue for a fresh variable type 'T%tFormatter of type 'State -> 'Residue

For example, the format string "%s %d %s" is given the type PrintfFormat<(string -> int -> string ->'d), 'b, 'c, 'd,(string * int * string)> for fresh variable types 'b, 'c, 'd. Applying printf to it
yields a function of type string -> int -> string -> unit.

6.4 Application Expressions
6.4.1 Basic Application Expressions
Application expressions involve variable names, dot-notation lookups, function applications, method applications,type applications, and item lookups, as shown in the following table.
Expression

Description

long-ident-or-op

Long-ident lookup expression

expr '.' long-ident-or-op

Dot lookup expression

expr expr

Function or member application expression

expr(expr)

High precedence function or member application
expression

expr<types>

Type application expression

expr< >

Type application expression with an empty type list

type expr

Simple object expression

The following are examples of application expressions:
System.Math.PI
System.Math.PI.ToString()(3 + 4).ToString()
System.Environment.GetEnvironmentVariable("PATH").Length
System.Console.WriteLine("Hello World")
Application expressions may start with object construction expressions that do not include the new keyword:
System.Object()
System.Collections.Generic.List<int>(10)System.Collections.Generic.KeyValuePair(3,"Three")69

System.Object().GetType()
System.Collections.Generic.Dictionary<int,int>(10).[1]If the long-ident-or-op starts with the special pseudo-identifier keyword global, F# resolves the identifier withrespect to the global namespace—that is, ignoring all open directives (see §14.2). For example:
global.System.Math.PI
is resolved to System.Math.PI ignoring all open directives.
The checking of application expressions is described in detail as an algorithm in §14.2. To check an application
expression, the expression form is repeatedly decomposed into a lead expression expr and a list of projections
projs through the use of Unqualified Lookup (§14.2.1). This in turn uses procedures such as ExpressionQualified Lookup and Method Application Resolution.
As described in §14.2, checking an application expression results in an elaborated expression that contains aseries of lookups and method calls. The elaborated expression may include:

Uses of named values

Uses of union cases



Record constructions

Applications of functions

Applications of methods (including methods that access properties)



Applications of object constructors

Uses of fields, both static and instanceUses of active pattern result elements

Additional constructs may be inserted when resolving method calls into simpler primitives:

The use of a method or value as a first-class function may result in a function expression.
For example, System.Environment.GetEnvironmentVariable elaborates to:(fun v -> System.Environment.GetEnvironmentVariable(v))
for some fresh variable v.

The use of post-hoc property setters results in the insertion of additional assignment and sequential
execution expressions in the elaborated expression.
For example, new System.Windows.Forms.Form(Text="Text") elaborates tolet v = new System.Windows.Forms.Form() in v.set_Text("Text"); v
for some fresh variable v.

The use of optional arguments results in the insertion of Some(_) and None data constructions in theelaborated expression.

For uses of active pattern results (see §10.2.4), for result i in an active pattern that has N possible results oftypes types, the elaborated expression form is a union case ChoiceNOfi of type
Microsoft.FSharp.Core.Choice<types>.

6.4.2 Object Construction Expressions
An expression of the following form is an object construction expression:
new ty(e1 ... en)70An object construction expression constructs a new instance of a type, usually by calling a constructor method on
the type. For example:
new System.Object()
new System.Collections.Generic.List<int>()
new System.Windows.Forms.Form (Text="Hello World")
new 'T()
The initial type of the expression is first asserted to be equal to ty. The type ty must not be an array, record,
union or tuple type. If ty is a named class or struct type:

ty must not be abstract.



If ty is a struct type, n is 0, and ty does not have a constructor method that takes zero arguments, theexpression elaborates to the default “zero-bit pattern” value for ty.

Otherwise, the type must have one or more accessible constructors. The overloading between thesepotential constructors is resolved and elaborated by using Method Application Resolution (see §14.4).

If ty is a delegate type the expression is a delegate implementation expression.


If the delegate type has an Invoke method that has the following signature
Invoke(ty1,...,tyn) -> rtyA,then the overall expression must be in this form:
new ty(expr) where expr has type ty1 -> ... -> tyn -> rtyB
If type rtyA is a CLI void type, then rtyB is unit, otherwise it is rtyA.



If any of the types tyi is a byref-type then an explicit function expression must be specified. That is, theoverall expression must be of the form new ty(fun pat1 ... patn -> exprbody).

If ty is a type variable:


There must be no arguments (that is, n = 0).



The type variable is constrained as follows:
ty : (new : unit -> ty)-- CLI default constructor constraint

The expression elaborates to a call to
Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicFunctions.CreateInstance<ty>(), which
in turn calls System.Activator.CreateInstance<ty>(), which in turn uses CLI reflection to find and callthe null object constructor method for type ty. On return from this function, any exceptions are wrapped byusing System.TargetInvocationException.

6.4.3 Operator Expressions
Operator expressions are specified in terms of their shallow syntactic translation to other constructs. The
following translations are applied in order:
infix-or-prefix-op e1
prefix-op e1
e1 infix-op e2→ (~infix-or-prefix-op) e1→ (prefix-op) e1→ (infix-op) e1 e2Note: When an operator that may be used as either an infix or prefix operator is used in prefix
position, a tilde character ~ is added to the name of the operator during the translation process.
These rules are applied after applying the rules for dynamic operators (§6.4.4).

71

The parenthesized operator name is then treated as an identifier and the standard rules for unqualified nameresolution (§14.1) in expressions are applied. The expression may resolve to a specific definition of a userdefined or library-defined operator. For example:
let (+++) a b = (a,b)
3 +++ 4
In some cases, the operator name resolves to a standard definition of an operator from the F# library. For
example, in the absence of an explicit definition of (+),
3 + 4
resolves to a use of the infix operator Microsoft.FSharp.Core.Operators.(+).
Some operators that are defined in the F# library receive special treatment in this specification. In particular:

The &expr and &&expr address-of operators (§6.4.5)

The expr && expr and expr || expr shortcut control flow operators (§6.5.4)

The %expr and %%expr expression splice operators in quotations (§6.8.3)

The library-defined operators, such as +, -, *, /, %, **, <<<, >>>, &&&, |||, and ^^^ (§17.2).

If the operator does not resolve to a user-defined or library-defined operator, the name resolution rules (§14.1)
ensure that the operator resolves to an expression that implicitly uses a static member invocation expression(§6.4.8) that involves the types of the operands. This means that the effective behavior of an operator that is notdefined in the F# library is to require a static member that has the same name as the operator, on the type of oneof the operands of the operator. In the following code, the otherwise undefined operator --> resolves to the static
member on the Receiver type, based on a type-directed resolution:
type Receiver(latestMessage:string) =static member (<--) (receiver:Receiver,message:string) =Receiver(message)
static member (-->) (message,receiver:Receiver) =Receiver(message)
let r = Receiver "no message"r <-- "Message One"
"Message Two" --> r

6.4.4 Dynamic Operator Expressions
Expressions of the following forms are dynamic operator expressions:expr1 ? expr2
expr1 ? expr2 <- expr3
These expressions are defined by their syntactic translation:
expr ? ident
expr1 ? (expr2)
expr1 ? ident <- expr2
expr1 ? (expr2) <- expr3→ (?) expr "ident"→ (?) expr1 expr2→ (?<-) expr1 "ident" expr2→ (?<-) expr1 expr2 expr3

Here "ident" is a string literal that contains the text of ident.
Note: The F# core library FSharp.Core.dll does not define the (?) and (?<-) operators.
However, user code may define these operators. For example, it is common to define the operators
to perform a dynamic lookup on the properties of an object by using reflection.

72

This syntactic translation applies regardless of the definition of the (?) and (?<-) operators. However, it does
not apply to uses of the parenthesized operator names, as in the following:(?) x y6.4.5 The AddressOf Operators
Under default definitions, expressions of the following forms are address-of expressions, called byref-address-of
expression and nativeptr-address-of expression, respectively:&expr&&exprSuch expressions take the address of a mutable local variable, byref-valued argument, field, array element, or
static mutable global variable.
For &expr and &&expr , the initial type of the overall expression must be of the form byref<ty> andnativeptr<ty> respectively, and the expression expr is checked with initial type ty.
The overall expression is elaborated recursively by taking the address of the elaborated form of expr, written
AddressOf(expr, DefinitelyMutates), defined in §6.9.4.
Use of these operators may result in unverifiable or invalid common intermediate language (CIL) code; whenpossible, a warning or error is generated. In general, their use is recommended only:To pass addresses where byref or nativeptr parameters are expected.

To pass a byref parameter on to a subsequent function.

When required to interoperate with native code.

Addresses that are generated by the && operator must not be passed to functions that are in tail call position. The
F# compiler does not check for this.
Direct uses of byref types, nativeptr types, or values in the Microsoft.FSharp.NativeInterop module may
result in invalid or unverifiable CIL code. In particular, byref and nativeptr types may NOT be used within
named types such as tuples or function types.
When calling an existing CLI signature that uses a CLI pointer type ty*, use a value of type nativeptr<ty>.
Note: The rules in this section apply to the following prefix operators, which are defined in the F#
core library for use with one argument.
Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators.(~&)
Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators.(~&&)
Other uses of these operators are not permitted.

6.4.6 Lookup Expressions
Lookup expressions are specified by syntactic translation:
e1.[e2]e1.[e2, e3]
e1.[e2, e3, e4]
e1.[e2, e3, e4, e5]
e1.[e2] <- e3
e1.[e2, e3] <- e4
e1.[e2, e3, e4] <- e5
e1.[e2, e3, e4, e5] <- e6→ e1.get_Item(e2)→ e1.get_Item(e2, e3)→ e1.get_Item(e2, e3, e4)→ e1.get_Item(e2, e3, e4, e5)→ e1.set_Item(e2, e3)→ e1.set_Item(e2, e3, e4)→ e1.set_Item(e2, e3, e4, e5)→ e1.set_Item(e2, e3, e4, e5, e6)73In addition, for the purposes of resolving expressions of this form, array types of rank 1, 2, 3, and 4 are assumed
to support a type extension that defines an Item property that has the following signatures:type 'T[] withmember arr.Item : int -> 'T
type 'T[,] withmember arr.Item : int * int -> 'T
type 'T[,,] withmember arr.Item : int * int * int -> 'T
type 'T[,,,] withmember arr.Item : int * int * int * int -> 'T
In addition, if type checking determines that the type of e1 is a named type that supports the DefaultMember
attribute, then the member name identified by the DefaultMember attribute is used instead of Item.

6.4.7 Slice Expressions
Slice expressions are defined by syntactic translation. For 1-D slices:
e1.[e2opt.. e3opt]
e1.[*]→ e1.GetSlice(arg2,arg3)→ e1.GetSlice(None,None)

where argi is Some eiopt if eiopt is present and None otherwise. At least one eiopt must be present. A similartranslation is used for 2-D slices:
e1.[e2opt.. e3opt, e4opt.. e5opt]
e1.[*, e2opt.. e3opt]
e1.[e2opt.. e3opt, *]e1.[*,*]→ e1.GetSlice(arg2,arg3,arg4,arg5)→ e1.GetSlice(None,None,arg2,arg3)→ e1.GetSlice(arg2,arg3,None,None)→ e1.GetSlice(None,None,None,None)

Because this is a shallow syntactic translation, the GetSlice name may be resolved by any of the relevant Name
Resolution (§14.1) techniques, including defining the method as a type extension for an existing type.
In addition, CIL array types of rank 1 to 4 are assumed to support a type extension that defines a method
GetSlice that has the following signature:
type 'T[] withmember arr.GetSlice : ?start1:int * ?end1:int -> 'T[,]
type 'T[,] withmember arr.GetSlice : ?start1:int * ?end1:int * ?start2:int * ?end2:int -> 'T[,]
type 'T[,,] withmember arr.GetSlice : ?start1:int * ?end1:int * ?start2:int * ?end2:int *?start3:int * ?end3:int-> 'T[,,]
type 'T[,,,] withmember arr.GetSlice : ?start1:int * ?end1:int * ?start2:int * ?end2:int *?start3:int * ?end3:int * ?start4:int * ?end4:int-> 'T[,,,]

6.4.8 Member Constraint Invocation Expressions
An expression of the following form is a member constraint invocation expression:(static-typars : (member-sig) expr)
Type checking proceeds as follows:1.

The expression is checked with initial type ty.

74

2.

A statically resolved member constraint is applied (§5.2.3):
static-typars : (member-sig)

3.

ty is asserted to be equal to the return type of the constraint.

4.

expr is checked with an initial type that corresponds to the argument types of the constraint.

The elaborated form of the expression is a member invocation. For example:
let inline speak (a: ^a) =let x = (^a : (member Speak: unit -> string) (a))
printfn "It said: %s" x
let y = (^a : (member MakeNoise: unit -> string) (a))
printfn "Then it went: %s" ytype Duck() =member x.Speak() = "I'm a duck"
member x.MakeNoise() = "quack"type Dog() =member x.Speak() = "I'm a dog"
member x.MakeNoise() = "grrrr"
let x = new Duck()let y = new Dog()
speak xspeak yOutputs:
It said: I'm a duck
Then it went: quack
It said: I'm a dog
Then it went: grrrr

6.4.9 Assignment Expressions
An expression of the following form is an assignment expression:
expr1 <- expr2
A modified version of Unqualified Lookup (§14.2.1) is applied to the expression expr1 using a fresh expected
result type ty, thus producing an elaborate expression expr1. The last qualification for expr1 must resolve to oneof the following constructs:


An invocation of a property with a setter method. The property may be an indexer.
Type checking incorporates expr2 as the last argument in the method application resolution for the settermethod. The overall elaborated expression is a method call to this setter property and includes the lastargument.



A mutable value path of type ty.
Type checking of expr2 uses the expected result type ty and generates an elaborated expression expr2.
The overall elaborated expression is an assignment to a value reference &path <-stobj expr2.



A reference to a value path of type byref<ty>.
Type checking of expr2 uses the expected result type ty and generates an elaborated expression expr2.
The overall elaborated expression is an assignment to a value reference path <-stobj expr2.

75



A reference to a mutable field expr1a.field with the actual result type ty.
Type checking of expr2 uses the expected result type ty and generatesan elaborated expression expr2. The
overall elaborated expression is an assignment to a field (see §6.9.4):
AddressOf(expr1a.field, DefinitelyMutates) <-stobj expr2



A array lookup expr1a.[expr1b] where expr1a has type ty[].
Type checking of expr2 uses the expected result type ty and generates thean elaborated expression expr2.
The overall elaborated expression is an assignment to a field (see §6.9.4):
AddressOf(expr1a.[expr1b] , DefinitelyMutates) <-stobj expr2
Note: Because assignments have the preceding interpretations, local values must be mutable sothat primitive field assignments and array lookups can mutate their immediate contents. In thiscontext, “immediate” contents means the contents of a mutable value type. For example, given[<Struct>]type SA =new(v) = { x = v }val mutable x : int[<Struct>]type SB =new(v) = { sa = v }val mutable sa : SA
let s1 = SA(0)let mutable s2 = SA(0)let s3 = SB(0)let mutable s4 = SB(0)Then these are not permitted:
s1.x <- 3s3.sa.x <- 3
and these are:s2.x <- 3s4.sa.x <- 3s4.sa <- SA(2)6.5 Control Flow Expressions
6.5.1 Parenthesized and Block Expressions
A parenthesized expression has the following form:(expr)
A block expression has the following form:
begin expr end
The expression expr is checked with the same initial type as the overall expression.
The elaborated form of the expression is simply the elaborated form of expr.

766.5.2 Sequential Execution Expressions
A sequential execution expression has the following form:
expr1; expr2
For example:
printfn "Hello"; printfn "World"; 3The ; token is optional when both of the following are true:The expression expr2 occurs on a subsequent line that starts in the same column as expr1.



The current pre-parse context that results from the syntax analysis of the program text is a SeqBlock (§15).

When the semicolon is optional, parsing inserts a $sep token automatically and applies an additional syntax rule
for lightweight syntax (§15.1.1). In practice, this means that code can omit the ; token for sequential execution
expressions that implement functions or immediately follow tokens such as begin and (.
The expression expr1 is checked with an arbitrary initial type ty. After checking expr1, ty is asserted to be equal
to unit. If the assertion fails, a warning rather than an error is reported. The expression expr2 is then checkedwith the same initial type as the overall expression.
Sequential execution expressions are a primitive elaborated form.

6.5.3 Conditional Expressions
A conditional expression has the following form:sif expr1a then expr1b
elif expr3a then expr2b…
elif exprna then exprnb
else exprlast
The elif and else branches may be omitted. For example:
if (1 + 1 = 2) then "ok" else "not ok"
if (1 + 1 = 2) then printfn "ok"Conditional expressions are equivalent to pattern matching on Boolean values. For example, the following
expression forms are equivalent:
if expr1 then expr2 else expr3
match (expr1:bool) with true -> expr2 | false -> expr3
If the else branch is omitted, the expression is a sequential conditional expression and is equivalent to:match (expr1:bool) with true -> expr2 | false -> ()with the exception that the initial type of the overall expression is first asserted to be unit.

6.5.4 Shortcut Operator Expressions
Under default definitions, expressions of the following form are respectively an shortcut and expression and ashortcut or expression:
expr && expr
expr || expr
These expressions are defined by their syntactic translation:
expr1 && expr2
expr1 || expr2→ if expr1 then expr2 else false→ if expr1 then true else expr2

77Note: The rules in this section apply when the following operators, as defined in the F# core library,
are applied to two arguments.
Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators.(&&)
Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators.(||)
If the operator is not immediately applied to two arguments, it is interpreted as a strict function that
evaluates both its arguments before use.

6.5.5 Pattern-Matching Expressions and Functions
A pattern-matching expressionhas the following form:
match expr with rules
Pattern matching is used to evaluate the given expression and select a rule (§7). For example:
match (3, 2) with
| 1, j -> printfn "j = %d" j| i, 2 -> printfn "i = %d" i| _-> printfn "no match"
A pattern-matching function is an expression of the following form:
function rules
A pattern-matching function is syntactic sugar for a single-argument function expression that is followed by
immediate matches on the argument. For example:
function| 1, j -> printfn "j = %d" j| _-> printfn "no match"
is syntactic sugar for the following, where x is a fresh variable:
fun x ->
match x with
| 1, j -> printfn "j = %d" j| _-> printfn "no match"

6.5.6 Sequence Iteration Expressions
An expression of the following form is a sequence iteration expression:for pat in expr1 do expr2 done
The done token is optional if expr2 appears on a later line and is indented from the column position of the fortoken. In this case, parsing inserts a $done token automatically and applies an additional syntax rule forlightweight syntax (§15.1.1).
For example:
for x, y in [(1, 2); (3, 4)] doprintfn "x = %d, y = %d" x yThe expression expr1 is checked with a fresh initial type tyexpr, which is then asserted to be a subtype of type
IEnumerable<ty>, for a fresh type ty. If the assertion succeeds, the expression elaborates to the following,where v is of type IEnumerator<ty> and pat is a pattern of type ty:
let v = expr1.GetEnumerator()try
while (v.MoveNext()) domatch v.Current with| pat -> expr2| _ -> ()78

finally
match box(v) with
| :? System.IDisposable as d -> d.Dispose()| _ -> ()If the assertion fails, the type tyexpr may also be of any static type that satisfies the “collection pattern” of CLI
libraries. If so, the enumerable extraction process is used to enumerate the type. In particular, tyexpr may be any
type that has an accessible GetEnumerator method that accepts zero arguments and returns a value that hasaccessible MoveNext and Current properties. The type of pat is the same as the return type of the Current
property on the enumerator value. However, if the Current property has return type obj and the collection typety has an Item property with a more specific (non-object) return type ty2, type ty2 is used instead, and a
dynamic cast is inserted to convert v.Current to ty2.
A sequence iteration of the form
for var in expr1 .. expr2 do expr3 done
where the type of expr1 or expr2 is equivalent to int, is elaborated as a simple for-loop expression (§6.5.7)

6.5.7 Simple for-Loop Expressions
An expression of the following form is a simple for loop expression:for var = expr1 to expr2 do expr3 done
The done token is optional when e2 appears on a later line and is indented from the column position of the fortoken. In this case, a $done token is automatically inserted, and an additional syntax rule for lightweight syntax
applies (§15.1.1). For example:
for x = 1 to 30 do
printfn "x = %d, x^2 = %d" x (x*x)
The bounds expr1 and expr2 are checked with initial type int. The overall type of the expression is unit. A
warning is reported if the body expr3 of the for loop does not have static type unit.
The following shows the elaborated form of a simple for-loop expression for fresh variables start and finish:
let start = expr1 in
let finish = expr2 infor var = start to finish do expr3 doneFor-loops over ranges that are specified by variables are a primitive elaborated form. When executed, theiterated range includes both the starting and ending values in the range, with an increment of 1.
An expression of the form
for var in expr1 .. expr2 do expr3 doneis always elaborated as a simple for-loop expression whenever the type of expr1 or expr2 is equivalent to int.

6.5.8 While Expressions
A while loop expression has the following form:
while expr1 do expr2 done
The done token is optional when expr2 appears on a subsequent line and is indented from the column positionof the while. In this case, a $done token is automatically inserted, and an additional syntax rule for lightweight
syntax applies (§15.1.1).
For example:
while System.DateTime.Today.DayOfWeek = System.DayOfWeek.Monday doprintfn "I don't like Mondays"

79

The overall type of the expression is unit. The expression expr1 is checked with initial type bool. A warning isreported if the body expr2 of the while loop cannot be asserted to have type unit.

6.5.9 Try-with Expressions
A try-with expression has the following form:
try expr with rules
For example:
try "1" with _ -> "2"
try
failwith "fail"with| Failure msg -> "caught"| :? System.InvalidOperationException -> "unexpected"
Expression expr is checked with the same initial type as the overall expression. The pattern matching clauses
are then checked with the same initial type and with input type System.Exception.
Try-with expressions are a primitive elaborated form.

6.5.10 Reraise Expressions
A reraise expression is an application of the reraise F# library function. This function must be applied to anargument and can be used only on the immediate right-hand side of rules in a try-with expression.
try
failwith "fail"with e -> printfn "Failing"; reraise()
Note: The rules in this section apply to any use of the function
Microsoft.FSharp.Core.Operators.reraise, which is defined in the F# core library.
When executed, reraise() continues exception processing with the original exception information.

6.5.11 Try-finally Expressions
A try-finally expression has the following form:
try expr1 finally expr2
For example:
try "1" finally printfn "Finally!"
try
failwith "fail"finally
printfn "Finally block"Expression expr1 is checked with the initial type of the overall expression. Expression expr2 is checked with
arbitrary initial type, and a warning occurs if this type cannot then be asserted to be equal to unit.
Try-finally expressions are a primitive elaborated form.

80

6.5.12 Assertion Expressions
An assertion expression has the following form:
assert expr
The expression assert expr is syntactic sugar for System.Diagnostics.Debug.Assert(expr)
Note: System.Diagnostics.Debug.Assert is a conditional method call. This means that
assertions are triggered only if the DEBUG conditional compilation symbol is defined.

6.6 Definition Expressions
A definition expression has one of the following forms:
let function-defn in expr
let value-defn in expr
let rec function-or-value-defns in expr
use ident = expr1 in expr
Such an expression establishes a local function or value definition within the lexical scope of expr and has thesame overall type as expr.
In each case, the in token is optional if expr appears on a subsequent line and is aligned with the token let. In
this case, a $in token is automatically inserted, and an additional syntax rule for lightweight syntax applies(§15.1.1)
For example:
let x = 1x + x
andlet x, y = ("One", 1)x.Length + yandlet id x = x in (id 3, id "Three")andlet swap (x, y) = (y,x)
List.map swap [ (1, 2); (3, 4) ]and
let K x y = x in List.map (K 3) [ 1; 2; 3; 4 ]Function and value definitions in expressions are similar to function and value definitions in class definitions(§8.6.1.3), modules (§10.2.1), and computation expressions (§6.3.10), with the following exceptions:Function and value definitions in expressions may not define explicit generic parameters (§5.3). For
example, the following expression is rejected:
let f<'T> (x:'T) = x in f 3

Function and value definitions in expressions are not public and are not subject to arity analysis (§14.10).

Any custom attributes that are specified on the declaration, parameters, and/or return arguments are ignored
and result in a warning. As a result, function and value definitions in expressions may not have theThreadStatic or ContextStatic attribute.

81

6.6.1 Value Definition Expressions
A value definition expression has the following form:
let value-defn in expr
where value-defn has the form:
mutableopt accessopt pat typar-defnsopt return-typeopt = rhs-expr
Checking proceeds as follows:
1.

Check the value-defn (§14.6), which defines a group of identifiers identj with inferred types tyj

2.

Add the identifiers identj to the name resolution environment, each with corresponding type tyj.

3.

Check the body expr against the initial type of the overall expression.

In this case, the following rules apply:If pat is a single value pattern ident, the resulting elaborated form of the entire expression is
let ident1 <typars1> = expr1 inbody-expr
where ident1, typars1 and expr1 are defined in §14.6.



Otherwise, the resulting elaborated form of the entire expression is
let tmp <typars1… typarsn> = expr in
let ident1 <typars1> = expr1 in…
let identn <typarsn> = exprn inbody-expr
where tmp is a fresh identifier and identi, typarsi, and expri all result from the compilation of the patternpat (§7) against the input tmp.

Value definitions in expressions may be marked as mutable. For example:
let mutable v = 0
while v < 10 dov <- v + 1
printfn "v = %d" v
Such variables are under the same restrictions as values of type byref<_> (§14.9), and are implicitly
dereferenced each time they are used.

6.6.2 Function Definition Expressions
A function definition expression has the form:
let function-defn in expr
where function-defn has the form:
inlineopt accessopt ident-or-op typar-defnsopt pat1 ... patn return-typeopt = rhs-expr
Checking proceeds as follows:
1.

Check the function-defn (§14.6), which defines ident1, ty1, typars1 and expr1

2.

Add the identifier ident1 to the name resolution environment, each with corresponding type ty1.

3.

Check the body expr against the initial type of the overall expression.

82

The resulting elaborated form of the entire expression is
let ident1 <typars1> = expr1 in
expr
where ident1, typars1 and expr1 are as defined in §14.6.

6.6.3 Recursive Definition Expressions
An expression of the following form is a recursive definition expression:
let rec function-or-value-defns in expr
The defined functions and values are available for use within their own definitions—that is can be used within anyof the expressions on the right-hand side of function-or-value-defns. Multiple functions or values may be
defined by using let rec … and …. For example:
let test() =let rec twoForward count =printfn "at %d, taking two steps forward" countif count = 1000 then "got there!"
else oneBack (count + 2)
and oneBack count =printfn "at %d, taking one step back " count
twoForward (count - 1)
twoForward 1
test()In the example, the expression defines a set of recursive functions. If one or more recursive values are defined,
the recursive expressions are analyzed for safety (§14.6.6). This may result in warnings (including some reported
as compile-time errors) and runtime checks.

6.6.4 Deterministic Disposal Expressions
A deterministic disposal expression has the form:
use ident = expr1 in expr2
For example:
use inStream = System.IO.File.OpenText "input.txt"
let line1 = inStream.ReadLine()
let line2 = inStream.ReadLine()(line1,line2)
The expression is first checked as an expression of form let ident = expr1 in expr2 (§Error! Reference
ource not found.), which results in an elaborated expression of the following form:
let ident1 : ty1 = expr1 in expr2.
Only one value may be defined by a deterministic disposal expression, and the definition is not generalized(§14.6.7). The type ty1, is then asserted to be a subtype of System.IDisposable. If the dynamic value of theexpression after coercion to type obj is non-null, the Dispose method is called on the value when the value goesout of scope. Thus the overall expression elaborates to this:let ident1 : ty1 = expr1
try expr2
finally (match (ident :> obj) with| null -> ()| _ -> (ident :> System.IDisposable).Dispose())

83

6.7 Type-Related Expressions
6.7.1 Type-Annotated Expressions
A type-annotated expression has the following form, where ty indicates the static type of expr:
expr : ty
For example:(1 : int)
let f x = (x : string) + x
When checked, the initial type of the overall expression is asserted to be equal to ty. Expression expr is thenchecked with initial type ty. The expression elaborates to the elaborated form of expr. This ensures that
information from the annotation is used during the analysis of expr itself.

6.7.2 Static Coercion Expressions
A static coercion expression—also called a flexible type constraint—has the following form:
expr :> ty
The expression upcast expr is equivalent to expr :> _, so the target type is the same as the initial type of theoverall expression. For example:(1 :> obj)("Hello" :> obj)([1;2;3] :> seq<int>).GetEnumerator()(upcast 1 : obj)
The initial type of the overall expression is ty. Expression expr is checked using a fresh initial type tye, withconstraint tye :> ty. Static coercions are a primitive elaborated form.

6.7.3 Dynamic Type-Test Expressions
A dynamic type-test expression has the following form:
expr :? ty
For example:((1 :> obj) :? int)((1 :> obj) :? string)
The initial type of the overall expression is bool. Expression expr is checked using a fresh initial type tye. Afterchecking:

The type tye must not be a variable type.



A warning is given if tye coerces to ty.



The type tye must not be sealed.

If type ty is sealed, or if ty is a variable type, or if type tye is not an interface type, then ty :> tye isasserted.

Dynamic type tests are a primitive elaborated form.

6.7.4 Dynamic Coercion Expressions
A dynamic coercion expression has the following form:
expr :?> ty84

The expression downcast e1 is equivalent to expr :?> _, so the target type is the same as the initial type of theoverall expression. For example:
let obj1 = (1 :> obj)
(obj1 :?> int)
(obj1 :?> string)(downcast obj1 : int)
The initial type of the overall expression is ty. Expression expr is checked using a fresh initial type tye. Afterthese checks:


The type tye must not be a variable type.



A warning is given if tye coerces to ty.



The type tye must not be sealed.

If type ty is sealed, or if ty is a variable type, or if type tye is not an interface type, then ty :> tye isasserted.

Dynamic coercions are a primitive elaborated form.

6.8 Quoted Expressions
An expression in one of these forms is a quoted expression:<@ expr @><@@ expr @@>The former is a strongly typed quoted expression, and the latter is a weakly typed quoted expression. In bothcases, the expression forms capture the enclosed expression in the form of a typed abstract syntax tree.
The exact nodes that appear in the expression tree are determined by the elaborated form of expr that typechecking produces.
For details about the nodes that may be encountered, see the documentation for theMicrosoft.FSharp.Quotations.Expr type in the F# core library. In particular, quotations may contain:

References to module-bound functions and values, and to type-bound members. For example:
let id x = x
let f (x : int) = <@ id 1 @>In this case the value appears in the expression tree as a node of kind
Microsoft.FSharp.Quotations.Expr.Call.



A function, value, or member that is annotated with the ReflectedDefinition attribute. If so, theexpression tree that forms its definition may be retrieved dynamically using theMicrosoft.FSharp.Quotations.Expr.TryGetReflectedDefinition.



References to defined values, such as the following:
let f (x : int) = <@ x + 1 @>Such a value appears in the expression tree as a node of kind
Microsoft.FSharp.Quotations.Expr.Value.

85



References to generic type parameters or uses of constructs whose type involves a generic parameter, suchas the following:
let f (x:'T) = <@ (x, x) : 'T * 'T @>In this case, the actual value of the type parameter is implicitly substituted throughout the type annotations
and types in the generated expression tree.

In F# 2.0, the following limitations apply to quoted expressions:

Quotations may not use object expressions.



Quotations may not define expression-bound functions that are themselves inferred to be generic. Instead,
expression-bound functions should either include type annotations to refer to a specific type or should be
written by using module-bound functions or class-bound members.

6.8.1 Strongly Typed Quoted Expressions
A strongly typed quoted expression has the following form:<@ expr @>For example:<@ 1 + 1 @><@ (fun x -> x + 1) @>In the first example, the type of the expression is Microsoft.FSharp.Quotations.Expr<int>. In the second
example, the type of the expression is Microsoft.FSharp.Quotations.Expr<int -> int>.
When checked, the initial type of a strongly typed quoted expression <@ expr @> is asserted to be of the form
Microsoft.FSharp.Quotations.Expr<ty> for a fresh type ty. The expression expr is checked with initial typety.

6.8.2 Weakly Typed Quoted Expressions
A weakly typed quoted expression has the following form:<@@ expr @@>Weakly typed quoted expressions are similar to strongly quoted expressions but omit any type annotation. For
example:<@@ 1 + 1 @@><@@ (fun x -> x + 1) @@>In both these examples, the type of the expression is Microsoft.FSharp.Quotations.Expr.
When checked, the initial type of a weakly typed quoted expression <@@ expr @@> is asserted to be of the form
Microsoft.FSharp.Quotations.Expr. The expression expr is checked with fresh initial type ty.

6.8.3 Expression Splices
Both strongly typed and weakly typed quotations may contain expression splices in the following forms:%expr%%expr
These are respectively strongly typed and weakly typed splicing operators.

866.8.3.1

Strongly Typed Expression Splices

An expression of the following form is a strongly typed expression splice:%expr
For example, given
open Microsoft.FSharp.Quotations
let f1 (v:Expr<int>) = <@ %v + 1 @>let expr = f1 <@ 3 @>the identifier expr evaluates to the same expression tree as <@ 3 + 1 @>. The expression tree for <@ 3 @>replaces the splice in the corresponding expression tree node.
A strongly typed expression splice may appear only in a quotation. Assuming that the splice expression %expr ischecked with initial type ty, the expression expr is checked with initial type
Microsoft.FSharp.Quotations.Expr<ty>.
Note: The rules in this section apply to any use of the prefix operator
Microsoft.FSharp.Core.ExtraTopLevelOperators.(~%). Uses of this operator must be applied
to an argument and may only appear in quoted expressions.

6.8.3.2

Weakly Typed Expression Splices

An expression of the following form is a weakly typed expression splice:%%exprFor example, given
open Microsoft.FSharp.Quotations
let f1 (v:Expr) = <@ %%v + 1 @>let tree = f1 <@@ 3 @@>the identifier tree evaluates to the same expression tree as <@ 3 + 1 @>. The expression tree replaces the
splice in the corresponding expression tree node.
A weakly typed expression splice may appear only in a quotation. Assuming that the splice expression %%expr ischecked with initial type ty, then the expression expr is checked with initial type
Microsoft.FSharp.Quotations.Expr. No additional constraint is placed on ty.
Additional type annotations are often required for successful use of this operator.
Note: The rules in this section apply to any use of the prefix operator
Microsoft.FSharp.Core.ExtraTopLevelOperators.(~%%), which is defined in the F# corelibrary. Uses of this operator must be applied to an argument and may only occur in quoted
expressions.

6.9 Evaluation of Elaborated Forms
At runtime, execution evaluates expressions to values. The evaluation semantics of each expression form are
specified in the subsections that follow.

6.9.1 Values and Execution Context
The execution of elaborated F# expressions results in values. Values include:Primitive constant values

The special value null87

References to object values in the global heap of object values



Values for value types, containing a value for each field in the value type

Pointers to mutable locations (including static mutable locations, mutable fields and array elements)

Evaluation assumes the following evaluation context:


A global heap of object values. Each object value contains:

A runtime type and dispatch map

A set of fields with associated valuesFor array objects, an array of values in index orderFor function objects, an expression which is the body of the function

An optional union case label, which is an identifier

A closure environment that assigns values to all variables that are referenced in the method bodies thatare associated with the object

A global environment that maps runtime-type/name pairs to values.Each name identifies a static field in a
type definition or a value in a module.



A local environment mapping names of variables to values.



A local stack of active exception handlers, made up of a stack of try/with and try/finally handlers.

Evaluation may also raise an exception. In this case, the stack of active exception handlers is processed until the
exception is handled, in which case additional expressions may be executed (for try/finally handlers), or analternative expression may be evaluated (for try/with handlers), as described below.

6.9.2 Parallel Execution and Memory Model
In a concurrent environment, evaluation may involve both multiple active computations (multiple concurrent andparallel threads of execution) and multiple pending computations (pending callbacks, such as those activated in
response to an I/O event).
If multiple active computations concurrently access mutable locations in the global environment or heap, theatomicity, read, and write guarantees of the underlying CLI implementation apply. The guarantees are related to
the logical sizes and characteristics of values, which in turn depend on their type:F# reference types are guaranteed to map to CLI reference types. In the CLI memory model, reference types
have atomic reads and writes.

F# value types map to a corresponding CLI value type that has corresponding fields. Reads and writes ofsizes less than or equal to one machine word are atomic.

The VolatileField attribute marks a mutable location as volatile in the compiled form of the code.
Ordering of reads and writes from mutable locations may be adjusted according to the limitations specified by the
CLI memory model. The following example shows situations in which changes to read and write order can occur,with annotations about the order of reads:
type ClassContainingMutableData() =let value = (1, 2)
let mutable mutableValue = (1, 2)[<VolatileField>]
let mutable volatileMutableValue = (1, 2)
member x.ReadValues() =// Two reads on an immutable valuelet (a1, b1) = value88

// One read on mutableValue, which may be duplicated according// to ECMA CLI spec.
let (a2, b2) = mutableValue// One read on volatileMutableValue, which may not be duplicated.
let (a3, b3) = volatileMutableValuea1, b1, a2, b2, a3, b3
member x.WriteValues() =// One read on mutableValue, which may be duplicated according// to ECMA CLI spec.
let (a2, b2) = mutableValue// One write on mutableValue.
mutableValue <- (a2 + 1, b2 + 1)// One read on volatileMutableValue, which may not be duplicated.
let (a3, b3) = volatileMutableValue// One write on volatileMutableValue.
volatileMutableValue <- (a3 + 1, b3 + 1)
let obj = ClassContainingMutableData()
Async.Parallel [ async { return obj.WriteValues() };async { return obj.WriteValues() };async { return obj.ReadValues() };async { return obj.ReadValues() } ]6.9.3 Zero Values
Some types have a zero value. The zero value is the“default” value for the type in the CLI execution environment.
The following types have the following zero values:For reference types, the null value.

For value types, the value with all fields set to the zero value for the type of the field. The zero value is also
computed by the F# library function Unchecked.defaultof<ty>.

6.9.4 Taking the Address of an Elaborated Expression
When the F# compiler determines the elaborated forms of certain expressions, it must compute a “reference” toan elaborated expression expr, written AddressOf(expr, mutation). The AddressOf operation is used internallywithin this specification to indicate the elaborated forms of address-of expressions, assignment expressions, andmethod and property calls on objects of variable and value types.
The AddressOf operation is computed as follows:

If expr has form path where path is a reference to a value with type byref<ty>, the elaborated form is&path.



If expr has form expra.field where field is a mutable, non-readonly CLI field, the elaborated form is&(AddressOf(expra).field).



If expr has form expra.[exprb] where the operation is an array lookup, the elaborated form is&(AddressOf(expra).[exprb]).



If expr has any other form, the elaborated form is &v,where v is a fresh mutable local value that is initialized
by adding let v = expr to the overall elaborated form for the entire assignment expression. Thisinitialization is known as a defensive copy of an immutable value. If expr is a struct, expr is copied each89

time the AddressOf operation is applied, which results in a different address each time. To keep the struct inplace, the field that contains it should be marked as mutable.
The AddressOf operation is computed with respect to mutation, which indicates whether the relevant elaborated
form uses the resulting pointer to change the contents of memory. This assumption changes the errors andwarnings reported.


If mutation is DefinitelyMutates, then an error is given if a defensive copy must be created.



If mutation is PossiblyMutates, then a warning is given if a defensive copy arises.

An F# compiler can optionally upgrade PossiblyMutates to DefinitelyMutates for calls to property settersand methods named MoveNext and GetNextArg, which are the most common cases of struct-mutators in CLI
library design. This is done by the F# 2.0 compiler.
Note:In F# 2.0, the warning “copy due to possible mutation of value type” is a level 4 warning and is
not reported when using the default settings of the F# compiler. This is because the majority ofvalue types in CLI libraries are immutable. This is warning number 52 in the F# 2.0 implementation.
CLI libraries do not include metadata to indicate whether a particular value type is immutable.
Unless a value is held in arrays or locations marked mutable, or a value type is known to be
immutable to the F# compiler, F# inserts copies to ensure that inadvertent mutation does not occur.

6.9.5 Evaluating Value References
At runtime, an elaborated value reference v is evaluated by looking up the value of v in the local environment.

6.9.6 Evaluating Function Applications
At runtime, an elaborated application of a function f e1 ... en is evaluated as follows:

The expressions f and e1 ... en, are evaluated.

If f evaluates to a function value with closure environment E, arguments v1 ... vm, and body expr, where m<= n, then E is extended by mapping v1 ... vm to the argument values for e1 ... em. The expression expr isthen evaluated in this extended environment and any remaining arguments applied.

If f evaluates to a function value with more than n arguments, then a new function value is returned with anextended closure mapping n additional formal argument names to the argument values for e1 ... em.

The result of calling the obj.GetType() method on the resulting object is under-specified (see §6.9.24).

6.9.7 Evaluating Method Applications
At runtime an elaborated application of a method is evaluated as follows:

The elaborated form is e0.M(e1,…,en) for an instance method or M(e1,…,en) for a static method.

The (optional) e0 and e1,…,en are evaluated in order.



If e0 evaluates to null, a NullReferenceException is raised.



If the method is declared abstract—that is, if it is a virtual dispatch slot—then the body of the member ischosen according to the dispatch maps of the value of e0 (§14.8).

The formal parameters of the method are mapped to corresponding argument values. The body of themethod member is evaluated in the resulting environment .

90

6.9.8 Evaluating Union Cases
At runtime, an elaborated use of a union case Case(e1,…,en) for a union type ty is evaluated as follows:

The expressions e1,…,en are evaluated in order.

The result of evaluation is an object value with union case label Case and fields given by the values of
e1,…,en.



If the type ty uses null as a representation (§5.4.8) and Case is the single union case without arguments,
the generated value is null.



The runtime type of the object is either ty or an internally generated type that is compatible with ty.

6.9.9 Evaluating Field Lookups
At runtime, an elaborated lookup of a CLI or F# fields is evaluated as follows:

The elaborated form is expr.F for an instance field or F for a static field.

The(optional) expr is evaluated.



If expr evaluates to null, a NullReferenceException is raised.

The value of the field is read from either the global field table or the local field table associated with theobject.

6.9.10 Evaluating Array Expressions
At runtime, an elaborated array expression [| e1; … ; en |]ty is evaluated as follows:Each expression e1 … en is evaluated in order.

The result of evaluation is a new array of runtime type ty[] that contains the resulting values in order.

6.9.11 Evaluating Record Expressions
At runtime, an elaborated record construction { field1 = e1; … ; fieldn = en }ty is evaluated as follows:Each expression e1 … en is evaluated in order.

The result of evaluation is an object of type ty with the given field values

6.9.12 Evaluating Function Expressions
At runtime, an elaborated function expression (fun v1 … vn -> expr) is evaluated as follows:

The expression evaluates to a function object with a closure that assigns values to all variables that arereferenced in expr and a function body that is expr.



The values in the closure are the current values of those variables in the execution environment.

The result of calling the obj.GetType() method on the resulting object is under-specified (see §6.9.24).

6.9.13 Evaluating Object Expressions
At runtime, elaborated object expressions{ new ty0 args-expropt object-members
interface ty1 object-members1…
interface tyn object-membersn }is evaluated as follows:91The expression evaluates to an object whose runtime type is compatible with all of the tyi and which has thecorresponding dispatch map (§14.8). If present, the base construction expression ty0 (args-expr) is
executed as the first step in the construction of the object.

The object is given a closure that assigns values to all variables that are referenced in expr.



The values in the closure are the current values of those variables in the execution environment.

The result of calling the obj.GetType() method on the resulting object is under-specified (see §6.9.24).

6.9.14 Evaluating Definition Expressions
At runtime, each elaborated definition pat = expr is evaluated as follows:

The expression expr is evaluated.

The expression is then matched against pat to produce a value for each variable pattern (§7.2) in pat.



These mappings are added to the local environment.

6.9.15 Evaluating Integer For Loops
At runtime, an integer for loop for var = expr1 to expr2 do expr3 done is evaluated as follows:

Expressions expr1 and expr2 are evaluated once to values v1 and v2.



The expression expr3 is evaluated repeatedly with the variable var assigned successive values in the rangeof v1 up to v2.



If v1 is greater than v2, then expr3 is never evaluated.

6.9.16 Evaluating While Loops
As runtime, while-loops while expr1 do expr2 done are evaluated as follows:

Expression expr1 is evaluated to a value v1.



If v1 is true, expression expr2 is evaluated, and the expression while expr1 do expr2 done is evaluatedagain.



If v1 is false, the loop terminates and the resulting value is null (the representation of the only value oftype unit)

6.9.17 Evaluating Static Coercion Expressions
At runtime, elaborated static coercion expressions of the form expr :> ty are evaluated as follows:

Expression expr is evaluated to a value v.

If the static type of e is a value type, and ty is a reference type, v is boxed; that is, v is converted to an
object on the heap with the same field assignments as the original value. The expression evaluates to areference to this object.

Otherwise, the expression evaluates to v.

6.9.18 Evaluating Dynamic Type-Test Expressions
At runtime, elaborated dynamic type test expressions expr :? ty are evaluated as follows:
1.

Expression expr is evaluated to a value v.

92

2.

3.

If v is null, then:

If tye uses null as a representation (§5.4.8), the result is true.

Otherwise the expression evaluates to false.

If v is not null and has runtime type vty which dynamically converts to ty (§5.4.10), the expression
evaluates to true. However, if ty is an enumeration type, the expression evaluates to true if and only if ty
is precisely vty.

6.9.19 Evaluating Dynamic Coercion Expressions
At runtime, elaborated dynamic coercion expressions expr :?> ty are evaluated as follows:
1.

Expression expr is evaluated to a value v.

2.

If v is null:3.



If tye uses null as a representation (§5.4.8), the result is the null value.

Otherwise a NullReferenceException is raised.

If v is not null:

If v has dynamic type vty which dynamically converts to ty (§5.4.10), the expression evaluates to the
dynamic conversion of v to ty.
oIf vty is a reference type and ty is a value type, then v is unboxed; that is, v is converted froman object on the heap to a struct value with the same field assignments as the object. The
expression evaluates to this value.

oOtherwise, the expression evaluates to v.

Otherwise an InvalidCastException is raised.

Expressions of the form expr :?> ty evaluate in the same way as the F# library function unbox<ty>(expr).
Note: Some F# types—most notably the option<_> type—use null as a representation for
efficiency reasons (§5.4.8),. For these types, boxing and unboxing can lose type distinctions. For
example, contrast the following two examples:> (box([]:string list) :?> int list);;System.InvalidCastException…
> (box(None:string option) :?> int option);;val it : int option = None
In the first case, the conversion from an empty list of strings to an empty list of integers (after firstboxing) fails. In the second case, the conversion from a string option to an integer option (after firstboxing) succeeds.

6.9.20 Evaluating Sequential Execution Expressions
At runtime, elaborated sequential expressions expr1; expr2 are evaluated as follows:

The expression expr1 is evaluated for its side effects and the result is discarded.

The expression expr2 is evaluated to a value v2 and the result of the overall expression is v2.

6.9.21 Evaluating Try-with Expressions
At runtime, elaborated try-with expressions try expr1 with rules are evaluated as follows:

The expression expr1 is evaluated to a value v1.

93If no exception occurs, the result is the value v1.



If an exception occurs, the pattern rules are executed against the resulting exception value.


If no rule matches, the exception is reraised.



If a rule pat -> expr2 matches, the mapping pat = v1 is added to the local environment, andexpr2 is evaluated.

6.9.22 Evaluating Try-finally Expressions
At runtime, elaborated try-finally expressions try expr1 finally expr2 are evaluated as follows:

The expression expr1 is evaluated.


If the result of this evaluation is a value v , then expr2 is evaluated.
1)

If this evaluation results in an exception, then the overall result is that exception.

2)

If this evaluation does not result in an exception, then the overall result is v.

If the result of this evaluation is an exception, then expr2 is evaluated.
3)

If this evaluation results in an exception, then the overall result is that exception.

4)

If this evaluation does not result in an exception, then the original exception is re-raised.

6.9.23 Evaluating AddressOf Expressions
At runtime, an elaborated address-of expression is evaluated as follows. First, the expression has one of thefollowing forms:&path where path is a static field.

&(expr.field)

&(expra.[exprb])

&v where v is a local mutable value.

The expression evaluates to the address of the referenced local mutable value, mutable field, or mutable static
field.
Note: The underlying CIL execution machinery that F# uses supports covariant arrays, asevidenced by the fact that the type string[] dynamically converts to obj[] (§5.4.10). Although
this feature is rarely used in F#, its existence means that array assignments and taking the addressof array elements may fail at runtime with a System.ArrayTypeMismatchException if the runtime
type of the target array does not match the runtime type of the element being assigned. For
example, the following code fails at runtime:
let F(x: byref<obj>) = ()let a = Array.zeroCreate<obj> 10
let b = Array.zeroCreate<string> 10
F(&a.[0])let bb = ((b :> obj) :?> obj[])// The next line raises a System.ArrayTypeMismatchException exception.
F(&bb.[1])6.9.24 Values with Underspecified Object Identity and Type Identity
The CLI and F# support operations that detect object identity—that is, whether two object references refer to thesame “physical” object. For example, System.Object.ReferenceEquals(obj1, obj2) returns true if the two
object references refer to the same object. Similarly,
System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode() returns a hash code that is partly

94based on physical object identity, and the AddHandler and RemoveHandler operations (which register andunregister event handlers) are based on the object identity of delegate values.
The results of these operations are underspecified when used with values of the following F# types:


Function types



Tuple types



Immutable record types

Union types



Boxed immutable value types

For two values of such types, the results of System.Object.ReferenceEquals andSystem.Runtime.CompilerServices.RuntimeHelpers.GetHashCode are underspecified; however, theoperations terminate and do not raise exceptions. An implementation of F# is not required to define the results ofthese operations for values of these types.
For function values and objects that are returned by object expressions, the results of the following operations
are underspecified in the same way:

Object.GetHashCode()Object.GetType()

For union types the results of the following operations are underspecified in the same way:

Object.GetType()

95

Patterns
Patterns are used to perform simultaneous case analysis and decomposition on values together with the match,try...with, function, fun, and let expression and declaration constructs. Rules are attempted in order from
top to bottom and left to right. The syntactic forms of patterns are shown in the subsequent table.
rule :=
pat pattern-guardopt -> expr-- pattern, optional guard and action

pattern-guard := when expr
pat :=
const
long-ident pat-paramopt patopt_
pat as ident
pat '|' patpat '&' patpat :: pat
pat : typepat,...,pat(pat)list-patarray-patrecord-pat:? atomic-type:? atomic-type as ident
null
attributes pat-- constant pattern-- named pattern-- wildcard pattern-- "as" pattern-- disjunctive pattern-- conjunctive pattern-- "cons" pattern-- pattern with type constraint-- tuple pattern-- parenthesized pattern-- list pattern-- array pattern-- record pattern-- dynamic type test pattern-- dynamic type test pattern-- null-test pattern-- pattern with attributes

list-pat :=[ ][ pat ; ... ; pat ]array-pat :=[| |][| pat ; ... ; pat |]record-pat :={ field-pat ; ... ; field-pat }atomic-pat :=pat :one ofconst long-ident:? atomic-typenull _

list-patrecord-pat

field-pat := long-ident = pat
pat-param :=| const| long-ident| [ pat-param ; ... ; pat-param ]| ( pat-param, ..., pat-param )| long-ident pat-param| pat-param : type
| <@ expr @>| <@@ expr @@>| nullpats := pat , ... , patfield-pats := field-pat ; ... ; field-pat

array-pat(pat)rules := '|'opt rule '|' ... '|' rule
Patterns are elaborated to expressions through a process called pattern match compilation. This reduces patternmatching to decision trees which operate on an input value, called the pattern input. The decision tree is made upof the following constructs:
Conditionals on integers and other constants

Switches on union cases

Conditionals on runtime types

Null testsValue definitions

An array of pattern-match targets referred to by index

7.1 Simple Constant Patterns
The pattern const is a constant pattern which matches values equal to the given constant. For example:
let rotate3 x =match x with
| 0 -> "two"| 1 -> "zero"| 2 -> "one"| _ -> failwith "rotate3"
In this example, the constant patterns are 0, 1, and 2. Any constant listed in §6.3.1 may be used as a constantpattern except for integer literals that have the suffixes Q, R, Z, I, N, G.
Simple constant patterns have the corresponding simple type. Such patterns elaborate to a call to the F#
structural equality function Microsoft.FSharp.Core.Operators.(=) with the pattern input and the constant asarguments. The match succeeds if this call returns true; otherwise, the match fails.
Note: The use of Microsoft.FSharp.Core.Operators.(=) means that CLI floating-point equality
is used to match floating-point values, and CLI ordinal string equality is used to match strings.

7.2 Named Patterns
Patterns in the following forms are named patterns:
Long-ident
Long-ident pat
Long-ident pat-params pat
If long-ident is a single identifier that does not begin with an uppercase character, it is interpreted as a variable
pattern. During checking, the variable is assigned the same value and type as the pattern input.
If long-ident is more than one-character long or begins with an uppercase character (that is, if
System.Char.IsUpperInvariant is true and System.Char.IsLowerInvariant is false on the firstcharacter), it is resolved by using Name Resolution in Patterns (§14.1.6). This algorithm produces one of thefollowing:

A union caseAn exception label98

An active pattern case name

A literal value

Otherwise, long-ident must be a single uppercase identifier ident. In this case, pat is a variable pattern. An
F# implementation may optionally generate a warning if the identifier is uppercase. Such a warning is
recommended if the length of the identifier is greater than two.
After name resolution, the subsequent treatment of the named pattern is described in the following sections.

7.2.1 Union Case Patterns
If long-ident from §7.2 resolves to a union case, the pattern is a union case pattern. If long-ident resolves toa union case Case, then long-ident and long-ident pat are patterns that match pattern inputs that haveunion case label Case. The long-ident form is used if the corresponding case takes no arguments, and thelong-ident pat form is used if it takes arguments.
At runtime, if the pattern input is an object that has the corresponding union case label, the data values carried by
the union are matched against the given argument patterns.
For example:
type Data =| Kind1 of int * int| Kind2 of string * string
let data = Kind1(3, 2)
let result =match data with| Kind1 (a, b) -> a + b| Kind2 (s1, s2) -> s1.Length + s2.Length
In this case, result is given the value 5.

7.2.2 Literal Patterns
If long-ident from §7.2 resolves to a literal value, the pattern is a literal pattern. The pattern is equivalent to thecorresponding constant pattern.
In the following example, the Literal attribute (§10.2.2) is first used to define two literals, and these literals areused as identifiers in the match expression:[<Literal>]let Case1 = 1[<Literal>]let Case2 = 100
let result =match 100 with| Case1 -> "Case1"| Case2 -> "Case2"| _ -> "Some other case"In this case, result is given the value "Case2”.

99

7.2.3 Active Patterns
If long-ident from §7.2 resolves to an active pattern case name CaseNamei then the pattern is an active pattern.
The rules for name resolution in patterns (§14.1.6) ensure that CaseNamei is associated with an active patternfunction f in one of the following forms:
(|CaseName|) inp
Single case. The function accepts one argument (the value being matched) and can return any type.

(|CaseName|_|) inp
Partial. The function accepts one argument (the value being matched) and must return a value of type
Microsoft.FSharp.Core.option<_>(|CaseName1| ...|CaseNamen|) inp
Multi-case. The function accepts one argument (the value being matched), and must return a value of type
Microsoft.FSharp.Core.Choice<_,...,_> based on the number of case names. In F# 2.0, the limitationn ≤ 7 applies.

(|CaseName|) arg1 ... argn inp
Single case with parameters. The function accepts n+1 arguments, where the last argument (inp) is thevalue to match, and can return any type.

(|CaseName|_|) arg1 ... argn inp
Partial with parameters. The function accepts n+1 arguments, where the last argument (inp) is the value tomatch, and must return a value of type Microsoft.FSharp.Core.option<_>.

Other active pattern functions are not permitted. In particular, multi-case, partial functions such as the following
are not permitted:(|CaseName1| ... |CaseNamen|_|)When an active pattern function takes arguments, the pat-params are interpreted as expressions that are
passed as arguments to the active pattern function. The pat-params are converted to the syntactically identicalcorresponding expression forms and are passed as arguments to the active pattern function f.
At runtime, the function f is applied to the pattern input, along with any parameters. The pattern matches if theactive pattern function returns v, ChoicekOfN v, or Some v, respectively, when applied to the pattern input. If thepattern argument pat is present, it is then matched against v.
The following example shows how to define and use a partial active pattern function:let (|Positive|_|) inp = if inp > 0 then Some(inp) else Nonelet (|Negative|_|) inp = if inp < 0 then Some(-inp) else None
match 3 with| Positive n -> printfn "positive, n = %d" n| Negative n -> printfn "negative, n = %d" n| _-> printfn "zero"The following example shows how to define and use a multi-case active pattern function:let (|A|B|C|) inp = if inp < 0 then A elif inp = 0 then B else C
match 3 with
| A -> "negative"| B -> "zero"| C -> "positive"
The following example shows how to define and use a parameterized active pattern function:

100let (|MultipleOf|_|) n inp = if inp%n = 0 then Some (inp / n) else None
match 16 with
| MultipleOf 4 n -> printfn "x = 4*%d" n| _ -> printfn "not a multiple of 4"
An active pattern function is executed only if a left-to-right, top-to-bottom reading of the entire pattern indicatesthat execution is required. For example, consider the following active patterns:let (|A|_|) x =if x = 2 then failwith "x is two"
elif x = 1 then Some()
else Nonelet (|B|_|) x =if x=3 then failwith "x is three" else Nonelet (|C|) x = failwith "got to C"
let f x =match x with
| 0 -> 0| A -> 1| B -> 2| C -> 3| _ -> 4
These patterns evaluate as follows:
f 0 // 0f 1 // 1f 2 // failwith "x is two"f 3 // failwith "x is three"f 4 // failwith "got to C"
An active pattern function may be executed multiple times against the same pattern input during resolution of asingle overall pattern match. The precise number of times that the active pattern function is executed against aparticular pattern input is implementation-dependent.

7.3 “As” Patterns
An “as” pattern is of the following form:
pat as ident
The “as” pattern defines ident to be equal to the pattern input and matches the pattern input against pat. For
example:let t1 = (1, 2)
let (x, y) as t2 = t1
printfn "%d-%d-%A" x y t2// 1-2-(1, 2)This example binds the identifiers x, y, and t1 to the values 1, 2, and (1,2), respectively.

101

7.4 Wildcard Patterns
The pattern _ is a wildcard pattern and matches any input. For example:
let categorize x =match x with
| 1 -> 0| 0 -> 1| _ -> 0
In the example, if x is 0, the match returns 1. If x has any other value, the match returns 0.

7.5 Disjunctive Patterns
A disjunctive pattern matches an input value against one or the other of two patterns:
pat | patAt runtime, the patterm input is matched against the first pattern. If that fails, the pattern input is matched against
the second pattern. Both patterns must bind the same set of variables with the same types. For example:
type Date = Date of int * int * int
let isYearLimit date =match date with| (Date (year, 1, 1) | Date (year, 12, 31)) -> Some year
| _ -> None
let result = isYearLimit (Date (2010,12,31))
In this example, result is given the value true, because the pattern input matches the second pattern.

7.6 Conjunctive Patterns
A conjunctive pattern matches the pattern input against two patterns.
pat1 & pat2
For example:
let (|MultipleOf|_|) n inp = if inp%n = 0 then Some (inp / n) else None
let result =match 56 with
| MultipleOf 4 m & MultipleOf 7 n -> m + n| _ -> false
In this example, result is given the value 22 (= 16 + 8), because the pattern input match matches both patterns.

7.7 List Patterns
The pattern pat :: pat is a union case pattern that matches the “cons” union case of F# list values.
The pattern [] is a union case pattern that matches the “nil” union case of F# list values.
The pattern [pat1 ; ... ; patn] is shorthand for a series of :: and empty list patterns pat1 :: … :: patn ::[].
102

For example:
let rec count x =match x with| [] -> 0| h :: t -> h + count t
let result1 = count [1;2;3]
let result2 =match [1;2;3] with
| [a;b;c] -> a + b + c| _ -> 0
In this example, both result1 and result2 are given the value 6.

7.8 Type-Annotated Patterns
A type-annotated pattern specifies the type of the value to match to a pattern.
pat : typeFor example:
let rec sum xs =match xs with| [] -> 0| (h : int) :: t -> h + sum t
In this example, the initial type of h is asserted to be equal to int before the pattern h is checked. Through type
inference, this in turn implies that xs and t have static type int list, and sum has static type
int list -> int.

7.9 Dynamic Type-Test Patterns
Dynamic type-test patterns have the following two forms::? type:? type as ident
A dynamic type-test pattern matches any value whose runtime type is type or a subtype of type. For example:
let message (x : System.Exception) =match x with
| :? System.OperationCanceledException -> "cancelled"| :? System.ArgumentException -> "invalid argument"| _ -> "unknown error"
If the type-test pattern is of the form :? type as ident, then the value is coerced to the given type and ident isbound to the result. For example:
let findLength (x : obj) =match x with
| :? string as s -> s.Length| _ -> 0
In the example, the identifier s is bound to the value x with type string.
If the pattern input has type tyin, pattern checking uses the same conditions as both a dynamic type-test
expression e :? type and a dynamic coercion expression e :?> type where e has type tyin. An error occurs

103

if type cannot be statically determined to be a subtype of the type of the pattern input. A warning occurs if thetype test will always succeed based on type and the static type of the pattern input.
A warning is issued if an expression contains a redundant dynamic type-test pattern, after any coercion isapplied. For example:
match box "3" with
| :? string -> 1
| :? string -> 1 // a warning is reported that this rule is "never matched"| _ -> 2
match box "3" with
| :? System.IComparable -> 1
| :? string -> 1 // a warning is reported that this rule is "never matched"| _ -> 2
At runtime, a dynamic type-test pattern succeeds if and only if the corresponding dynamic type-test expression
e :? ty would return true where e is the pattern input. The value of the pattern is bound to the results of a
dynamic coercion expression e :?> ty.

7.10 Record Patterns
The following is a record pattern:{ long-ident1 = pat1; ... ; long-identn = patn}
For example:
type Data = { Header:string; Size: int; Names: string list }let totalSize data =match data with
| { Header = "TCP"; Size = size; Names = names } -> size + names.Length * 12
| { Header = "UDP"; Size = size } -> size
| _ -> failwith "unknown header"
The long-identi are resolved in the same way as field labels for record expressions and must together identify
a single, unique F# record type. Not all record fields for the type need to be specified in the pattern.

7.11 Array Patterns
An array pattern matches an array of a partciular length:[|pat ; ... ; pat|]
For example:
let checkPackets data =match data with
| [| "HeaderA"; data1; data2 |] -> (data1, data2)| [| "HeaderB"; data2; data1 |] -> (data1, data2)| _ -> failwith "unknown packet"

1047.12 Null Patterns
The null pattern null matches values that are represented by the CLI value null. For example:
let path =match System.Environment.GetEnvironmentVariable("PATH") with| null -> failwith "no path set!"| res -> res
Most F# types do not use null as a representation; consequently, the null pattern is generally used to checkvalues passed in by CLI method calls and properties. For a list of F# types that use null as a representation, see§5.4.8.

7.13 Guarded Pattern Rules
Guarded pattern rules have the following form:
pat when expr
For example:
let categorize x =match x with| _ when x < 0 -> -1| _ when x < 0 -> 1
| _ -> 0
The guards on a rule are executed only after the match value matches the corresponding pattern. For example,
the following evaluates to 2 with no output.
match (1, 2) with| (3, x) when (printfn "not printed"; true) -> 0
| (_, y) -> y105

Type Definitions
Type definitions define new named types. The grammar of type definitions is shown below.
type-defn :=abbrev-type-defn
record-type-defn
union-type-defn
anon-type-defn
class-type-defn
struct-type-defn
interface-type-defn
enum-type-defn
delegate-type-defn
type-extension
type-name :=attributesopt accessopt ident typar-defnsopt
abbrev-type-defn :=type-name = typeunion-type-defn :=type-name '=' union-type-cases type-extension-elementsopt
union-type-cases:='|'opt union-type-case '|' ... '|' union-type-caseunion-type-case :=attributesopt union-type-case-data
union-type-case-data :=ident
ident of type * ... * typeident : uncurried-sig-- null union case-- n-ary union case-- n-ary union caserecord-type-defn :=type-name = '{' record-fields '}' type-extension-elementsopt
record-fields :=record-field ; ... ; record-field ;opt
record-field :=attributesopt mutableopt accessopt ident : typeanon-type-defn :=type-name primary-constr-argsopt object-valopt '=' begin class-type-body end
class-type-defn :=type-name primary-constr-argsopt object-valopt '=' class class-type-body end
as-defn := as ident
class-type-body :=class-inherits-declopt class-function-or-value-defnsopt type-defn-elementsopt
class-inherits-decl := inherit type exproptclass-function-or-value-defn :=attributesopt staticopt let recopt function-or-value-defns
attributesopt staticopt do expr

struct-type-defn :=type-name primary-constr-argsopt as-defnopt '=' struct struct-type-body end
struct-type-body := type-defn-elementsinterface-type-defn :=type-name '=' interface interface-type-body end
interface-type-body := type-defn-elements
exception-defn :=attributesopt exception union-type-case-dataattributesopt exception ident = long-ident-- exception definition-- exception abbreviation

enum-type-defn :=type-name '=' enum-type-cases
enum-type-cases ='|'opt enum-type-case '|' ... '|' enum-type-caseenum-type-case :=ident '=' const-- enum constant definitiondelegate-type-defn :=type-name '=' delegate-sig
delegate-sig :=delegate of uncurried-sig-- CLI delegate definition

type-extension :=type-name type-extension-elements
type-extension-elements := with type-defn-elements end
type-defn-element :=member-defn
interface-impl
interface-spec
type-defn-elements :=type-defn-element ... type-defn-element

primary-constr-args :=attributesopt accessopt (simple-pat, ... , simple-pat)

simple-pat :=| ident| simple-pat : typeadditional-constr-defn :=attributesopt accessopt new pat as-defn = additional-constr-expr
additional-constr-expr :=stmt ';' additional-constr-expr-- sequence construction (after)
additional-constr-expr then expr-- sequence construction (before)
if expr then additional-constr-expr else additional-constr-expr
let function-or-value-defn in additional-constr-expr
additional-constr-init-expr
additional-constr-init-expr :='{' class-inherits-decl field-initializers '}'new type expr-- explicit construction-- delegated construction

member-defn :=attributesopt staticopt member accessopt method-or-prop-defn
108-- concrete member

attributesopt abstract memberopt accessopt member-sig -- abstract member
attributesopt override accessopt method-or-prop-defn-- override member
attributesopt default accessopt method-or-prop-defn-- override member
attributesopt staticopt val mutableopt accessopt ident : type -- value member
additional-constr-defn-- additional constructor
method-or-prop-defn :=ident.opt function-defn-- method definition
ident.opt value-defn-- property definition
ident.opt ident with function-or-value-defns-- property definition via
get/set methods
member-sig :=
ident typar-defnsopt : curried-sig
ident typar-defnsopt : curried-sig with get
ident typar-defnsopt : curried-sig with set
ident typar-defnsopt : curried-sig with get,set
ident typar-defnsopt : curried-sig with set,get
curried-sig :=args-spec -> ... -> args-spec -> typeuncurried-sig :=args-spec -> typeargs-spec :=arg-spec * ... * arg-spec
arg-spec :=attributesopt arg-name-specopt typearg-name-spec :=?opt ident :interface-spec :=interface typeFor example:
type int = System.Int32
type Color = Red | Green | Blue
type Map<'T> = { entries: 'T[] }Type definitions can be declared in:Module definitions

Namespace declaration groups

F# supports the following kinds of type definitions:Type abbreviations (§8.3)



Record type definitions (§8.4)

Union type definitions (§8.5)

Class type definitions (§8.6)

Interface type definitions (§8.7)

Struct type definitions (§8.8)

Enum type definitions (§8.9)

Delegate type definitions (§8.10)



Exception type definitions (§8.11)

109-- method or property signature-- property signature-- property signature-- property signature-- property signatureType extension definitions (§8.12)

Measure type definitions (§9.4)

With the exception of type abbreviations and type extension definitions, type definitions define fresh, named
types that are distinct from other types.
A type definition group defines several type definitions or extensions simultaneously:
type ... and ...
For example:
type RowVector(entries: seq<int>) =let entries = Seq.toArray entries
member x.Length = entries.Length
member x.Permute = ColumnVector(entries)
and ColumnVector(entries: seq<int>) =let entries = Seq.toArray entries
member x.Length = entries.Length
member x.Permute = RowVector(entries)
A type definition group can include any type definitions except for exception type definitions and module
definitions.
Most forms of type definitions may contain both static elements and instance elements. Static elements areaccessed by using the type definition. Within a static definition, only the static elements are in scope. Most
forms of type definitions may contain members (§8.13).
Custom attributes may be placed immediately before a type definition group, in which case they apply to the first
type definition, or immediately before the name of the type definition:[<Obsolete>] type X1() = class end
type [<Obsolete>] X2() = class end
and [<Obsolete>] Y2() = class end

8.1 Type Definition Group Checking and Elaboration
F# checks type definition groups by determining the basic shape of the definitions and then filling in the details. In
overview, a type definition group is checked as follows:
1.

2.

3.

For each type definition:

Determine the generic arguments, accessibility and kind of the type definition

Determine whether the type definition supports equality and/or comparison

Elaborate the explicit constraints for the generic parameters.

For each type definition:
Establish type abbreviations



Determine the base types and implemented interfaces of each new type definition

Detect any cyclic abbreviations

Verify the consistency of types in fields, union cases, and base types.

For each type definition:

Determine the union cases, fields, and abstract members (§8.14) of each new type definition

110



Check the union cases, fields, and abstract members themselves, as described in the corresponding
sections of this chapter.

4.

For each member, add items that represent the members to the environment as a recursive group.

5.

Check the members, function, and value definitions in order and apply incremental generalization.

In the context in which type definitions are checked, the type definition itself is in scope, as are all members andother accessible functionality of the type. This context enables recursive references to the accessible static
content of a type. It also enables recursive references to the accessible properties of any object that has thesame type as the type definition or a related type.
In more detail, given an initial environment env, a type definition group is checked as described in the following
paragraphs.
First, check the individual type definitions. For each type definition:
1.

Determine the number, names, and sorts of generic arguments of the type definition.
For each generic argument, if a Measure attribute is present, mark the generic argument as a measureparameter. The generic arguments are initially inference parameters, and additional constraints may beinferred for these parameters.

For each type definition T, the subsequent steps use an environment envT that is produced by adding
the type definitions themselves and the generic arguments for T to env.

2.

Determine the accessibility of the type definition.

3.

Determine and check the basic kind of the type definition, using Type Kind Inference if necessary (§8.2).

4.

Mark the type definition as a measure type definition if a Measure attribute is present.

5.

If the type definition is generic, infer whether the type definition supports equality and/or comparison.

6.

Elaborate and add the explicit constraints for the generic parameters of the type definition, and thengeneralize the generic parameters. Inference of additional constraints is not permitted.

7.

If the type definition is a type abbreviation, elaborate and establish the type being abbreviated.

8.

Check and elaborate any base types and implemented interfaces.

9.

If the type definition is a type abbreviation, check that the type abbreviation is not cyclic.

10. Check whether the type definition has a single, zero-argument constructor, and hence forms a type thatsatisfies the default constructor constraint.
11. Recheck the following to ensure that constraints are consist:The type being abbreviated, if any.

The explicit constraints for any generic parameters, if any.



The types and constraints occurring in the base types and implemented interfaces, if any.

12. Determine the union cases, fields, and abstract members, if any, of the type definition. Check and elaborate
the types that the union cases, fields, and abstract members include.
13. Make additional checks as defined elsewhere in this chapter. For example, check that the AbstractClass
attribute does not appear on a union type.
14. For each type definition that is a struct, class, or interface, check that the inheritance graph and the structinclusion graph are not cyclic. This check ensures that a struct does not contain itself and that a class orinterface does not inherit from itself. This check includes the following steps:a)Create a graph with one node for each type definition.

111

b)

Close the graph under edges.
(T, base-type-definition)(T, interface-type-definition)(T1, T2) where T1 is a struct and T2 is a type that would store a value of type T1 <…> for someinstantiation. Here “X storing Y” means that X is Y or is a struct type with an instance field that
stores Y.

c)

Check for cycles.

The special case of a struct S<typars> storing a static field of type S<typars> is allowed.
15. Collectively add the elaborated member items that represent the members for all new type definitions to theenvironment as a recursive group (§8.13), excluding interface implementation members.
16. If the type definition has a primary constructor, create a member item to represent the primary constructor.
After these steps are complete for each type definition, check the members. For each member:1.

If the member is in a generic type, create a copy of the type parameters for the generic type and add the
copy to the environment for that member.

2.

If the member has explicit type parameters, elaborate these type parameters and any explicit constraints.

3.

If the member is an override, default, or interface implementation member, apply dispatch-slot inference.

4.

If the member has syntactic parameters, assign an initial type to the elaborated member item based on thepatterns that specify arguments for the members.

5.

If the member is an instance member, assign a type to the instance variable.

Finally, check the function, value, and member definitions of each new type definition in order as a recursive
group.

8.2 Type Kind Inference
A type that is specified in one of the following ways has an anonymous type kind:By using begin and end on the right-hand side of the = token.

In lightweight syntax, with an implicit begin/end.

F# infers the kind of an anonymous type by applying the following rules, in order:
1.

If the type has a Class attribute, Interface attribute, or Struct attribute, this attribute identifies the kind of
the type.

2.

If the type has any concrete elements, the type is a class. Concrete elements are primary constructors,
additional object constructors, function definitions, value definitions, non-abstract members, and any
inherit declarations that have arguments.

3.

Otherwise, the type is an interface type.

For example:// This is implicitly an interface
type IName =abstract Name : string// This is implicitly a class, because it has a constructor

112

type ConstantName(n:string) =member x.Name = n// This is implicitly a class, because it has a constructor
type AbstractName(n:string) =abstract Name : string
default x.Name = "<no-name>"
If a type is not an anonymous type, any use of the Class attribute, Interface attribute, or Struct attribute mustmatch the class/end, interface/end, and struct/end tokens, if such tokens are present. These attributes
cannot be used with other kinds of type definitions such as type abbreviations, record, union, or enum types.

8.3 Type Abbreviations
Type abbreviations define new names for other types. For example:
type PairOfInt = int * int
Type abbreviations are expanded and erased during compilation and do not appear in the elaborated form of F#
declarations, nor can they be referred to or accessed at runtime.
The process of repeatedly eliminating type abbreviations in favor of their equivalent types must not result in an
infinite type derivation. For example, the following are not valid type definitions:type X = option<X>
type Identity<'T> = 'T
and Y = Identity<Y>
The constraints on a type abbreviation must satisfy any constraints that the abbreviated type requires.
For example, assuming the following declarations:type IA =abstract AbstractMember : int -> int
type IB =abstract AbstractMember : int -> int
type C<'T when 'T :> IB>() =static member StaticMember(x : 'a) = x.AbstractMember(1)the following is permitted:
type D<'T when 'T :> IB> = C<'T>
whereas the following is not permitted:
type E<'T> = C<'T>// invalid: missing constraint

Type abbreviations can define additional constraints, so the following is permitted:
type F<'T when 'T :> IA and 'T :> IB> = C<'T>
The right side of a type abbreviation must use all the declared type variables that appear on the left side. For thispurpose, the order of type variables that are used on the right-hand side of a type definition is determined by theirleft-to-right occurrence in the type.
For example, the following is not a valid type abbreviation.
type Drop<'T,'U> = 'T * 'T // invalid: dropped type variable113Note: This restriction simplifies the process of guaranteeing a stable and consistent compilation to
generic CLI code.
Flexible type constraints #type may not be used on the right side of a type abbreviation, because they expand toa type variable that has not been named in the type arguments of the type abbreviation. For example, thefollowing type is disallowed:
type BadType = #Exception -> int// disallowed

Type abbreviations may be declared internal or private.
Note: Private type abbreviations are still, for all purposes, considered equivalent to the abbreviatedtypes.

8.4 Record Type Definitions
A record type definition introduces a type in which all the inputs that are used to construct a value are accessible
as properties on values of the type. For example:
type R1 ={ x : int;y : int }member this.Sum = this.x + this.yIn this example, the integers x and y can be accessed as properties on values of type R1.
Record fields may be marked mutable. For example:
type R2 ={ mutable x : int;
mutable y : int }member this.Move(dx,dy) =this.x <- this.x + dx
this.y <- this.y + dy
The mutable attribute on x and y makes the assignments valid.
Record types are implicitly sealed and may not be given the Sealed attribute. Record types may not be given the
AbstractClass attribute.
Record types are implicitly marked serializable unless the AutoSerializable(false) attribute is used.

8.4.1 Members in Record Types
Record types may declare members (§8.13), overrides, and interface implementations. Like all types withoverrides and interface implementations, they are subject to Dispatch Slot Checking (§14.8).

8.4.2 Name Resolution and Record Field Labels
For a record type, the record field labels field1 ... fieldN are added to the FieldLabels table of the current nameresolution environmentunless the record type has the RequireQualifiedAccess attribute.
Record field labels in the FieldLabels table play a special role in Name Resolution for Members (§14.1): an
expression’s type may be inferred from a record label. For example:
type R = { dx : int; dy: int }let f x = x.dx // x is inferred to have type R
In this example, the lookup .dx is resolved to be a field lookup.

114

8.4.3 Structural Hashing, Equality, and Comparison for Record Types
Record types implicitly implement the following interfaces and dispatch slots unless they are explicitly
implemented as part of the definition of the record type:interface System.Collections.IStructuralEquatableinterface System.Collections.IStructuralComparable
interface System.IComparable
override GetHashCode : unit -> int
override Equals : obj -> bool
The implicit implementations of these interfaces and overrides are described in §8.15.

8.4.4 With/End in Record Type Definitions
Record type definitions can include with/end tokens, as the following shows:type R1 ={ x : int;y : int }withmember this.Sum = this.x + this.yend
The with/end tokens can be omitted if the type-defn-elements vertically align with the { in the recordfields. The semicolon (;) tokens can be omitted if the next record-field vertically aligns with the previous
record-field.

8.5 Union Type Definitions
A union type definition is a type definition that includes one or more union cases. For example:
type Message =| Result of string| Request of int * string
member x.Name = match x with Result(nm) -> nm | Request(_,nm) -> nm
Union case names must begin with an uppercase letter, which is defined to mean any character for which the CLI
library function System.Char.IsUpper returns true and System.Char.IsLower returns false.
The union cases Case1 ... CaseN have module scope and are added to the ExprItems and PatItems tables in thename resolution environment. This means that their unqualified names can be used to form both expressions andpatterns, unless the record type has the RequireQualifiedAccess attribute.
Parentheses are significant in union definitions. Thus, the following two definitions differ:type CType = C of int * int
type CType = C of (int * int)
The lack of parentheses in the first example indicates that the union case takes two arguments. The parentheses
in the second example indicate that the union case takes one argument that is a first-class tuple value.
The following declaration defines a type abbreviation if the named type A exists in the name resolution
environment. Otherwise it defines a union type.
type OneChoice = A
To disambiguate this case and declare an explicit union type, use the following:type OneChoice =
| A

115

Union types are implicitly marked serializable unless the AutoSerializable(false) attribute is used.

8.5.1 Members in Union Types
Union types may declare members (§8.13), overrides, and interface implementations. As with all types thatdeclare overrides and interface implementations, they are subject to Dispatch Slot Checking (§14.8).

8.5.2 Structural Hashing, Equality, and Comparison for Union Types
Union types implicitly implement the following interfaces and dispatch slots unless they are explicitly implemented
as part of the definition of the union type:interface System.Collections.IStructuralEquatableinterface System.Collections.IStructuralComparable
interface System.IComparable
override GetHashCode : unit -> int
override Equals : obj -> bool
The implicit implementations of these interfaces and overrides are described in §8.15.

8.5.3 With/End in Union Type Definitions
Union type definitions can include with/end tokens, as the following shows:type R1 ={ x : int;y : int }withmember this.Sum = this.x + this.yend
The with/end tokens can be omitted if the type-defn-elements vertically align with the { in the recordfields. The semicolon (;) tokens can be omitted if the next record-field vertically aligns with the previous
record-field.
For union types, the with/end tokens can be omitted if the type-defn-elements vertically alignwith the first | inthe union-type-cases. However, with/end must be present if the | tokens align with the type token. For
example:/// Note: this layout is permitted
type Message =| Result of string| Request of int * string
member x.Name = match x with Result(nm) -> nm | Request(_,nm) -> nm/// Note: this layout is not permitted
type Message =| Result of string| Request of int * string
member x.Name = match x with Result(nm) -> nm | Request(_,nm) -> nm

8.5.4 Compiled Form of Union Types for Use from Other CLI Languages
A compiled union type U has:

One CLI static getter property U.C for each null union case C. This property gets a singleton object that
represents each such case.



One CLI nested type U.C for each non-null union case C. This type has instance properties Item1, Item2....
for each field of the union case, or a single instance property Item if there is only one field. However, a116compiled union type that has only one case does not have a nested type. Instead, the union type itself plays
the role of the case type.


One CLI static method U.NewC for each non-null union case C. This method constructs an object for that
case.



One CLI instance property U.IsC for each case C. This property returns true or false for the case.



One CLI instance property U.Tag for each case C. This property fetches or computes an integer tag
corresponding to the case.



If U has more than one case, it has one CLI nested type U.Tags. The U.Tags typecontains one integer literalfor each case, in increasing order starting from zero.

A compiled union type has the methods that are required to implement its auto-generated interfaces, inaddition to any user-defined properties or methods.

These methods and properties may not be used directly from F#. However, these types have user-facing
List.Empty, List.Cons, Option.None, and Option.Some properties and/or methods.
A compiled union type may not be used as a base type in another CLI language, because it has at least one
assembly-private constructor and no public constructors.

8.6 Class Type Definitions
A class type definition encapsulates values that are constructed by using one or more object constructors. Class
types have the form:
type type-name patopt as-defnopt =classclass-inherits-declopt
class-function-or-value-defnsopt
type-defn-elements
end
The class/end tokens can be omitted, in which case Type Kind Inference (§8.2) is used to determine the kind of
the type.
In F#, class types are implicitly marked serializable unless the AutoSerializable(false) attribute is present.

8.6.1 Primary Constructors in Classes
An object constructor represents a way of initializing an object. Object constructors can create values of the typeand can partially initialize an object from a subclass. A class can have an optional primary constructor and zeroor more additional object constructors.
If a type definition has a pattern immediately after the type-name and any accessibility annotation, then it has a
primary constructor. For example, the following type has a primary constructor:
type Vector2D(dx : float, dy : float) =let length = sqrt(dx*x + dy*dy)
member v.Length = length
member v.DX = dx
member v.DY = dyClass definitions that have a primary constructor may contain function and value definitions, including those thatuse let rec.
The pattern for a primary constructor must have zero or more patterns of the following form:(simple-pat, ..., simple-pat)117

Each simple-pat has this form:
simple-pat :=| ident| simple-pat : typeSpecifically, nested patterns may not be used in the primary constructor arguments. For example, the following is
not permitted because the primary constructor arguments contain a nested tuple pattern:
type TwoVectors((px, py), (qx, qy)) =member v.Length = sqrt((qx-px)*(qx-px) + (qy-py)*(qy-py))
Instead, one or more value definitions should be used to accomplish the same effect:
type TwoVectors(pv, qv) =let (px, py) = pv
let (qx, qy) = qv
member v.Length = sqrt((qx-px)*(qx-px) + (qy-py)*(qy-py))
When a primary constructor is evaluated, the inheritance and function and value definitions are evaluated inorder.

8.6.1.1

Object References in Primary Constructors

For types that have a primary constructor, the name of the object parameter can be bound and used in the nonnon-static function, value and member definitions of the type definition as follows:
type X(a:int) as x =let mutable currentA = a
let mutable currentB = 0
do x.B <- x.A + 3
member self.GetResult()= currentA + currentB
member self.A with get() = currentA and set v = currentA <- v
member self.B with get() = currentB and set v = currentB <- v
During construction, no member on the type may be called before the last value or function definition in the typehas completed; such a call results in an InvalidOperationException. For example, the following code raises
this exception:
type C() as self =let f = (fun (x:C) -> x.F())
let y = f self
do printfn "construct"member this.F() = printfn "hi, y = %A" ylet r = new C() // raises InvalidOperationException
The exception is raised because an attempt may be made to access the value of the field y before initialization iscomplete.

8.6.1.2

Inheritance Declarations in Primary Constructors

An inherit declaration specifies that the type being defined is an extension of an existing type. Suchdeclarations have the following form:
class-inherits-decl := inherit type expropt
For example:
type MyDerived(...) =inherit MyBase(...)
If a class definition does not contain an inherit declaration, the class inherits fromSystem.Object by default.
The inherit declaration for a type must have arguments if and only if the type has a primary constructor.

118

8.6.1.3

Instance Function and Value Definitions in Primary Constructors

Classes that have primary constructors may include function definitions, value definitions, and “do” statements.
The following rules apply to these definitions:

Each definition may be marked static (see §8.6.2.1). If the definition is not marked static, it is called an
instance definition.



The functions and values defined by instance definitions are lexically scoped (and thus implicitly private) tothe object being defined.

Each value definition may optionally be marked mutable.



A group of function and value definitions may optionally be marked rec.



Function and value definitions are generalized.

Value definitions that declared in classes are represented in compiled code as follows:

If a value definition is not mutable, and is not used in any function or member, then the value isrepresented as a local value in the object constructor.



If a value definition is mutable, or used in any function or member, then the value is represented as an
instance field in the corresponding CLI type.

Function definitions are represented in compiled code as private members of the corresponding CLI type.
For example, consider this type:
type C(x:int,y:int) =let z = x + y
let f w = x + w
member this.Z = z
member this.Add(w) = f w
The input y is used only during construction, and no field is stored for it. Likewise the function f isrepresented as a member rather than a field that is a function value.

A value definition is considered a function definition if its immediate right-hand-side is an anonymous function, as
in this example:
let f = (fun w -> x + w)
Function and value definitions may have attributes as follows:Value definitions represented as fields may have attributes that target fields.

Value definitions represented as locals may have attributes that target fields, but these attributes will not be
attached to any construct in the resulting CLI assembly.



Function definitions represented as methods may have attributes that target methods.

For example:
type C(x:int) =[<System.Obsolete>]let unused = x
member __.P = 1
In this example, no field is generated for unused, and no corresponding compiled CLI attribute is generated.

8.6.1.4

Static Function and Value Definitions in Primary Constructors

Classes that have primary constructors may have function definitions, value definitions, and “do” statements thatare marked as static:

The values that are defined by static function and value definitions are lexically scoped (and thus implicitly
private) to the type being defined.
119Each value definition may optionally be marked mutable.



A group of function and value definitions may optionally be marked rec.



Static function and value definitions are generalized.



Static function and value definitions are computed once per generic instantiation.

Static function and value definitions are elaborated to a static initializer associated with each genericinstantiation of the generated class. Static initializers are executed on demand in the same way as static
initializers for implementation files §12.5.



The compiled representation for static value definitions is as follows:

If the value is not used in any function or member then the value is represented as a local value in the
CLI class initializer of the type.



If the value is used in any function or member, then the value is represented as a static field of the CLI
class for the type.

The compiled representation for a static function definition is a private static member of the corresponding
CLI type.

Static function and value definitions may have attributes as follows:

Static function and value definitions represented as fields may have attributes that target fields.



Static function and value definitions represented as methods may have attributes that target methods.

For example:
type C<'T>() =static let mutable v = 2 + 2
static do v <- 3
member x.P = v
static member P2 = v+v
printfn "check: %d = 3" (new C<int>()).P
printfn "check: %d = 3" (new C<int>()).P
printfn "check: %d = 3" (new C<string>()).P
printfn "check: %d = 6" (C<int>.P2)
printfn "check: %d = 6" (C<string>.P2)
In this example, the value v is represented as a static field in the CLI type for C. One instance of this field exists
for each generic instantiation of C. The output of the program ischeck: 3 = 3
check: 3 = 3
check: 3 = 3
check: 6 = 6
check: 6 = 6

8.6.2 Members in Classes
Class types may declare members (§8.13), overrides, and interface implementations. As with all types that haveoverrides and interface implementations, such class types are subject to Dispatch Slot Checking (§14.8).

8.6.3 Additional Object Constructors in Classes
Although the use of primary object constructors is generally preferable, additional object constructors may also
be specified. Additional object constructors are required in two situations:

To define classes that have more than one constructor.

120



To specify explicit val fields without the DefaultValue attribute.

For example, the following statement adds a second constructor to a class that has a primary constructor:
type PairOfIntegers(x:int,y:int) =new (x) = PairOfIntegers(x,x)
The next example declares a class without a primary constructor:
type PairOfStrings =val s1 : stringval s2 : stringnew (s) = { s1 = s; s2 = s }new (s1,s2) = { s1 = s1; s2 = s2 }If a primary constructor is present, additional object constructors must call another object constructor in the same
type, which may be another additional constructor or the primary constructor.
If no primary constructor is present, additional constructors must initialize any val fields of the object that do nothave the DefaultValue attribute. They must also specify a call to a base class constructor for any inherited classtype. No call to a base class constructor is required if the base class is System.Object.
The use of additional object constructors and val fields is required if a class has multiple object constructors thatmust each call different base class constructors. For example:
type BaseClass =val s1 : stringnew (s) = { s1 = s }new () = { s = "default" }type SubClass =inherit BaseClassval s2 : stringnew (s1,s2) = { inherit BaseClass(s1); s2 = s2 }new (s2) = { inherit BaseClass(); s2 = s2 }To implement additional object constructors, F# uses a restricted subset of expressions that ensure that the code
generated for the constructor is valid according to the rules of object construction for CLI objects. Note thatprecisely one additional-constr-init-expr occurs for each branch of a construction expression.
For classes without a primary constructor, side effects can be performed after the initialization of the fields of theobject by using the additional-constr-expr then stmt form. For example:
type PairOfIntegers(x:int,y:int) =// This additional constructor has a side effect after initialization.
new(x) =PairOfIntegers(x, x)
thenprintfn "Initialized with only one integer"
The name of the object parameter can be bound within additional constructors. For example:
type X =val a : (unit -> string)val mutable b : stringnew() as x = { a = (fun () -> x.b); b = "b" }A warning is given if x occurs syntactically in or before the additional-constr-init-expr of the construction
expression. If any member is called before the completion of execution of the additional-constr-init-expr
within the additional-constr-expr then an InvalidOperationException is thrown.

1218.6.4 Additional Fields in Classes
Additional field declarations indicate that a value is stored in an object. They are generally used only for classes
without a primary constructor, or for mutable fields that use default initialization, and typically occur only ingenerated code. For example:
type PairOfIntegers =val x : int
val y : int
new(x, y) = {x = x; y = y}
The following shows an additional field declaration as a static field in an explicit class type:
type TypeWithADefaultMutableBooleanField =[<DefaultValue>]static val mutable ready : bool
At runtime, such a field is initially assigned the zero value for its type (§6.9.3). For example:
type MyClass(name:string) =// Keep a global count. It is initially zero.
[<DefaultValue>]static val mutable count : int// Increment the count each time an object is createddo MyClass.count <- MyClass.count + 1
static member NumCreatedObjects = MyClass.countmember x.Name = name
A val specification in a type that has a primary constructor must be marked mutable and must have theDefaultValue attribute. For example:
type X() =[<DefaultValue>]
val mutable x : int
The DefaultValue attribute takes a check parameter, which indicates whether to ensure that the val
specification does not create unexpected null values. The default value for check is true. If this parameter istrue, the type of the field must permit default initialization (§5.4.8). For example, the following type is rejected:
type MyClass<'T>() =[<DefaultValue>]static val mutable uninitialized : 'T
This is because the type 'T does not admit default initialization. However, in compiler-generated and handoptimized code it is sometimes essential to be able to emit fields that are completely uninitialized. In this case,
DefaultValue(false) can be used. For example:
type MyNullable<'T>() =[<DefaultValue>]static val mutable ready : bool[<DefaultValue(false)>]static val mutable uninitialized : 'T

122

8.7 Interface Type Definitions
An interface type definition represents a contract that an object may implement. Such a type definition
containsonly abstract members. For example:
type IPair<'T,'U> =interface
abstract First: 'T
abstract Second: 'U
endtype IThinker<'Thought> =abstract Think: ('Thought -> unit) -> unit
abstract StopThinking: (unit -> unit)
Note: The interface/end tokens can be omitted when lightweight syntax is used, in which caseType Kind Inference (§8.2) is used to determine the kind of the type. The presence of any nonabstract members or constructors means a type is not an interface type.
By convention, interface type names start with I, as in IEvent. However, this convention is notfollowed as strictly in F# as in other CLI languages.
Interface types may be arranged hierarchically by specifying inherit declarations. For example:
type IA =abstract One: int -> int
type IB =abstract Two: int -> int
type IC =inherit IA
inherit IB
abstract Three: int -> int
Each inherit declaration must itself be an interface type. Circular references are not allowed among inherit
declarations. F# uses the named types of the inherited interface types to determine whether references are
circular.

8.8 Struct Type Definitions
A struct type definition is a type definition whose instances are stored inline inside the stack frame or object ofwhich they are a part. The type is represented as a CLI struct type, also called a value type. For example:
type Complex =structval real: float;
val imaginary: float
member x.R = x.real
member x.I = x.imaginaryend
Note: The struct/end tokens can be omitted when lightweight syntax is used, in which case Type
Kind Inference (§8.2) is used to determine the kind of the type.

123

Becaues structs undergo type kind inference (§8.2), the following is valid:[<Struct>]type Complex(r:float, i:float) =member x.R = r
member x.I = i
Structs may have primary constructors:[<Struct>]type Complex(r : float, I : float) =member x.R = r
member x.I = i
Structs that have primary constructors must accept at least one argument.
Structs may have additional constructors. For example:[<Struct>]type Complex(r : float, I : float) =member x.R = r
member x.I = inew(r : float) = new Complex(r, 0.0)
The fields in a struct may be mutable only if the struct does not have a primary constructor. For example:[<Struct>]type MutableComplex =val mutable real : float;
val mutable imaginary : float
member x.R = x.real
member x.I = x.imaginary
member x.Change(r, i) = x.real <- r; x.imaginary <- i
new (r, i) = { real = r; imaginary = i }Struct types may declare members, overrides, and interface implementations. As for all types that declareoverrides and interface implementations, struct types are subject to Dispatch Slot Checking (§14.8).
Structs may not have inherit declarations.
Structs may not have “let” or “do” statements unless they are static. For example, the following is not valid:[<Struct>]type BadStruct1 (def : int) =do System.Console.WriteLine("Structs cannot use 'do'!")Structs may have static “let” or “do” statements. For example, the following is valid:[<Struct>]type GoodStruct1 (def : int) =static do System.Console.WriteLine("Structs can use 'static do'")A struct type must be valid according to the CLI rules for structs; in particular, recursively constructed structs are
not permitted. For example, the following type definition is not permitted, because the size of BadStruct2 would
be infinite:[<Struct>]type BadStruct2 =val data : float;
val rest : BadStruct2
new (data, rest) = { data = data; rest = rest }Likewise, the implied size of the following struct would be infinite:[<Struct>]type BadStruct3 (data : float, rest : BadStruct3) =124

member s.Data = data
member s.Rest = rest
If the types of all the fields in a struct type permit default initialization, the struct type has an implicit default
constructor,which initializes all the fields to the default value. For example, the Complex type defined earlier inthis section permits default initialization.
[<Struct>]type Complex(r : float, I : float) =member x.R = r
member x.I = inew(r : float) = new Complex(r, 0.0)
let zero = Complex()
Note: The existence of the implicit default constructor for structs is not recorded in CLI metadata
and is an artifact of the CLI specification and implementation itself. A CLI implementation permits
default constructors for all struct types, although F# does not permit their direct use for F# structtypes unless all field types admit default initialization. This is similar to the way that F# considers
some types to have null as an abnormal value.
Public struct types for use from other CLI languages should be designed with the existence of thedefault zero-initializing constructor in mind.

8.9 Enum Type Definitions
Occasionally the need arises to represent a type that compiles as a CLI enumeration type. An enum typedefinition has values that are represented by integer constants and has a CLI enumeration as its compiled form.
Enum type definitions are declared by specifying integer constants in a format that is syntactically similar to aunion type definition. For example:
type Color =| Red = 0
| Green = 1| Blue = 2
let rgb = (Color.Red, Color.Green, Color.Blue)
let show(colorScheme) =match colorScheme with| (Color.Red, Color.Green, Color.Blue) -> printfn "RGB in use"
| _ -> printfn "Unknown color scheme in use"
The example defines the enum type Color, which has the values Red, Green, and Blue, mapped to the constants
0, 1, and 2 respectively. The values are accessed by their qualified names: Color.Red, Color.Green, andColor.Blue.
Each case must be given a constant value of the same type. The constant values dictate the underlying type ofthe enum, and must be one of the following types:
sbyte, int16, int32, int64, byte, uint16, uint32, uint64, char

The declaration of an enumeration type in an implementation file has the following effects on the typing
environment:Brings a named type into scope.

Adds the named type to the inferred signature of the containing namespace or module.

125

Enum types coerce to System.Enum and satisfy the enum<underlying-type> constraint for their underlying
type.
Each enum type declaration is implicitly annotated with the RequiresQualifiedAccess attribute and does not
add the tags of the enumeration to the name environment.
type Color =| Red = 0
| Green = 1| Blue = 2
let red = Red // not accepted, must use Color.Red
Unlike unions, enumeration types are fundamentally “incomplete,” because CLI enumerations can be converted
to and from their underlying primitive type representation. For example, a Color value that is not in the above
enumeration can be generated by using the enum function from the F# library:
let unknownColor : Color = enum<Color>(7)This statement adds the value named unknownColor, equal to the constant 7, to the Color enumeration.

8.10 Delegate Type Definitions
Occasionally the need arises to represent a type that compiles as a CLI delegate type. A delegate type definition
has as its values functions that are represented as CLI delegate values. A delegate type definition is declared byusing the delegate keyword with a member signature. For example:
type Handler<'T> = delegate of obj * 'T -> unit
Delegates are often used when using Platform Invoke (P/Invoke) to interface with CLI libraries, as in the following
example:type ControlEventHandler = delegate of int -> bool[<DllImport("kernel32.dll")>]extern void SetConsoleCtrlHandler(ControlEventHandler callback, bool add)

8.11 Exception Definitions
An exception definition defines a new way of constructing values of type exn (a type abbreviation for
System.Exception). Exception definitions have the form:
exception ident of type1 * … * typen
An exception definition has the following effect:

The identifier ident can be used to generate values of type exn.



The identifier ident can be used to pattern match on values of type exn.

The definition generates a type with name ident that derives from exn.

For example:
exception Error of int * string
raise (Error (3, "well that didn't work did it"))
try

126

raise (Error (3, "well that didn't work did it"))with| Error(sev, msg) -> printfn "severity = %d, message = %s" sev msg
The type that corresponds to the exception definition can be used as a type in F# code. For example:
let exn = Error (3, "well that didn't work did it")let checkException() =if (exn :? Error) then printfn "It is of type Error"
if (exn.GetType() = typeof<Error>) then printfn "Yes, it really is of type Error"
Exception abbreviations may abbreviate existing exception constructors. For example:
exception ThatWentBadlyWrong of string * int
exception ThatWentWrongBadly = ThatWentBadlyWronglet checkForBadDay() =if System.DateTime.Today.DayOfWeek = System.DayOfWeek.Monday thenraise (ThatWentWrongBadly("yes indeed",123))
Exception values may also be generated by defining and using classes that extend System.Exception.

8.12 Type Extensions
A type extension associates additional members with an existing type. For example, the following associates the
additional member IsLong with the existing type System.String:type System.String withmember x.IsLong = (x.Length > 1000)
Type extensions may be applied to any accessible type definition except those defined by type abbreviations. A
type can have any number of extensions.
If the type extension is in the same module or namespace declaration group as the original type definition, it iscalled an intrinsic extension. Members that are defined in intrinsic extensions follow the same name resolution
and other language rules as members that are defined as part of the original type definition.
If the type extension is not intrinsic, it must be in a module, and it is called an extension member. Opening a
module that contains an extension member extends the name resolution of the dot syntax for the extended type.
That is, extension members are accessible only if the module that contains the extension is open.
Name resolution for members that are defined in type extensions behaves as follows:

In method application resolution (see §14.4), regular members (that is, members that are part of the original
definition of a type, plus intrinsic extensions) are preferred to extension members.

Extension members that are in scope and have the correct name are included in the group of members
considered for method application resolution (see §14.4).

An intrinsic member is always preferred to an extension member. If an extension member has the same
name and type signature as a member in the original type definition or an inherited member, then it will beinaccessible.

The following illustrates the definition of one intrinsic and one extension member for the same type:
namespace Numbers
type Complex(r : float, i : float) =member x.R = r
member x.I = i

127

// intrinsic extension
type Complex withstatic member Create(a, b) = new Complex (a, b)
member x.RealPart = x.R
member x.ImaginaryPart = x.I
namespace Numbers
module ComplexExtensions =// extension member
type Numbers.Complex withmember x.Magnitude = ...
member x.Phase = ...
Extensions may define both instance members and static members.
Extensions are checked as follows:

Checking applies to the member definitions in an extension together with the members and other definitionsin the group of type definitions of which the extension is a part.



Two intrinsic extensions may not contain conflicting members because intrinsic extensions are consideredpart of the definition of the type.



Extensions may not define fields, interfaces, abstract slots, inherit declarations, or dispatch slot (interface
and override) implementations.



Extension members must be in modules.

Extension members are compiled as CLI static members with encoded names.
The elaborated form of an application of a static extension member C.M(arg1,…,argn) is a call to thisstatic member with arguments arg1,…,argn..

The elaborated form of an application of an instance extension member obj.M(arg1,…,argn) is an
invocation of the static instance member where the object parameter is supplied as the first argument to
the extension member followed by arguments arg1 … argn.

8.12.1 Imported CLI C# Extensions Members
The CLI C# language defines an “extension member,” which commonly occurs in CLI libraries, along with someother CLI languages. C# limits extension members to instance methods.
C#-defined extension members are made available to F# code in environments where the C#-authored assemblyis referenced and an open declaration of the corresponding namespace is in effect. However, some notable
limitations apply:C# extension members whose “this” parameter is a variable type are not available to F# code in F# 2.0.

C# extension members whose “this” parameter is an array type are not available to F# code in F# 2.0.

The encoding of compiled names for F# extension members is not compatible with C# encodings of C# extension
members. However, for instance extension methods, the naming can be made compatible. For example:[<System.Runtime.CompilerServices.Extension>]
module EnumerableExtensions =[<CompiledName("OutputAll")>][<System.Runtime.CompilerServices.Extension>]type System.Collections.Generic.IEnumerable<'T> withmember x.OutputAll (this:seq<'T>) =for x in this do
System.Console.WriteLine (box x)128

8.13 Members
Member definitions describe functions that are associated with type definitions and/or values of particular types.
Member definitions can be used in type definitions. Members can be classified as follows:

Property members



Method members

A static member is prefixed by static and is associated with the type, rather than with any particular object.
Here are some examples of static members:
type MyClass() =static let mutable adjustableStaticValue = "3"static let staticArray = [| "A"; "B" |]static let staticArray2 = [|[| "A"; "B" |]; [| "A"; "B" |] |]static member StaticMethod(y:int) = 3 + 4 + ystatic member StaticProperty = 3 + staticArray.Length
static member StaticProperty2
with get() = 3 + staticArray.Length
static member MutableStaticProperty
with get()= adjustableStaticValueand set(v:string) = adjustableStaticValue <- v
static member StaticIndexer
with get(idx) = staticArray.[idx]static member StaticIndexer2
with get(idx1,idx2) = staticArray2.[idx1].[idx2]
static member MutableStaticIndexer
with get (idx1) = staticArray.[idx1]
and set (idx1) (v:string) = staticArray.[idx1] <- v
An instance member is a member without static. Here are some examples of instance members:
type MyClass() =let mutable adjustableInstanceValue = "3"
let instanceArray = [| "A"; "B" |]let instanceArray2 = [| [| "A"; "B" |]; [| "A"; "B" |] |]member x.InstanceMethod(y:int) = 3 + y + instanceArray.Length
member x.InstanceProperty = 3 + instanceArray.Length
member x.InstanceProperty2
with get () = 3 + instanceArray.Length
member x.InstanceIndexer
with get (idx) = instanceArray.[idx]member x.InstanceIndexer2
with get (idx1,idx2) = instanceArray2.[idx1].[idx2]
member x.MutableInstanceProperty
with get ()= adjustableInstanceValueand set (v:string) = adjustableInstanceValue <- v

129member x.MutableInstanceIndexer
with get (idx1) = instanceArray.[idx1]
and set (idx1) (v:string) = instanceArray.[idx1] <- v
Members from a set of mutually recursive type definitions are checked as a single mutually recursive group. As
with collections of recursive functions, recursive calls to potentially-generic methods may result in inconsistent
type constraints:
type Test() =static member Id x = x
member t.M1 (x: int) = Test.Id(x)member t.M2 (x: string) = Test.Id(x) // error, x has type 'string' not 'int'
A target method that has a full type annotation is eligible for early generalization (§14.6.7).
type Test() =static member Id<'T> (x:'T) : 'T = x
member t.M1 (x: int) = Test.Id(x)member t.M2 (x: string) = Test.Id(x)8.13.1 Property Members
A property member is a method-or-prop-defn in one of the following forms:
staticopt member ident.opt ident = expr
staticopt member ident.opt ident with get pat = expr
staticopt member ident.opt ident with set patopt pat= expr
staticopt member ident.opt ident with get pat = expr and set patopt pat = expr
staticopt member ident.opt ident with set patopt pat = expr and get pat = expr
A property member in the form
staticopt member ident.opt ident with get pat1 = expr1 and set pat2a pat2b opt = expr2
is equivalent to two property members of the form:
staticopt member ident.opt ident with get pat1 = expr1
staticopt member ident.opt ident with set pat2a pat2b opt = expr2
Furthermore, the following two members are equivalent:staticopt member ident.opt ident = expr
staticopt member ident.opt ident with get () = expr
These two are also equivalent:staticopt member ident.opt ident with set pat = expr2
staticopt member ident.opt ident with set () pat = expr
Thus, property members may be reduced to the following two forms:
staticopt member ident.opt ident with get patidx = expr
staticopt member ident.opt ident with set patidx pat = expr
The ident.opt must be present if and only if the property member is an instance member. When evaluated, theidentifier ident is bound to the “this” or “self” object parameter that is associated with the object within theexpression expr.
A property member is an indexer property if patidx is not the unit pattern (). Indexer properties called Item are
special in the sense that they are accessible via the .[] notation. An Item property that takes one argument isaccessed by using x.[i]; with two arguments by x.[i,j], and so on. Setter properties must return type unit.
Property members may be declared abstract. If a property has both a getter and a setter, then both must be
abstract or neither must be abstract.

130

Each property member has an implied property type. The property type is the type of the value that the getter
property returns or the setter property accepts. If a property member has both a getter and a setter, and neither isan indexer property, the signatures of both the getter and the setter must imply the same property type.
Static and instance property members are evaluated every time the member is invoked. For example, in thefollowing, the body of the member is evaluated each time C.Time is evaluated:
type C () =static member Time = System.DateTime.Now
Note: A static property member may also be written with an explicit get method:
static member ComputerName
with get() = System.Environment.GetEnvironmentVariable("COMPUTERNAME")

8.13.2 Method Members
A method member is of the form:
staticopt member ident.opt ident pat1 ... patn = expr
The ident.opt can be present if and only if the property member is an instance member. In this case, theidentifier ident corresponds to the “this” (or “self”) variable associated with the object on which the member isbeing invoked.
Arity analysis (§14.10) applies to method members. This is because F# members must compile to CLI methods,
which accept only a single fixed collection of arguments.

8.13.3 Curried Method Members
Methods that take multiple arguments may be written in iterated (“curried”) form. For example:
static member StaticMethod2 s1 s2 =sprintf "In StaticMethod(%s,%s)" s1 s2
The rules of arity analysis (§14.10) determine the compiled form of these members.
The following limitations apply to curried method members:Additional argument groups may not include optional or byref parameters.

When the member is called, additional argument groups may not use named arguments(§8.13.4).

Curried members may not be overloaded.

The compiled representation of a curried method member is a .NET method in which the arguments areconcatenated into a single argument group.
Note: It is recommended that curried argument members do not appear in the public API of an F#
assembly that is designed for use from other .NET languages. Information about the currying order
is not visible to these languages.

8.13.4 Named Arguments to Method Members
Calls to methods—but not to let-bound functions or function values—may use named arguments. For example:
System.Console.WriteLine(format = "Hello {0}", arg0 = "World")
System.Console.WriteLine("Hello {0}", arg0 = "World")
System.Console.WriteLine(arg0 = "World", format = "Hello {0}")The argument names that are associated with a method declaration are derived from the names that appear in
the first pattern of a member definition, or from the names used in the signature for a method member. For
example:131

type C() =member x.Swap(first, second) = (second, first)
let c = C()
c.Swap(first = 1,second = 2)
c.Swap(second = 1,first = 2)// result is '(2,1)'// result is '(1,2)'

Named arguments may be used only with the arguments that correspond to the arity of the member. That is,because members have an arity only up to the first set of tupled arguments, named arguments may not be usedwith subsequent curried arguments of the member.
The resolution of calls that use named arguments is specified in Method Application Resolution (see §14.4). The
rules in that section describe how resolution matches a named argument with either a formal parameter of thesame name or a “settable” return property of the same name. For example, the following code resolves thenamed argument to a settable property:
System.Windows.Forms.Form(Text = "Hello World")
If an ambiguity exists, assigning the named argument is assigned to a formal parameter rather than to a settable
return property.
The Method Application Resolution (§14.4) rules ensure that:


Named arguments must appear after all other arguments, including optional arguments that arematched by position.

After named arguments have been assigned, the remaining required arguments are called the required unnamed
arguments. The required unnamed arguments must precede the named arguments in the argument list. The n
unnamed arguments are matched to the first n formal parameters; the subsequent named arguments must
include only the remaining formal parameters. In addition, the arguments must appear in the correct sequence.
For example, the following code is invalid:// error: unnamed args after named
System.Console.WriteLine(arg0 = "World", "Hello {0}")Similarly, the following code is invalid:
type Foo() =static member M (arg1, arg2, arg3) = 1// error: arg1, arg3 not a prefix of the argument list
Foo.M(1, 2, arg2 = 3)
The following code is valid:
type Foo() =static member M (arg1, arg2, arg3) = 1
Foo.M (1, 2, arg3 = 3)
The names of arguments to members may be listed in member signatures. For example, in a signature file:
type C =static member ThreeArgs : arg1:int * arg2:int * arg3:int -> int
abstract TwoArgs : arg1:int * arg2:int -> int

8.13.5 Optional Arguments to Method Members
Method members—but not functions definitions—may have optional arguments. Optional arguments must appear
at the end of the argument list. An optional argument is marked with a ? before its name in the method
declaration. Inside the member, the argument has type option<argType>.

132

The following example declares a method member that has two optional arguments:
let defaultArg x y = match x with None -> y | Some v -> vtype T() =static member OneNormalTwoOptional (arg1, ?arg2, ?arg3) =let arg2 = defaultArg arg2 3
let arg3 = defaultArg arg3 10
arg1 + arg2 + arg3
Optional arguments may be used in interface and abstract members. In a signature, optional arguments appear
as follows:
static member OneNormalTwoOptional : arg1:int * ?arg2:int * ?arg3:int -> int
Callers may specify values for optional arguments in the following ways:


By name, such as arg2 = 1.



By propagating an existing optional value by name, such as ?arg2=None or ?arg2=Some(3) or ?arg2=arg2.
This can be useful when building a method that passes optional arguments on to another method.



By using normal, unnamed arguments that are matched by position.

For example:
T.OneNormalTwoOptional(3)T.OneNormalTwoOptional(3, 2)
T.OneNormalTwoOptional(arg1 = 3)
T.OneNormalTwoOptional(arg1 = 3, arg2 = 1)
T.OneNormalTwoOptional(arg2 = 3, arg1 = 0)
T.OneNormalTwoOptional(arg2 = 3, arg1 = 0, arg3 = 11)
T.OneNormalTwoOptional(0, 3, 11)
T.OneNormalTwoOptional(0, 3, arg3 = 11)
T.OneNormalTwoOptional(arg1 = 3, ?arg2 = Some 1)
T.OneNormalTwoOptional(arg2 = 3, arg1 = 0, arg3 = 11)
T.OneNormalTwoOptional(?arg2 = Some 3, arg1 = 0, arg3 = 11)
T.OneNormalTwoOptional(0, 3, ?arg3 = Some 11)
The resolution of calls that use optional arguments is specified in Method Application Resolution (see §14.4).
Optional arguments may not be used in member constraints.
Note: Imported CLI metadata may specify arguments as optional and may additionally specify adefault value for the argument. These are treated as F# optional arguments. CLI optional
arguments can propagate an existing optional value by name; for example, ?ValueTitle = Some(…).
For example, here is a fragment of a call to a Microsoft Excel COM automation API that usesnamed and optional arguments.
chartobject.Chart.ChartWizard(Source = range5,
Gallery = XlChartType.xl3DColumn,
PlotBy = XlRowCol.xlRows,
HasLegend = true,
Title = "Sample Chart",
CategoryTitle = "Sample Category Type",
ValueTitle = "Sample Value Type")133

CLI optional arguments are not passed as values of type Option<_>. If the optional argument ispresent, its value is passed. If the optional argument is omitted, the default value from the CLI
metadata is supplied instead. The value System.Reflection.Missing.Value is supplied for any
CLI optional arguments of type System.Object that do not have a corresponding CLI default value,
and the default (zero-bit pattern) value is supplied for other CLI optional arguments of other types
that have no default value.
The compiled representation of members varies as additional optional arguments are added. The addition of
optional arguments to a member signature results in a compiled form that is not binary-compatible with theprevious compiled form.
Marking an argument as optional is equivalent to adding the Microsoft.FSharp.Core.OptionalArgument
attribute (§16.1) to a required argument. This attribute is added implicitly for optional arguments. Adding the[<OptionalArgument>] attribute to a parameter of type 'a option in a virtual method signature is equivalent
to using the (?x:'a) syntax in a method definition. If the attribute is applied to an argument of a method, itshould also be applied to all subsequent arguments of the method. Otherwise, it has no effect and callers must
provide all of the arguments.

8.13.6 Type-directed Conversions at Member Invocations
As described in Method Application Resolution (see §14.4), two type-directed conversions are applied at method
invocations.
The first type-directed conversion converts anonymous function expressions and other function-valued
arguments to delegate types. Given:

A formal parameter of delegate type DAn actual argument farg of known type ty1 -> ... -> tyn -> rtyPrecisely n arguments to the Invoke method of delegate type D

Then:


The parameter is interpreted as if it were written:
new D(fun arg1 ... argn -> farg arg1 ... argn)

If the type of the formal parameter is a variable type, then F# uses the known inferred type of the argument
including instantiations to determine whether a formal parameter has delegate type. For example, if an explicit
type instantiation is given that instantiates a generic type parameter to a delegate type, the following conversion
can apply:type GenericClass<'T>() =static member M(arg: 'T) = ()GenericClass<System.Action>.M(fun () -> ())// allowed

The second type-directed conversion enables an F# reference cell to be passed where a byref<ty> is expected.
Given:
A formal out parameter of type byref<ty>An actual argument that is not a byref type

Then:


The actual parameter is interpreted as if it had type ref<ty>.

For example:
type C() =static member M1(arg: System.Action) = ()134static member M2(arg: byref<int>) = ()C.M1(fun () -> ())
let f = (fun () -> ()) in C.M1(f)// allowed// not allowed

let result = ref 0
C.M2(result)// allowed
Note: These type-directed conversions are primarily for interoperability with existing member-based.NET libraries and do not apply at invocations of functions defined in modules or bound locally in
expressions.
A value of type ref<ty> may be passed to a function that accepts a byref parameter. The interior address of theheap-allocated cell that is associated with such a parameter is passed as the pointer argument.
For example, consider the following C# code:
public class C{
static public void IntegerOutParam(out int x) { x = 3; }}public class D{
virtual public void IntegerOutParam(out int x) { x = 3; }}This C# code can be called by the following F# code:
let res1 = ref 0
C.IntegerOutParam(res1)// res1.contents now equals 3
Likewise, the abstract signature can be implemented as follows:
let x = {new D() with IntegerOutParam(res : byref<int>) = res <- 4}
let res2 = ref 0x.IntegerOutParam(res2);// res2.contents now equals 4

8.13.7 Overloading of Methods
Multiple methods that have the same name may appear in the same type definition or extension. For example:
type MyForm() =inherit System.Windows.Forms.Form()
member x.ChangeText(text: string) =x.Text <- text
member x.ChangeText(text: string, reason: string) =x.Text <- text
System.Windows.Forms.MessageBox.Show ("changing text due to " + reason)
Methods must be distinct based on their name and fully inferred types, after erasure of type abbreviations andunit-of-measure annotations.
Methods that take curried arguments may not be overloaded.

135

8.13.8 Naming Restrictions for Members
A member in a record type may not have the same name as a record field in that type.
A member may not have the same name and signature as another method in the type. This check ignores return
types except for members that are named op_Implicit or op_Explicit.

8.13.9 Members Represented as Events
Events are the CLI notion of a “listening point”—that is, a configurable object that holds a set of callbacks, which
can be triggered, often by some external action such as a mouse click or timer tick.
In F#, events are first-class values; that is, they are objects that mediate the addition and removal of listeners
from a backing list of listeners. The F# library supports the type Microsoft.FSharp.Control.IEvent<_,_> andthe module Microsoft.FSharp.Control.Event, which contains operations to map, fold, create, and compose
events. The type is defined as follows:
type IDelegateEvent<'del when 'del :> System.Delegate > =abstract AddHandler : 'del -> unit
abstract RemoveHandler : 'del -> unit
type IEvent<'Del,'T when 'Del : delegate<'T,unit> and 'del :> System.Delegate > =abstract Add : event : ('T -> unit) -> unit
inherit IDelegateEvent<'del>type Handler<'T> =delegate of sender : obj * 'T -> unit

type IEvent<'T> = IEvent<Handler<'T>, 'T>
The following shows a sample use of events:
open System.Windows.Forms
type MyCanvas() =inherit Form()
let event = new Event<PaintEventArgs>()
member x.Redraw = event.Publish
override x.OnPaint(args) = event.Trigger(args)
let form = new MyCanvas()form.Redraw.Add(fun args -> printfn "OnRedraw")
form.Activate()
Application.Run(form)
Events from CLI languages are revealed as object properties of type
Microsoft.FSharp.Control.IEvent<tydelegate, tyargs>. The F# compiler determines the type arguments,
which are derived from the CLI delegate type that is associated with the event.
Event declarations are not built into the F# language, and event is not a keyword. However, property membersthat are marked with the CLIEvent attribute and whose type coerces to
Microsoft.FSharp.Control.IDelegateEvent<tydelegate> are compiled to include extra CLI metadata andmethods that mark the property name as a CLI event. For example, in the following code, the ChannelChanged
property is currently compiled as a CLI event:type ChannelChangedHandler = delegate of obj * int -> unit
type C() =let channelChanged = new Event<ChannelChangedHandler,_>()[<CLIEvent>]
member self.ChannelChanged = channelChanged.Publish

136

Similarly, the following shows the definition and implementation of an abstract event:type I =[<CLIEvent>]
abstract ChannelChanged : IEvent<ChannelChanged,int>
type ImplI() =let channelChanged = new Event<ChannelChanged,_>()
interface I with[<CLIEvent>]
member self.ChannelChanged = channelChanged.Publish

8.13.10

Members Represented as Static Members

Most members are represented as their corresponding CLI method or property. However, in certain situations an
instance member may be compiled as a static method. This happens when either of the following is true:The type definition uses null as a representation by placing theCompilationRepresentation(CompilationRepresentationFlags.UseNullAsTrueValue) attribute on
the type that declares the member.

The member is an extension member.

Compilation of an instance member as a static method can affect the view of the type when seen from otherlanguages or from System.Reflection. A member that might otherwise have a static representation can bereverted to an instance member representation by placing the attribute
CompilationRepresentation(CompilationRepresentationFlags.Instance) on the member.
For example, consider the following type:[<CompilationRepresentation(CompilationRepresentationFlags.UseNullAsTrueValue)>]type option<'T> =| None| Some of 'T
member x.IsNone = match x with None -> true | _ -> false
member x.IsSome = match x with Some _ -> true | _ -> false[<CompilationRepresentation(CompilationRepresentationFlags.Instance)>]member x.Item =match x with| Some x -> x
| None -> failwith "Option.Item"The IsNone and IsSome properties are represented as CLI static methods. The Item property is represented as
an instance property.

8.14 Abstract Members and Interface Implementations
Abstract member definitions and interface declarations in a type definition represent promises that an object will
provide an implementation for a corresponding contract.

1378.14.1 Abstract Members
An abstract member definition in a type definition represents a promise that an object will provide an
implementation for a dispatch slot. For example:
type IX =abstract M : int -> int
The abstract member M indicates that an object of type IX will implement a displatch slot for a member that
returns an int.
A class definition may contain abstract member definitions, but the definition must be labeled with the
AbstractClass attribute:[<AbstractClass>]
type X() =abstract M : int -> int
An abstract member definition has the form
abstract accessopt member-sig
where a member signature has one of the following forms
ident typar-defnsopt : curried-sig
ident typar-defnsopt : curried-sig with get
ident typar-defnsopt : curried-sig with set
ident typar-defnsopt : curried-sig with get, set
ident typar-defnsopt : curried-sig with set, get
and the curried signature has the form
args-spec1 -> ... -> args-specn -> type
If n ≥ 2, then args-spec2 … args-specn must all be patterns without attribute or optional argument
specifications.
If get or set is specified, the abstract member is a property member. If both get and set are specified, theabstract member is equivalent to two abstract members, one with get and one with set.

8.14.2 Members that Implement Abstract Members
An implementation member has the form:
override ident.ident pat1 ... patn = expr
default ident.ident pat1 ... patn = expr
Implementation members implement dispatch slots. For example:[<AbstractClass>]
type BaseClass() =abstract AbstractMethod : int -> int
type SubClass(x: int) =inherit BaseClass()
override obj.AbstractMethod n = n + x
let v1
let v2= BaseClass()// not allowed – BaseClass is abstract= (SubClass(7) :> BaseClass)v2.AbstractMethod 6// evaluates to 13

In this example, BaseClass() declares the abstract slot AbstractMethod and the SubClass type supplies animplementation member obj.AbstractMethod, which takes an argument n and returns the sum of n and the138argument that was passed in the instantiation of SubClass. The v2 object instantiates SubClass with the value 7,
so v2.AbstractMethod 6 evaluates to 13.
The combination of an abstract slot declaration and a default implementation of that slot create the F# equivalentof a “virtual” method in some other languages—that is, an abstract member that is guaranteed to have an
implementation. For example:
type BaseClass() =abstract AbstractMethodWithDefaultImplementation : int -> int
default obj.AbstractMethodWithDefaultImplementation n = n
type SubClass1(x: int) =inherit BaseClass()
override obj.AbstractMethodWithDefaultImplementation n = n + x
type SubClass2() =inherit BaseClass()
let v1
let v2let v3= BaseClass()// allowed -- BaseClass contains a default implementation= (SubClass1(7) :> BaseClass)= (SubClass2() :> BaseClass)v1.AbstractMethodWithDefaultImplementation 6
v2.AbstractMethodWithDefaultImplementation 6
v3.AbstractMethodWithDefaultImplementation 6// evaluates to 6// evaluates to 13// evaluates to 6

Here, the BaseClass type contains a default implementation, so F# allows the instantiation of v1. The
instantiation of v2 is the same as in the previous example. The instantiation of v3 is similar to that of v1, because
SubClass2 inherits directly from BaseClass and does not override the default method.
Note: The keywords override and default are synonyms. However, it is recommended that
default be used only when the implementation is in the same class as the corresponding
abstract definition; override should be used in other cases. This records the intended role of themember implementation.
Implementations may override methods from System.Object:
type BaseClass() =override obj.ToString() = "I'm an instance of BaseClass"type SubClass(x: int) =inherit BaseClass()
override obj.ToString() = "I'm an instance of SubClass"In this example, BaseClass inherits from System.Object and overrides the ToString method from that class.
The SubClass, in turn, inherits from BaseClass and overrides its version of the ToString method.
Implementations may include abstract property members:[<AbstractClass>]
type BaseClass() =let mutable data1 = 0
let mutable data2 = 0
abstract AbstractProperty : int
abstract AbstractSettableProperty : int

with get, set

abstract AbstractPropertyWithDefaultImplementation : int
default obj.AbstractPropertyWithDefaultImplementation = 3
abstract AbstractSettablePropertyWithDefaultImplementation : int
default obj.AbstractSettablePropertyWithDefaultImplementation
with get() = data2
139

with get, setand set v = data2 <- v
type SubClass(x: int) =inherit BaseClass()
let mutable data1b = 0
let mutable data2b = 0
override obj.AbstractProperty = 3 + x
override obj.AbstractSettableProperty
with get() = data1b + x
and set v = data1b <- v - x
override obj.AbstractPropertyWithDefaultImplementation = 6 + x
override obj.AbstractSettablePropertyWithDefaultImplementation
with get() = data2b + x
and set v = data2b <- v - x
The same rules apply to both property members and method members. In the preceding example, BaseClass
includes abstract properties named AbstractProperty, AbstractSettableProperty,
AbstractPropertyWithDefaultImplementation, andAbstractSettablePropertyWithDefaultImplementation and provides default implementations for the latter
two. SubClass provides implementations for AbstractProperty and AbstractSettableProperty, andoverrides the default implementations for AbstractPropertyWithDefaultImplementation and
AbstractSettablePropertyWithDefaultImplementation.
Implementation members may also implement CLI events (§8.13.9). In this case, the member should be marked
with the CLIEvent attribute. For example:
type ChannelChangedHandler = delegate of obj * int -> unit[<AbstractClass>]
type BaseClass() =[<CLIEvent>]
abstract ChannelChanged : IEvent<ChannelChangedHandler, int>
type SubClass() =inherit BaseClass()
let mutable channel = 7
let channelChanged = new Event<ChannelChangedHandler, int>()[<CLIEvent>]
override self.ChannelChanged = channelChanged.Publish
member self.Channel
with get () = channeland set v = channel <- v; channelChanged.Trigger(self, channel)
BaseClass implements the CLI event IEvent, so the abstract member ChannelChanged is marked with[<CLIEvent>] as described earlier in §8.13.9. SubClass provides an implementation of the abstract member, so
the [<CLIEvent>] attribute must also precede the override declaration in SubClass.

8.14.3 Interface Implementations
An interface implementation specifies how objects of a given type support a particular interface. An interface in a
type definition indicates that objects of the defined type support the interface. For example:
type IIncrement =abstract M : int -> int
type IDecrement =abstract M : int -> int

140

type C() =interface IIncrement withmember x.M(n) = n + 1
interface IDecrement withmember x.M(n) = n - 1
The first two definitions in the example are implementations of the interfaces IIncrement and IDecrement. In
the last definition,the type C supports these two interfaces.
No type may implement multiple different instantiations of a generic interface, either directly or throughinheritance. For example, the following is not permitted:// This type definition is not permitted because it implements two instantiations// of the same generic interface
type ClassThatTriesToImplemenTwoInstantiations() =interface System.IComparable<int> withmember x.CompareTo(n : int) = 0
interface System.IComparable<string> withmember x.CompareTo(n : string) = 1
Each member of an interface implementation is checked as follows:

The member must be an instance member definition.



Dispatch Slot Inference (§14.7) is applied.



The member is checked under the assumption that the “this” variable has the enclosing type.

In the following example, the value x has type C.
type C() =interface IIncrement withmember x.M(n) = n + 1
interface IDecrement withmember x.M(n) = n - 1
All interface implementations are made explicit. In its first implementation, every interface must be completely
implemented, even in an abstract class. However, interface implementations may be inherited from a base class.
In particular, if a class C implements interface I, and a base class of C implements interface I, then C is not
required to implement all the methods of I;it can implement all, some, or none of the methods instead. For
example:type I1 =abstract V1 : string
abstract V2 : string
type I2 =inherit I1
abstract V3 : string
type C1() =interface I1 withmember this.V1 = "C1"
member this.V2 = "C2"// This is OK
type C2() =inherit C1()// This is also OK; C3 implements I2 but not I1.
type C3() =inherit C1()
interface I2 with141

member this.V3 = "C3"// This is also OK; C4 implements one method in I1.
type C4() =inherit C1()
interface I1 withmember this.V2 = "C2b"8.15 Equality, Hashing, and Comparison
Functional programming in F# frequently involves the use of structural equality, structural hashing, and structuralcomparison. For example, the following expression evaluates to true, because tuple types support structural
equality:(1, 1 + 1) = (1, 2)
Likewise, these two function calls return identical values:
hash (1, 1 +1 )hash (1,2)
Similarly, an ordering on constituent parts of a tuple induces an ordering on tuples themselves, so all thefollowing evaluate to true:(1, 2) < (1, 3)(1, 2) < (2, 3)(1, 2) < (2, 1)(1, 2) > (1, 0)
The same applies to lists, options, arrays, and user-defined record, union, and struct types whose constituent
field types permit structural equality, hashing, and comparison. For example, given:
type R = R of int * int
then all of the following also evaluate to true:
R (1, 1 + 1) = R (1, 2)
R (1, 3) <> R (1, 2)
hash (R (1, 1 + 1)) = hash (R (1, 2))
R (1, 2) < R (1, 3)
R (1, 2) < R (2, 3)
R (1, 2) < R (2, 1)
R (1, 2) > R (1, 0)
To facilitate this, by default, record, union, struct, and exception type definitions, called structural types, implicitly
include compiler-generated declarations for structural equality, hashing, and comparison. These implicit
declarations consist of the following for structural equality and hashing:override x.GetHashCode() = ...
override x.Equals(y:obj) = ...
interface System.Collections.IStructuralEquatable withmember x.Equals(yobj: obj, comparer: System.Collections.IEqualityComparer) = ...
member x.GetHashCode(comparer: System.IEqualityComparer) = ...
The following declarations enable structural comparison:interface System.IComparable withmember x.CompareTo(y:obj) = ...
interface System.Collections.IStructuralComparable withmember x.CompareTo(yobj: obj, comparer: System.Collections.IComparer) = ...

142

Implicit declarations are never generated for interface, delegate, class, or enum types. Enum types implicitly
derive support for equality, hashing, and comparison through their underlying representation as integers.

8.15.1 Equality Attributes
Several attributes affect the equality behavior of types:
Microsoft.FSharp.Core.NoEquality
Microsoft.FSharp.Core.ReferenceEquality
Microsoft.FSharp.Core.StructuralEquality
Microsoft.FSharp.Core.CustomEquality
The following table lists the effects of each attribute on a type:
Attrribute

EffectNoEquality

 No equality or hashing is generated for the type.
 The type does not satisfy the ty : equality constraint.
 No equality or hashing is generated for the type.
 The defaults for System.Object will implicitly be used.
 The type must be a structural type.
 All structural field types ty must satisfy ty : equality.
 The type must have an explicit implementation ofoverride Equals(obj: obj)
 For a non-structural type, the default is ReferenceEquality.
 For a structural type:The default is NoEquality if any structural field type F fails F : equality.
The default is StructuralEquality if all structural field types F satisfy
F : equality.

ReferenceEquality
StructuralEquality
CustomEquality
None

Equality inference also determines the constraint dependencies of a generic structural type. That is:

If a structural type has a generic parameter 'T and T : equality is necessary to make the type default to
StructuralEquality, then the EqualityConditionalOn constraint dependency is inferred for 'T.

8.15.2 Comparison Attributes
The comparison behavior of types can be affected by the following attributes:
Microsoft.FSharp.Core.NoComparison
Microsoft.FSharp.Core.StructuralComparison
Microsoft.FSharp.Core.CustomComparison
The following table lists the effects of each attribute on a type.
AttributeEffect

NoComparison

 No comparisons are generated for the type.
 The type does not satisfy the ty : comparison constraint.
 The type must be a structural type other than an exception type.
 All structural field types must ty satisfy ty : comparison.
 An exception type may not have the StructuralComparison attribute.
 The type must have an explicit implementation of one or both of the following:interface System.IComparable
interface System.Collections.IStructuralComparable
 A structural type that has an explicit implementation of one or both of thesecontracts must specify the CustomComparison attribute.
 For a non-structural or exception type, the default is NoComparison.
 For any other structural type:The default is NoComparison if any structural field type F fails F : comparison.
The default is StructuralComparison if all structural field types F satisfy
F : comparison.

StructuralComparison

CustomComparison

None

143

This check also determines the constraint dependencies of a generic structural type. That is:

If a structural type has a generic parameter 'T and T : comparison is necessary to make the type default
to StructuralComparison, then the ComparisonConditionalOn constraint dependency is inferred for 'T.

For example:[<StructuralEquality; StructuralComparison>]type X = X of (int -> int)
results in the following message:
The struct, record or union type 'X' has the 'StructuralEquality' attributebut the component type '(int -> int)' does not satisfy the 'equality' constraint
For example, given
type R1 ={ myData : int }static member Create() = { myData = 0 }[<ReferenceEquality>]
type R2 ={ mutable myState : int }static member Fresh() = { myState = 0 }[<StructuralEquality; NoComparison >]type R3 ={ someType : System.Type }static member Make() = { someType = typeof<int> }then the following expressions all evaluate to true:
R1.Create() = R1.Create()not (R2.Fresh() = R2.Fresh())
R3.Make() = R3.Make()
Combinations of equality and comparion attributes are restricted. If any of the following attributes are present,
they may be used only in the following combinations:

No attributes

[<NoComparison>] on any type[<NoEquality; NoComparison>] on any type[<CustomEquality; NoComparison>] on a structural type[<ReferenceEquality>] on a non-struct structural type[<ReferenceEquality; NoComparison>] on a non-struct structural type[<StructuralEquality; NoComparison>] on a structural type[<CustomEquality; CustomComparison>] on a structural type[<StructuralEquality; CustomComparison>] on a structural type[<StructuralEquality; StructuralComparison>] on a structural type8.15.3 Behavior of the Generated Object.Equals Implementation
For a type definition T, the behavior of the generated override x.Equals(y:obj) = ... implementation is asfollows.

144

1.

If the interface System.IComparable has an explicit implementation, then just call
System.IComparable.CompareTo:
override x.Equals(y : obj) =((x :> System.IComparable).CompareTo(y) = 0)2.

Otherwise:Convert the y argument to type T. If the conversion fails, return false.

Return false if T is a reference type and y is null.



If T is a struct or record type, invoke Microsoft.FSharp.Core.Operators.(=) on each corresponding
pair of fields of x and y in declaration order. This method stops at the first false result and returns
false.



If T is a union type, invoke Microsoft.FSharp.Core.Operators.(=) first on the index of the union
cases for the two values, then on each corresponding field pair of x and y for the data carried by theunion case. This method stops at the first false result and returns false.



If T is an exception type, invoke Microsoft.FSharp.Core.Operators.(=) on the index of the tags forthe two values, then on each corresponding field pair for the data carried by the exception. This method
stops at the first false result and returns false.

8.15.4 Behavior of the Generated CompareTo Implementations
For a type T, the behavior of the generated System.IComparable.CompareTo implementation is as follows:Convert the y argument to type T . If the conversion fails, raise the InvalidCastException.



If T is a reference type and y is null, return 1.



If T is a struct or record type, invoke Microsoft.FSharp.Core.Operators.compare on each corresponding
pair of fields of x and y in declaration order, and return the first non-zero result.



If T is a union type, invoke Microsoft.FSharp.Core.Operators.compare first on the index of the union
cases for the two values, and then on each corresponding field pair of x and y for the data carried by theunion case. Return the first non-zero result.

The first few lines of this code can be written:
interface System.IComparable withmember x.CompareTo(y:obj) =let y = (obj :?> T) in
match obj with| null -> 1
| _ -> ...

8.15.5 Behavior of the Generated GetHashCode Implementations
For a type T, the generated System.Object.GetHashCode() override implementd a combination hash of thestructural elements of a structural type.

8.15.6 Behavior of Hash, =, and Compare
The generated equality, hashing, and comparison declarations that are described in sections 8.15.3, 8.15.4, and8.15.5 use the hash, = and compare functions from the F# library. The behavior of these library functions isdefined by the pseudocode later in this section. This code ensures:

Ordinal comparison for strings

145



Structural comparison for arrays

Natural ordering for native integers (which do not support System.IComparable)

1468.15.6.1 Pseudocode for Microsoft.FSharp.Core.Operators.compare
Note: In practice, fast (but semantically equivalent) code is emitted for direct calls to (=), compare,and hash for all base types, and faster paths are used for comparing most arrays.
open System/// Pseudo code for code implementation of generic comparison.
let rec compare x y =let xobj = box x
let yobj = box ymatch xobj, yobj with| null, null -> 0
| null, _ -> -1| _, null -> 1// Use Ordinal comparison for strings| (:? string as x),(:? string as y) ->String.CompareOrdinal(x, y)// Special types not supporting IComparable| (:? Array as arr1), (:? Array as arr2) ->... compare the arrays by rank, lengths and elements ...
| (:? nativeint as x),(:? nativeint as y) ->... compare the native integers x and y....
| (:? unativeint as x),(:? unativeint as y) ->... compare the unsigned integers x and y....
// Check for IComparable| (:? IComparable as x),_ -> x.CompareTo(yobj)| _,(:? IComparable as yc) -> -(sign(yc.CompareTo(xobj)))// Otherwise raise a runtime error| _ -> raise (new ArgumentException(...))

8.15.6.2 Pseudo code for Microsoft.FSharp.Core.Operators.(=)
Note: In practice, fast (but semantically equivalent) code is emitted for direct calls to (=), compare,and hash for all base types, and faster paths are used for comparing most arrays
open System/// Pseudo code for core implementation of generic equality.
let rec (=) x y =let xobj = box x
let yobj = box ymatch xobj,yobj with| null,null -> true
| null,_ -> false| _,null -> false// Special types not supporting IComparable| (:? Array as arr1), (:? Array as arr2) ->... compare the arrays by rank, lengths and elements ...
// Ensure NaN semantics on recursive calls| (:? float as f1), (:? float as f2) ->... IEEE equality on f1 and f2...
| (:? float32 as f1), (:? float32 as f2) ->147

... IEEE equality on f1 and f2...
// Otherwise use Object.Equals. This is reference equality// for reference types unless an override is provided (implicitly// or explicitly).
| _ -> xobj.Equals(yobj)

148

Units Of MeasureF# supports static checking of units of measure. Units of measure, or measures for short, are like types in thatthey can appear as parameters to other types and values (as in float<kg>, vector<m/s>, add<m>), can containvariables (as in float<'U>), and are checked for consistency by the type-checker.
However, measures differ from types in several important ways:

Measures play no role at runtime; in fact, they are erased.

Measures obey special rules of equivalence, so that N m can be interchanged with m N.



Measures are supported by special syntax.

The syntax of constants (§4.3) is extended to support numeric constants with units of measure. The syntax oftypes is extended with measure type annotations.
measure-literal-atom :=long-ident -- named measure e.g. kg( measure-literal-simp )-- parenthesized measure, such as (N m)measure-literal-power :=measure-literal-atommeasure-literal-atom ^ int32 -- power of measure, such as m^3measure-literal-seq :=measure-literal-power
measure-literal-power measure-literal-seq
measure-literal-simp :=measure-literal-seq-- implicit product, such as m s^-2
measure-literal-simp * measure-literal-simp-- product, such as m * s^3measure-literal-simp / measure-literal-simp-- quotient, such as m/s^2/ measure-literal-simp-- reciprocal, such as /s
1 -- dimensionlessmeasure-literal :=_ -- anonymous measure
measure-literal-simp-- simple measure, such as N mconst :=...
sbyte < measure-literal >int16 < measure-literal >int32 < measure-literal >int64 < measure-literal >ieee32 < measure-literal >ieee64 < measure-literal >decimal < measure-literal >-- 8-bit integer constant-- 16-bit integer constant-- 32-bit integer constant-- 64-bit integer constant-- single-precision float32 constant-- double-precision float constant-- decimal constantmeasure-atom :=typar
long-ident( measure-simp )-- variable measure, such as 'U-- named measure, such as kg-- parenthesized measure, such as (N m)measure-power :=measure-atommeasure-atom ^ int32-- power of measure, such as m^3measure-seq :=measure-power
measure-power measure-seq

measure-simp :=measure-seq-- implicit product, such as 'U 'V^3
measure-simp * measure-simp -- product, such as 'U * 'V
measure-simp / measure-simp -- quotient, such as 'U / 'V
/ measure-simp-- reciprocal, such as /'U
1 -- dimensionless measure (no units)measure :=_measure-simp-- anonymous measure-- simple measure, such as 'U 'V

Measure definitions use the special Measure attribute on type definitions. Measure parameters use the syntax ofgeneric parameters with the same special Measure attribute to parameterize types and members by units ofmeasure. The primitive types sbyte, int16, int32, int64, float, float32, and decimal have nonparameterized (dimensionless) and parameterized versions.
Here is a simple example:[<Measure>] type m// base measure: meters[<Measure>] type s// base measure: seconds[<Measure>] type sqm = m^2// derived measure: square meters
let areaOfTriangle (baseLength:float<m>, height:float<m>) : float<sqm> =baseLength*height/2.0
let distanceTravelled (speed:float<m/s>, time:float
) : float<m> = speed*time
As with ordinary types, F# can infer that functions are generic in their units. For example, consider the following
function definitions:
let sqr (x:float<_>) = x*x
let sumOfSquares x y = sqr x + sqr y
The inferred types are:val sqr : float<'u> -> float<'u ^ 2>
val sumOfSquares : float<'u> -> float<'u> -> float<'u ^ 2>
Measures are type-like annotations such as kg or m/s or m^2. Their special syntax includes the use of * and / forproduct and quotient of measures, juxtaposition as shorthand for product, and ^ for integer powers.

9.1 Measures
Measures are built from:

Atomic measures from long identifiers such as SI.kg or MyUnits.feet.

Product measures, which are written measure measure (juxtaposition ) or measure * measure.



Quotient measures, which are written measure / measure.

Integer powers of measures, which are written measure ^ int.

Dimensionless measures, which are written 1.



Variable measures, which are written 'u or 'U. Variable measures can include anonymous measures _,which indicates that the compiler can infer the measure from the context.

Dimensionless measures indicate “without units,” but are rarely needed, because non-parameterized types suchas float are aliases for the parameterized type with 1 as parameter, that is, float = float<1>.

150

The precedence of operations involving measure is similar to that for floating-point expressions:

Products and quotients (* and /) have the same precedence, and associate to the left, but juxtaposition has
higher syntactic precedence than both * and /.

Integer powers (^) have higher precedence than juxtaposition.



The / symbol can also be used as a unary reciprocal operator.

9.2 Constants Annotated by Measures
A floating-point constant can be annotated with its measure by specifying a literal measure in angle brackets
following the constant.
Measure annotations on constants may not include measure variables.
Here are some examples of annotated constants:
let earthGravity = 9.81f<m/s^2>
let atmosphere = 101325.0<N m^-2>
let zero = 0.0f<_>
Constants that are annotated with units of measure are assigned a corresponding numeric type with the measureparameter that is specified in the annotation. In the example above, earthGravity is assigned the typefloat32<m/s^2>, atmosphere is assigned the type float<N/m^2> and zero is assigned the type float<'U>.

9.3 Relations on Measures
After measers are parsed and checked, they are maintained in the following normalized form:
measure-int := 1 | long-ident | measure-par | measure-int measure-int | / measure-int
Powers of measures are expanded. For example, kg^3 is equivalent to kg kg kg.
Two measures are indistinguishable if they can be made equivalent by repeated application of the following rules:Commutativity. measure-int1 measure-int2 is equivalent to measure-int2 measure-int1.

Associativity. It does not matter what grouping is used for juxtaposition (product) of measures, soparentheses are not required. For example, kg m s can be split as the product of kg m and s, or as the
product of kg and m s.



Identity. 1 measure-int is equivalent to measure-int.



Inverses. measure-int / measure-int is equivalent to 1.



Abbreviation. long-ident is equivalent to measure if a measure abbreviation of the form [<Measure>]type long-ident = measure is currently in scope.

Note that these are the laws of Abelian groups together with expansion of abbreviations.
For example, kg m / s^2 is the same as m kg / s^2.
For presentation purposes (for example, in error messages), measures are presented in the normalized form thatappears at the beginning of this section, but with the following restrictions:Powers are positive and greater than 1. This splits the measure into positive powers and negative powers,
separated by /.

151



Atomic measures are ordered as follows: measure parameters first, ordered alphabetically, followed bymeasure identifiers, ordered alphabetically.

For example, the measure expression m^1 kg s^-1 would be normalized to kg m / s.
This normalized form provides a convenient way to check the equality of measures: given two measure
expressions measure-int1 and measure-int2, reduce each to normalized form by using the rules ofcommutativity, associativity, identity, inverses and abbreviation, and then compare the syntax.

9.3.1 Constraint Solving
The mechanism described in §14.5 is extended to support equational constraints between measure expressions.
Such expressions arise from equations between parameterized types—that is, when type<tyarg11,...,
tyarg1n> = type<tyarg21,..., tyarg2n> is reduced to a series of constraints tyarg1i = tyarg2i. For thearguments that are measures, rather than types, the rules listed in §9.3 are applied to obtain primitive equationsof the form 'U = measure-int where 'U is a measure variable and measure-int is a measure expression ininternal form. The variable 'U is then replaced by measure-int wherever else it occurs. For example, theequation float<m^2/s^2> = float<'U^2> would be reduced to the constraint m^2/s^2 = 'U^2, which would
be further reduced to the primitive equation 'U = m/s.
If constraints cannot be solved, a type error occurs. For example, the following expression
fun (x : float<m^2>, y : float
) -> x + ywould eventually)result in the constraint m^2 = s, which cannot be solved, indicating a type error.

9.3.2 Generalization of Measure Variables
Analogous to the process of generalization of type variables described in §14.6.7, a generalization procedure
produces measure variables over which a value, function, or member can be generalized.

9.4 Measure Definitions
Measure definitions define new named units of measure by using the same syntax as for type definitions, with theaddition of the Measure attribute. For example:[<Measure>] type kg[<Measure>] type m[<Measure>] type s[<Measure>] type N = kg / m s^2
A primitive measure abbreviation defines a fresh, named measure that is distinct from other measures. Measureabbreviations, like type abbreviations, define new names for existing measures. Also like type abbreviations,
repeatedly eliminating measure abbreviations in favor of their equivalent measures must not result in infinite
measure expressions. For example, the following is not a valid measure definition because it results in the infinite
squaring of X:[<Measure>] type X = X^2
Measure definitions and abbreviations may not have type or measure parameters.

152

9.5 Measure Parameter Definitions
Measure parameter definitions can appear wherever ordinary type parameter definitions can (see §5.2.9). If an
explicit parameter definition is used, the parameter name is prefixed by the special Measure attribute. For
example:val sqr<[<Measure>] 'U> : float<'U> -> float<'U^2>
type Vector<[<Measure>] 'U> ={ X: float<'U>;
Y: float<'U>;
Z: float<'U>}
type Sphere<[<Measure>] 'U> ={ Center:Vector<'U>;
Radius:float<'U> }type Disc<[<Measure>] 'U> ={ Center:Vector<'U>;
Radius:float<'U>;
Norm:Vector<1> }type SceneObject<[<Measure>] 'U> =| Sphere of Sphere<'U>| Disc of Disc<'U>
Internally, the type checker distinguishes between type parameters and measure parameters by assigning one oftwo sorts (Type or Measure) to each parameter. This technique is used to check the actual arguments to types
and other parameterized definitions. The type checker rejects ill-formed types such as float<int> andIEnumerable<m/s>.

9.6 Measure Parameter Erasure
In contrast to type parameters on generic types, measure parameters are not exposed in the metadata that theruntime interprets; instead, measures are erased. Erasure has several consequences:

Casting is with respect to erased types.



Method application resolution (see §14.4) is with respect to erased types.



Reflection is with respect to erased types.

9.7 Type Definitions with Measures in the F# Core Library
The F# core library defines the following types:
type float<[<Measure>] 'U>
type float32<[<Measure>] 'U>
type decimal<[<Measure>] 'U>
type int<[<Measure>] 'U>
type sbyte<[<Measure>] 'U>
type int16<[<Measure>] 'U>
type int64<[<Measure>] 'U>

153

Note: These definitions are called measure-annotated base types and are marked with the
MeasureAnnotatedAbbreviation attribute in the implementation of the library. The
MeasureAnnotatedAbbreviation attribute is not for use in user code and in future revisions of thelanguage may result in a warning or error.
These type definitions have the following special properties:


They extend System.ValueType.



They explicitly implement System.IFormattable, System.IComparable, System.IConvertible, andcorresponding generic interfaces, instantiated at the given type—for example,
System.IComparable<float<'u>> and System.IEquatable<float<'u>> (so that you can invoke, forexample, CompareTo after an explicit upcast).



As a result of erasure, their compiled form is the corresponding primitive type.



For the purposes of constraint solving and other logical operations on types, a type equivalence holds
between the unparameterized primitive type and the corresponding measured type definition that is
instantiated at <1>:sbyte = sbyte<1>
int16 = int16<1>int32 = int32<1>
int64 = int64<1>float = float<1>float32 = float32<1>decimal = decimal<1>

The measured type definitions sbyte, int16, int32, int64, float32, float, and decimal are assumed tohave additional static members that have the measure types that are listed in the table. Note that N is any ofthese types, and F is either float32 or float.
MemberMeasure Type

Sqrt

F<'U^2> -> F<'U>

Atan2

F<'U> -> F<'U> -> F<1>

op_Addition

N<'U> -> N<'U> -> N<'U>

op_Subtraction
op_Modulus
op_Multiply

N<'U> -> N<'V> -> N<'U 'V>

op_Division

N<'U> -> N<'V> -> N<'U/'V>

Abs

N<'U> -> N<'U>

op_UnaryNegation
op_UnaryPlus
Sign

N<'U> -> int

This mechanism is used to support units of measure in the following math functions of the F# library:(+),(-), (*), (/),(%),(~+),(~-),abs, sign, atan2 and sqrt.

154

Namespaces and Modules
F# is primarily an expression-based language. However, F# source code units are made up of declarations,some of which can contain further declarations. Declarations are grouped using namespace declaration groups,
type definitions, and module definitions. These also have corresponding forms in signatures. For example, a file
may contain multiple namespace declaration groups, each of which defines types and modules, and the types
and modules may contain member, function, and value definitions, which contain expressions.
Declaration elements are processed in the context of an environment. The definition of the elements of anenvironment is found in §14.1.
namespace-decl-group :=namespace long-ident module-elems
namespace global module-elems-- elements within a namespace-- elements within no namespacemodule-defn :=attributesopt module accessopt ident = module-defn-bodymodule-defn-body :=begin module-elemsopt end
module-elem :=module-function-or-value-defn
type-defns
exception-defn
module-defn
module-abbrev
import-decl
compiler-directive-decl-- function or value definitions-- type definitions-- exception definitions-- module definitions-- module abbreviations-- import declarations-- compiler directives

module-function-or-value-defn :=attributesopt let function-defn
attributesopt let value-defn
attributesopt let recopt function-or-value-defns
attributesopt do expr
import-decl := open long-ident
module-abbrev := module ident = long-ident
compiler-directive-decl := # ident string ... string
module-elems := module-elem ... module-elem
access :=privateinternal
public10.1 Namespace Declaration Groups
Modules and types in an F# program are organized into namespaces, which encompass the identifiers that are
defined in the modules and types. New components may contribute entities to existing namespaces. Each suchcontribution to a namespace is called a namespace declaration group.

In the following example, the MyCompany.MyLibrary namespace contains Values and x:
namespace MyCompany.MyLibrary
module Values1 =let x = 1
A namespace declaration group is the basic declaration unit within an F# implementation file and is of the form
namespace long-ident
module-elems
The long-ident must be fully qualified. Each such group contains a series of module and type definitions that
contribute to the indicated namespace. An implementation file may contain multiple namespace declarationgroups, as in this example:
namespace MyCompany.MyOtherLibrary
type MyType() =let x = 1
member v.P = x + 2
module MyInnerModule =let myValue = 1
namespace MyCompany.MyOtherLibrary.Collections
type MyCollection(x : int) =member v.P = x
Namespace declaration groups may not be nested.
A namespace declaration group can contain type and module definitions, but not function or value definitions. For
example:namespace MyCompany.MyLibrary// A type definition in a namespace
type MyType() =let x = 1
member v.P = x+2// A module definition in a namespacemodule MyInnerModule =let myValue = 1// The following is not allowed: value definitions are not allowed in namespaces
let addOne x = x + 1
When a namespace declaration group N is checked in an environment env, the individual declarations arechecked in order and an overall namespace declaration group signature Nsig is inferred for the module. An entry
for N is then added to the ModulesAndNamespaces table in the environment env (see §14.1.3).
Like module declarations, namespace declaration groups are processed sequentially rather than simultaneously,
so that later namespace declaration groups are not in scope when earlier ones are processed. This prevents
invalid recursive definitions. In the following example, the declaration of x in Module1 generates an error because
the Utilities.Part2 namespace is not in scope:156

namespace Utilities.Part1
module Module1 =let x = Utilities.Part2.Module2.x + 1// error (Part2 not yet declared)

namespace Utilities.Part2
module Module2 =let x = Utilities.Part1.Module1.x + 2
Within a namespace declaration group, the namespace itself is implicitly opened if any preceding namespacedeclaration groups or referenced assemblies contribute to it. For example:
namespace MyCompany.MyLibrary
module Values1 =let x = 1
namespace MyCompany.MyLibrary// Here, the implicit open of MyCompany.MyLibrary brings Values1 into scope
module Values2 =let x = Values1.x

10.2 Module Definitions
A module definition is a named collection of declarations such as values, types, and function values. Groupingcode in modules helps keep related code together and helps avoid name conflicts in your program. For example:
module MyModule =let x = 1
type Foo = A | B
module MyNestedModule =let f y = y + 1
type Bar = C | D
When a module definition M is checked in an environment env0, the individual declarations are checked in orderand an overall module signature Msig is inferred for the module. An entry for M is then added to the
ModulesAndNamespaces table to environment env0 to form the new environment used for checking subsequent
modules.
Like namespace declaration groups, module definitions are processed sequentially rather than simultaneously,
so that later modules are not in scope when earlier ones are processed.
module Part1 =let x = Part2.StorageCache()// error (Part2 not yet declared)

module Part2 =type StorageCache() =member cache.Clear() = ()No two types or modules may have identical names in the same namespace. The[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>] attribute adds thesuffix Module to the name of a module to distinguish the module name from a type of a similar name.

157

For example, this is frequently used when defining a type and a set of functions and values to manipulate valuesof this type.
type Cat(kind: string) =member x.Meow() = printfn "meow"
member x.Purr() = printfn "purr"
member x.Kind = kind[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Cat =let tabby = Cat "Tabby"
let purr (c:Cat) = c.Purr()
let purrTwice (c:Cat) = purr(); purr()
Cat.tabby |> Cat.purr |> Cat.purrTwice

10.2.1 Function and Value Definitions in Modules
Function and value definitionsin modules introduce named values and functions.
let recopt function-or-value-defn1 and ... and function-or-value-defnn
The following example defines value x and functions id and fib:module M =let x = 1
let id x = x
let rec fib x = if x <= 2 then 1 else fib (n - 1) + fib (n - 2)
Function and value definitions in modules may declare explicit type variables and type constraints:
let pair<'T>(x : 'T) = (x, x)
let dispose<'T when 'T :> System.IDisposable>(x : 'T) = x.Dispose()
let convert<'T, 'U>(x) = unbox<'U>(box<'T>(x))
A value definition that has explicit type variables is called a type function (§10.2.3).
Function and value definitions may specify attributes:// A value definition with the System.Obsolete attribute[<System.Obsolete("Don't use this")>]let oneTwoPair = (1, 2)// A function definition with an attribute[<System.Obsolete("Don't use this either")>]let pear v = (v, v)
By the use of pattern matching, a value definition can define more than one value . In such cases, the attributes
apply to each value.
// A value definition that defines two values, each with an attribute[<System.Obsolete("Don't use this")>]let (a, b) = (1, 2)
Values may be declared mutable:// A value definition that defines a mutable value
let mutable count = 1
let freshName() = (count <- count + 1; count)
Function and value definitions in modules are processed in the same way as function and value definitions in
expressions (§14.6), with the following adjustments:

158



Each defined value may have an accessibility annotation (§10.5). By default, the accessibility annotation of afunction or value definition in a module is public.

Each defined value is externally accessible if its accessibility annotation is public and it is not hidden by an
explicit signature. Externally accessible values are guaranteed to have compiled CLI representations incompiled CLI binaries.

Each defined value can be used to satisfy the requirements of any signature for the module (§11.2).

Each defined value is subject to arity analysis (§14.10).



Values may have attributes, including the ThreadStatic or ContextStatic attribute.

10.2.2 Literal Definitions in Modules
Value definitions in modules may have the Literal attribute. This attribute causes the value to be compiled as aconstant. For example:[<Literal>]let PI = 3.141592654
Literal values may be used in custom attributes and pattern matching. For example:[<Literal>]let StartOfWeek = System.DayOfWeek.Monday[<MyAttribute(StartOfWeek)>]
let feeling(day) =match day with
| StartOfWeek -> "rough"| _ -> "great"
A value that has the Literal attribute is subject to the following restrictions:

It may not be marked mutable or inline.

It may not also have the ThreadStatic or ContextStatic attributes.



The right-hand side expression must be a literal constant expression that is made up of either:

A simple constant expression, with the exception of (), native integer literals, unsigned native integer
literals, byte array literals, BigInteger literals, and user-defined numeric literals.

—OR—A reference to another literal.

10.2.3 Type Function Definitions in Modules
Value definitions within modules may have explicit generic parameters. For example, ‘T is a generic parameterto the value empty:
let empty<'T> : (list<'T> * Set<'T>) = ([], Set.empty)
A value that has explicit generic parameters but has arity [] (that is, no explicit function parameters) is called atype function. The following are some example type functions from the F# library:
val typeof<'T> : System.Type
val sizeof<'T> : int
module Set =val empty<'T> : Set<'T>
module Map =val empty<'Key,'Value> : Map<'Key,'Value>

159

Type functions are rarely used in F# programming, although they are convenient in certain situations. Typefunctions are typically used for:

Pure functions that compute type-specific information based on the supplied type arguments.

Pure functions whose result is independent of inferred type arguments, such as empty sets and maps.

Type functions receive special treatment during generalization (§14.6.7) and signature conformance (§11.2).
They typically have either the RequiresExplicitTypeArguments attribute or the GeneralizableValue
attribute. Type functions may not be defined inside types, expressions, or computation expressions.
In general, type functions should be used only for computations that do not have observable side effects.
However, type functions may still perform computations. In this example, r is a type function that calculates thenumber of times it has been called
let mutable count = 1
let r<'T> = (count <- count + 1); ref ([] : 'T list);;// count = 1
let x1 = r<int>// count = 2
let x2 = r<int>// count = 3
let z0 = x1// count = 3
The elaborated form of a type function is that of a function definition that takes one argument of type unit. Thatis, the elaborated form of
let ident typar-defns= expris the same as the compiled form for the following declaration:
let ident typar-defns () = expr
References to type functions are elaborated to invocations of such a function.

10.2.4 Active Pattern Definitions in Modules
A value definition within a module that has an active-pattern-op-name introduces pattern-matching tags into
the environment when the module is accessed or opened. For example,
let (|A|B|C|) x = if x < 0 then A elif x = 0 then B else C
introduces pattern tags A, B, and C into the PatItems table in the name resolution environment.

10.2.5 “do” statements in Modules
A “do” statement within a module has the following form:
do exprThe expression expr is checked with an arbitrary initial type ty. After checking expr, ty is asserted to be equal
to unit. If the assertion fails, a warning rather than an error is reported. This warning is suppressed for plain
expressions without do in script files (that is, .fsx and .fsscript files).
A “do” statement may have attributes. In this example, the STAThread attribute specifies that main uses the
single-threaded apartment (STA) threading model of COM:
let main() =let form = new System.Windows.Forms.Form()
System.Windows.Forms.Application.Run(form)

160

[<STAThread>]do main()10.3 Import Declarations
Namespace declaration groups and module definitions can include import declarations in the following form:
open long-ident
Import declarations make elements of other namespace declaration groups and modules accessible by the use of
unqualified names. For example:
open Microsoft.FSharp.Collections
open System
Import declarations can be used in:Module definitions and their signatures.

Namespace declaration groups and their signatures.

An import declaration is processed by first resolving the long-ident to one or more namespace declarationgroups and/or modules [F1, ..., Fn] by Name Resolution in Module and Namespace Paths (§14.1.2). For example,
System.Collections.Generic may resolve to one or more namespace declaration groups—one for each
assembly that contributes a namespace declaration group in the current environment. Next, each Fi is added tothe environment successively by using the technique specified in §14.1.3. An error occurs if any Fi is a modulethat has the RequireQualifiedAccess attribute.

10.4 Module Abbreviations
A module abbreviation defines a local name for a module long identifier, as follows:
module ident = long-ident
For example:
module Ops = Microsoft.FSharp.Core.Operators
Module abbreviations can be used in:Module definitions and their signatures.

Namespace declaration groups and their signatures.

Module abbreviations are implicitly private to the module or namespace declaration group in which they appear.
A module abbreviation is processed by first resolving the long-ident to a list of modules by Name Resolution in
Module and Namespace Paths (see §14.1). The list is then appended to the set of names that are associatedwith ident in the ModulesAndNamespaces table.
Module abbreviations may not be used to abbreviate namespaces.

10.5 Accessibility Annotations
Accessibilities may be specified on declaration elements in namespace declaration groups and modules, and on
members in types. The table lists the accessibilities that can appear in user code:161

Accessibility

Description

publicNo restrictions on access.

privateAccess is permitted only from the enclosing type, module, or namespacedeclaration group.

internal

Access is permitted only from within the enclosing assembly, or from assemblies
whose name is listed using the InternalsVisibleTo attribute in the current
assembly.

The default accessibilities are public. Specifically:Function definitions, value definitions, type definitions, and exception definitions in modules are public.

Modules, type definitions, and exception definitions in namespaces are public.

Members in type definitions are public.

Some function and value definitions may not be given an accessibility and, by their nature, have restricted lexicalscope. In particular:Function and value definitions in classes are lexically available only within the class being defined, and only
from the point of their definition onward.

Module type abbreviations are lexically available only within the module or namespace declaration group
being defined, and only from their point of their definition onward.

Note that:

private on a member means “private to the enclosing type or module.”

private on a function or value definition in a module means “private to the module or namespacedeclaration group.”

private on a type, module, or type representation in a module means “private to the module.”

The CLI compiled form of all non-public entities is internal.
Note: The family and protected specifications are not supported in this version of the F#
language.
Accessibility modifiers can appear only in the locations summarized in the following table.
Component

Location

ExampleFunction or value definitionin module

Precedes mutable and inline

let private x = 1
let private inline f x = 1
let private mutable x = 1

Module definition

module private M =Precedes identifier

let x = 1
Type definition

type private C = A | B

Precedes identifier

type private C<'T> = A | B
val definition in a class

Precedes identifier

val private x : int

Explicit constructor

Precedes identifier

private new () = { inherit Base }Implicit constructor

Precedes identifier

type C private() = ...

162

Component

Location

ExampleMember definition

Precedes identifier, but cannot

member private x.X = 1

appear on: inherit definitions
 interface definitions
 abstract definitions
 Individual union cases
Accessibility for inherit,interface, and abstract
definitions is always the same asthat of the enclosing class.
Explicit property get or set
in a classmember v.Item

Precedes identifier

with private get i = 1
andType representation

private set i v = ()type Cases =Precedes identifier

private
| A
| B

163

Namespace and Module
Signatures
A signature file contains one or more namespace or module signatures, and specifies the functionality that is
implemented by its corresponding implementation file. It also can hide functionality that the corresponding
implementation file contains.
namespace-decl-group-signature :=namespace long-ident module-signature-elements
module-signature =module ident = module-signature-bodymodule-signature-element :=val mutableopt curried-sig-- value signatureval value-defn-- literal value signature
type type-signatures-- type(s) signature
exception exception-signature -- exception signature
module-signature-- submodule signature
module-abbrev-- local alias for a module
import-decl-- locally import contents of a module
module-signature-elements := module-signature-element ... module-signature-element
module-signature-body =begin module-signature-elements end
type-signature :=abbrev-type-signaturerecord-type-signature
union-type-signatureanon-type-signatureclass-type-signature
struct-type-signatureinterface-type-signatureenum-type-signaturedelegate-type-signature
type-extension-signature
type-signatures := type-signature ... and ... type-signature
type-signature-element :=attributesopt accessopt new : uncurried-sig
attributesopt member accessopt member-sig
attributesopt abstract accessopt member-sig
attributesopt override member-sig
attributesopt default member-sig
attributesopt static member accessopt member-sig
interface type-- constructor signature-- member signature-- member signature-- member signature-- member signature-- static member signature-- interface signatureabbrev-type-signature := type-name '=' typeunion-type-signature := type-name '=' union-type-cases type-extension-elementssignatureopt
record-type-signature := type-name '=' '{' record-fields '}' type-extensionelements-signatureopt
anon-type-signature := type-name '=' begin type-elements-signature end

class-type-signature := type-name '=' class type-elements-signature end
struct-type-signature := type-name '=' struct type-elements-signature end
interface-type-signature := type-name '=' interface type-elements-signature end
enum-type-signature := type-name '=' enum-type-cases
delegate-type-signature :=type-name '=' delegate-sig

type-extension-signature := type-name type-extension-elements-signature
type-extension-elements-signature := with type-elements-signature end
The begin and end tokens are optional when lightweight syntax is used.
Like module declarations, signature declarations are processed sequentially rather than simultaneously, so thatlater signature declarations are not in scope when earlier ones are processed.
namespace Utilities.Part1
module Module1 =val x : Utilities.Part2.StorageCache// error (Part2 not yet declared)

namespace Utilities.Part2
type StorageCache =new : unit -> unit

11.1 Signature Elements
A namespace or module signature declares one or more value signatures and one or more type definition
signatures. A type definition signature may include one or more member signatures, in addition to other elementsof type definitions that are specified in the signature grammar at the start of this chapter.
Note: This section does not yet document all constructs that may be used in signatures in F# 2.0.

11.1.1 Value Signatures
A value signature indicates that a value exists in the implementation. For example, in the signature of a module,
the following declares two value signatures:
module MyMap =val mapForward : index1: int * index2: int -> stringval mapBackward : name: string -> (int * int)
The corresponding implementation file might contain the following implementation:
module MyMap =let mapForward (index1:int, index2:int) = string index1 + "," + string index2
let mapBackward (name:string) = (0, 0)

11.1.2 Type Definition and Member Signatures
A type definition signature indicates that a corresponding type definition appears in the implementation. For
example, in an interface type, the following declares a type definition signature for Forward and Backward:
type IMap =interface
abstract Forward : index1: int * index2: int -> string166

abstract Backward : name: string -> (int * int)end
A member signature indicates that a corresponding member appears on the corresponding type definition in theimplementation. Member specifications must specify argument and return types, and can optionally specify
names and attributes for parameters.
For example, the following declares a type definition signature for a type with one constructor member, one
property member Kind and one method member Purr:
type Cat =new : kind:string -> Cat
member Kind : string
member Purr : unit -> Cat
The corresponding implementation file might contain the following implementation:
type Cat(kind: string) =member x.Meow() = printfn "meow"
member x.Purr() = printfn "purr"
member x.Kind = kind11.2 Signature Conformance
Values, types, and members that are present in implementations can be omitted in signatures, with the following
exceptions:Type abbreviations may not be hidden by signatures. That is, a type abbreviation type T = ty in an
implementation does not match type T (without an abbreviation) in a signature.



Any type that is represented as a record or union must reveal either all or none of its fields or cases, in thesame order as that specified in the implementation. Types that are represented as classes may reveal some,all, or none of their fields in a signature.



Any type that is revealed to be an interface, or a type that is a class or struct with one or more constructors
may not hide its inherit declaration, abstract dispatch slot declarations, or abstract interface declarations.

Note: This section does not yet document all checks made by the F# 2.0 language implementation.

11.2.1 Signature Conformance for Functions and Values
If both a signature and an implementation contain a function or value definition with a given name, the signatureand implementation must conform as follows:

The declared accessibilities, inline, and mutable modifiers must be identical in both the signature and theimplementation.

If either the signature or the implementation has the [<Literal>] attribute, both must have this attribute.
Furthermore, the declared literal values must be identical.



The number of generic parameters—both inferred and explicit—must be identical.



The types and type constraints must be identical up to renaming of inferred and/or explicit genericparameters. For example, assume a signature is written “val head : seq<'T> -> 'T” and the compiler
could infer the type “val head : seq<'a> -> 'a” from the implementation. These are considered identicalup to renaming the generic parameters.



The arities must match, as described in the next section.

167

11.2.1.1 Arity Conformance for Functions and Values
Arities of functions and values must conform between implementation and signature. Arities of values are implicit
in module signatures. A signature that contains the following results in the arity [A1...An] for F:
val F : ty1,1 * ... * ty1,A1 -> ... -> tyn,1 * ... * tyn,An -> rty
Arities in a signature must be equal to or shorter than the corresponding arities in an implementation, and theprefix must match. This means that F# makes a deliberate distinction between the following two signatures:val F: int -> int
andval F: (int -> int)
The parentheses indicate a top-level function, which might be a first-class computed expression that computes toa function value, rather than a compile-time function value.
The first signature can be satisfied only by a true function; that is, the implementation must be a lambda value as
in the following:
let F x = x + 1
Note: Because arity inference also permits right-hand-side function expressions, the implementation
may currently also be:
let F = fun x -> x + 1
The second signature
val F: (int -> int)
can be satisfied by any value of the appropriate type. For example:
let f =
let myTable = new System.Collections.Generic.Dictionary<int,int>(4)fun x ->
if myTable.ContainsKey x thenmyTable.[x]else
let res = x * x
myTable.[x] <- resres—or—let f = fun x -> x + 1—or—// throw an exception as soon as the module initialization is triggered
let f : int -> int = failwith "failure"
For both the first and second signatures, you can still use the functions as first-class function values from client
code—the parentheses simply act as a constraint on the implementation of the value.
The reason for this interpretation of types in value and member signatures is that CLI interoperability requires thatF# functions compile to methods, rather than to fields that are function values. Thus, signatures must containenough information to reveal the desired arity of a method as it is revealed to other CLI programming languages.

11.2.1.2 Signature Conformance for Type Functions
If a value is a type function, then its corresponding value signature must have explicit type arguments. For
example, the implementation
let empty<'T> : list<'T> = printfn "hello"; []168conforms to this signature:val empty<'T> : list<'T>
but not to this signature:val empty : list<'T>
The reason for this rule is that the second signature indicates that the value is, by default, generalizable(§14.6.7).

11.2.2 Signature Conformance for Members
If both a signature and an implementation contain a member with a given name, the signature andimplementation must conform as follows:

If one is an extension member, both must be extension members.



If one is a constructor, then both must be constructors.



If one is a property, then both must be properties.



The types must be identical up to renaming of inferred or explicit type parameters (as for functions andvalues).

The static, abstract, and override qualifiers must match precisely.



Abstract members must be present in the signature if a representation is given for a type.

Note: This section does not yet document all checks made by the F# 2.0 language implementation.

169

Program Structure andExecution
F# programs are made up of a collection of assemblies. F# assemblies are made up of static references toexisting assemblies, called the referenced assemblies, and an interspersed sequence of signature (.fsi) files,
implementation (.fs) files, script (.fsx or .fsscript) files, and interactively executed code fragments.
implementation-file :=namespace-decl-group ... namespace-decl-groupnamed-module
anonynmous-module
script-file := implementation-file-- script file, additional directives allowed
signature-file:=
namespace-decl-group-signature ... namespace-decl-group-signature
anonynmous-module-signature
named-module-signature
named-module :=module long-ident module-elems
anonymous-module :=module-elems
named-module-signature :=module long-ident module-signature-elements
anonymous-module-signature :=module-signature-elements
script-fragment :=module-elems-- interactively entered code fragment

A sequence of implementation and signature files is checked as follows.
1.

Form an initial environment sig-env0 and impl-env0 by adding all assembly references to the environment
in the order in which they are supplied to the compiler. This means that the following procedure is applied foreach referenced assembly:

Add the top level types, modules, and namespaces to the environment.

For each AutoOpen attribute in the assembly, find the types, modules, and namespaces that the
attribute references and add these to the environment.

The resulting environment becomes the active environment for the first file to be processed.
2.

For each file:

If the ith file is a signature file file.fsi:a.

Check it against the current signature environment sig-envi-1, which generates the signature
Sigfile for the current file.

b.

Add Sigfile to sig-envi-1 to produce sig-envi to make it available for use in later signature files.

The processing of the signature file has no effect on the implementation environment, so impl-envi isidentical to impl-envi-1.



If the file is an implementation file file.fs, check it against the environment impl-envi-1, which gives
elaborated namespace declaration groups Implfile.
a.

If a corresponding signature Sigfile exists, check Implfile against Sigfile during this process(§11.2). Then add Sigfile to impl-envi-1 to produce impl-envi. This step makes the signatureconstrained view of the implementation file available for use in later implementation files. The
processing of the implementation file has no effect on the signature environment, so sig-envi isidentical to sig-envi-1.

b.

If the implementation file has no signature file, add Implfile to both sig-envi-1 and impl-envi-1, to
produce sig-envi and impl-envi. This makes the contents of the implementation available for use
in both later signature and implementation files.

The signature file for a particular implementation must occur before the implementation file in the compilation
order. For every signature file, a corresponding implementation file must occur after the file in the compilation
order. Script files may not have signatures.

12.1 Implementation Files
Implementation files consist of one or more namespace declaration groups. For example:
namespace MyCompany.MyOtherLibrary
type MyType() =let x = 1
member v.P = x + 2
module MyInnerModule =let myValue = 1
namespace MyCompany. MyOtherLibrary.Collections
type MyCollection(x : int) =member v.P = x
An implementation file that begins with a module declaration defines a single namespace declaration group with
one module. For example:
module MyCompany.MyLibrary.MyModule
let x = 1
is equivalent to:namespace MyCompany.MyLibrary
module MyModule =let x = 1
The final identifier in the long-ident that follows the module keyword is interpreted as the module name, andthe preceding identifiers are interpreted as the namespace.
Anonymous implementation files do not have either a leading module or namespace declaration. Only the scripts
and the last file within an implementation group for an executable image (.exe) may be anonymous. An
anonymous implementation file contains module definitions that are implicitly placed in a module. The name of
the module is generated from the name of the source file by capitalizing the first letter and removing the filename
extensionIf the filename contains characters that are not valid in an F# identifier, the resulting module name isunusable and a warning occurs.

172Given an initial environment env0, an implementation file is checked as follows:

Create a new constraint solving context.

Check the namespace declaration groups in the file against the existing environment envi-1 andincrementally add them to the environment (§10.1) to create a new environment envi.

Apply default solutions to any remaining type inference variables that include default constraints. The
defaults are applied in the order that the type variables appear in the type-annotated text of the checked
namespace declaration groups.



Check the inferred signature of the implementation file against any required signature by using Signature
Conformance (§11.2). The resulting signature of an implementation file is the required signature, if it ispresent; otherwise it is the inferred signature.



Report a “value restriction” error if the resulting signature of any item that is not a member, constructor,
function, or type function contains any free inference type variables.

Choose solutions for any remaining type inference variables in the elaborated form of an expression.
Process any remaining type variables in the elaborated form from left-to-right to find a minimal type solutionthat is consistent with constraints on the type variable. If no unique minimal solution exists for a typevariable, report an error.

The result of checking an implementation file is a set of elaborated namespace declaration groups.

12.2 Signature Files
Signature files specify the functionality that is implemented by a corresponding implementation file. Eachsignature file contains a sequence of namespace-decl-group-signature elements. The inclusion of a signature
file in compilation implicitly applies that signature type to the contents of a corresponding implementation file.
Anonymous signature files do not have either a leading module or namespace declaration. Anonymous signature
files contain module-elems that are implicitly placed in a module. The name of the module is generated from thename of the source file by capitalizing the first letter and removing the filename extension. If the filename
contains characters that are not valid in an F# identifier, the resulting module name is unusable and a warning
occurs.
Given an initial environment env, a signature file is checked as follows:

Create a new constraint solving context.

Check each namespace-decl-group-signaturei in envi-1 and add the result to that environment to create
a new environment envi.

The result of checking a signature file is a set of elaborated namespace declaration group types.

12.3 Script Files
Script files have the.fsx or .fsscript filename extension. They are processed in the same way as files thathave the .fs extension, with the following exceptions:Side effects from all scripts are executed at program startup.



For script files, the namespace Microsoft.FSharp.Compiler.Interactive.Settings is opened bydefault.

173



F# Interactive references the assembly FSharp.Compiler.Interactive.Settings.dll by default, but theF# compiler does not. If the script uses the script helper fsi object, then the script should explicitly reference
FSharp.Compiler.Interactive.Settings.dll.

Script files may add to the set of referenced assemblies by using the #r directive (§12.312.4).
Script files may add other signature, implementation, and script files to the list of sources by using the #load
directive. Files are compiled in the same order that was passed to the compiler, except that each script issearched for #load directives and the loaded files are placed before the script, in the order they appear in thescript. If a filename appears in more than one #load directive, the file is placed in the list only once, at theposition it first appeared.
Script files may have #nowarn directives, which disable a warning for the entire compilation.
The F# compiler defines the COMPILED compilation symbol for input files that it has processed. F# Interactive
defines the INTERACTIVE symbol.
Script files may not have corresponding signature files.

12.4 Compiler Directives
Compiler directives are declarations in non-nested modules or namespace declaration groups in the followingform:# id string ... string
The lexical preprocessor directives #if, #else, #endif and #indent "off" are similar to compiler directives.
For details on #if, #else, #endif, see §3.3. The #indent "off" directive is described in §18.4.
The following directives are valid in all files:
Directive

ExampleShort Description#nowarn#nowarn "54"For signature (.fsi) files and implementation(.fs) files, turns off warnings within this lexicalscope.
For script (.fsx or .fsscript) files, turns off
warnings globally.

174

The following directives are valid in script files:
Directive

ExampleShort Description#r#r "System.Core"#reference#r @"Nunit.Core.dll"

References a DLL within this entire
script.

#r @"c:\NUnit\Nunit.Core.dll"#r "nunit.core, Version=2.2.2.0,
Culture=neutral,
PublicKeyToken=96d09a1eb7f44a77"#I#I @"c:\Projects\Libraries\Bin"

Adds a path to the search paths for DLLs
that are referenced within this entire
script.

#load "library.fs"

Loads a set of signature andimplementation files into the script
execution engine.

#Include#load#load "core.fsi" "core.fs"
#time

#time

Enables or disables the display of
performance information, including
elapsed real time, CPU time, andgarbage collection information for eachsection of code that is interpreted andexecuted.

#time "on"#time "off"

#help

#help

Asks the script execution environment for
help.

#q

#q

#quit

#quit

Requests the script execution
environment to halt execution and exit.

12.5 Program Execution
Execution of F# code occurs in the context of an executing CLI program into which one or more compiled F#
assemblies or script fragments is loaded. During execution, the CLI program can use the functions, values, static
members, and object constructors that the assemblies and script fragments define.

12.5.1 Execution of Static Initializers
Each implementation file, script file, and script fragment involves a static initializer. The execution of the static
initializer is triggered as follows:For executable (.exe) files that have an explicit entry point function, the static initializer for the last file thatappears on the command line is forced immediately as the first action in the execution of the entry point
function.

For executable files that have an implicit entry point, the static initializer for the last file that appears on thecommand line is the body of the implicit entry point function.

For scripts, F# Interactive executes the static initializer for each program fragment immediately.

175



For all other implementation files, the static initializer for the file is executed on first access of a value thathas observable initialization according to the rules that follow, or first access to any member of any type in
the file that has at least one “static let” or “static do” declaration.

At runtime, the static initializer evaluates, in order, the definitions in the file that have observable initializationaccording to the rules that follow. Definitions with observable initialization in nested modules and types areincluded in the static initializer for the overall file.
All definitions have observable initialization except for the following definitions in modules:
Function definitions

Type function definitions



Literal definitions



Value definitions that are generalized to have one or more type variables

Non-mutable, non-thread-local values that are bound to a simple constant expression, which is anexpression whose elaborated form is one of the following:

A constant expression.

A null expression.



A use of the typeof<_> or sizeof<_> operator from Microsoft.FSharp.Core.Operators, or thedefaultof<_> operator from Microsoft.FSharp.Core.Operators.Unchecked.

A let expression where the constituent expressions are simple constant expressions.



A match expression where the input is a simple constant expression, each case is a test against a
constant, and each target is a simple constant expression.



A use of one of the unary or binary operators =, <>, <, >, <=, >=, +, -, * , <<<, >>>, |||, &&&, ^^^, ~~~,
enum<_>, not, compare, prefix –, and prefix + from Microsoft.FSharp.Core.Operators on one or twoarguments, respectively, The arguments themselves must be simple constant expressions, but cannot
be operations on decimals or strings. Note that the operators are unchecked for arithmetic operations,and that the operators % and / are not included because their use can raise division-by-zero exceptions.



A use of a [<Literal>] value.



A use of a case from an enumeration type.



A use of a null case from a union type.



A use of a value that is defined in the same assembly and does not have observable initialization, or theuse of a value that is defined by a “let” or “match” expression within the expression itself.

If the execution environment supports the concurrent execution of multiple threads of F# code, each static
initializer runs as a mutual exclusion region. The use of a mutual exclusion region ensures that if another threadattempts to access a value that has observable initialization, that thread pauses until static initialization iscomplete. A static initializer runs only once, on the first thread that acquires entry to the mutual exclusion region.
Values that have observable initialization have implied CLI fields that are private to the assembly. If such a field isaccessed by using CLI reflection before the execution of the corresponding initialization code, then the default
value for the type of the field will be returned.
Within implementation files, generic types that have static value definitions receive a static initializer for eachgeneric instantiation. These initializers are executed immediately before the first dereference of the static fields
for the generic type, subject to any limitations present in the specific CLI implementation in used. If the static
initializer for the enclosing file is first triggered during execution of the static initializer for a generic instantiation,references to static values definition in the generic class evaluate to the default value.

176

For example, if external code accesses data in this example, the static initializer runs and the program prints“hello”:
module LibraryModule
printfn "hello"
let data = new Dictionary<int,int>()
That is, the side effect of printing “hello” is guaranteed to be triggered by an access to the value data.
If external code calls id or accesses size in the following example, the execution of the static initializer is not yet
triggered. However if external code calls f(),the execution of the static initializer is triggered because the bodyrefers to the value data, which has observable initialization.
module LibraryModule
printfn "hello"
let data = new Dictionary<int,int>()
let size = 3
let id x = x
let f() = data
All of the following represent definitions that do not have observable initialization because they are simple
constant expressions.
let x = System.DayOfWeek.Friday
let x = 1.0
let x = "two"let x = enum<System.DayOfWeek>(0)let x = 1 + 1
let x : int list = []let x : int option = None
let x = compare 1 1
let x = match true with true -> 1 | false -> 2
let x = true && true
let x = 42 >>> 2
let x = typeof<int>
let x = Unchecked.defaultof<int>
let x = Unchecked.defaultof<string>
let x = sizeof<int>

12.5.2 Explicit Entry Point
The last file that is specified in the compilation order for an executable file may contain an explicit entry point. The
entry point is indicated by annotating a function in a module with EntryPoint attribute:The EntryPoint attribute applies only to a “let”-bound function in a module. The function cannot be amember.

This attribute can apply to only one function, and the function must be the last declaration in the last fileprocessed on the command line. The function may be in a nested module.



The function is asserted to have type string[] -> int before type checking. If the assertion fails, an error
occurs.



At runtime, the entry point is passed one argument at startup: an array that contains the same entries as
System.Environment.GetCommandLineArgs(), minus the first entry in that array.

The function becomes the entry point to the program. At startup, F# immediately forces execution of the static
initializer for the file in which the function is declared, and then evaluates the body of the function.

177

Custom Attributes andReflection
CLI languages use metadata inspection and the System.Reflection libraries to make guarantees about how
compiled entities appear at runtime. They also allow entities to be attributed by static data, and these attributes
may be accessed and read by tools and running programs. This chapter describes these mechanisms for F#.
Attributes are given by the following grammar:
attribute := attribute-target:opt object-construction
attribute-set := [< attribute ; ... ; attribute >]attributes := attribute-set ... attribute-set
attribute-target :=assembly
modulereturnfield
property
param
type
constructor
event

13.1 Custom Attributes
CLI languages support the notion of custom attributes which can be added to most declarations. These areadded to the corresponding elaborated and compiled forms of the constructs to which they apply.
Custom attributes can be applied only to certain target language constructs according to the AttributeUsageattribute, which is found on the attribute class itself. An error occurs if an attribute is attached to a language
construct that does not allow that attribute.
Custom attributes are not permitted on function or value definitions in expressions or computation expressions.
Attributes on parameters are given as follows:
let foo([<SomeAttribute>] a) = a + 5
Custom attributes on return values are given as follows:
let foo a : [<SomeAttribute>] = a + 5
Custom attributes on primary constructors are given before the arguments and before any accessibility
annotation:
type Foo1 [<System.Obsolete("don't use me")>] () =member x.Bar() = 1
type Foo2 [<System.Obsolete("don't use me")>] private () =member x.Bar() = 1

Custom attributes are mapped to compiled CLI metadata as follows:Custom attributes map to the element that is specified by their target, if a target is given.



A custom attribute on a type type is compiled to a custom attribute on the corresponding CLI type definition,
whose System.Type object is returned by typeof<type>.



By default, a custom attribute on a record field F for a type T is compiled to a custom attribute on the CLI
property for the fieldthat is named F, unless the target of the attribute is field, in which case it becomes acustom attribute on the underlying backing field for the CLI property that is named _F.

A custom attribute on a union case ABC for a type T is compiled to a custom attribute on a static method on
the CLI type definition T. This method is called:

get_ABC if the union case takes no argumentsABC otherwiseCustom attributes on arguments are propagated only for arguments of member definitions, and not for “let”bound function definitions.



Custom attributes on generic parameters are not propagated.

Custom attributes that appear immediately preceding “do” statements in modules anywhere in an assembly areattached to one of the following:

The main entry point of the program.



The compiled module.



The compiled assembly.

Custom attributes are attached to the main entry point if it is valid for them to be attached to a method according
to the AttributeUsage attribute that is found on the attribute class itself, and likewise for the assembly. If it isvalid for the attribute to be attached to either the main method or the assembly. the main method takes
precedence.
For example, the STAThread attribute should be placed immediately before a top-level “do” statement.
let main() =let form = new System.Windows.Forms.Form()
System.Windows.Forms.Application.Run(form)[<STAThread>]do main()13.1.1 Custom Attributes and Signatures
During signature checking, custom attributes attached to items in F# signature files (.fsi files) are combinedwith custom attributes on the corresponding element from the implementation file according to the following
algorithm:
Start with lists AImpl and ASig containing the attributes in the implementation and signature, in declaration
order.



Check each attribute in AImpl against the available attributes in ASig.



If ASig contains an attribute that is an exact match after evaluating attribute arguments, then ignore theattribute in the implementation, remove the attribute from ASig, and continue checking;

If ASig contains an attribute that has the same attribute type but is not an exact match, then give a warningand ignore the attribute in the implementation;

Otherwise, keep the attribute in the implementation.

180

The compiled element contains the compiled forms of the attributes from the signature and the retained attributes
from the implementation.
This means:When an implementation has an attribute X("abc") and the signature is missing the attribute, then no
warning is given and the attribute appears in the compiled assembly.

When a signature has an attribute X("abc") and the implementation is missing the attribute, then no
warning is given, and the attribute appears in the compiled assembly.

When an implementation has an attribute X("abc") and the signature has attribute X("def"), then awarning is given, and only X("def") appears in the compiled assembly.

13.2 Reflected Forms of Declaration Elements
The typeof and typedefof F# library operators return a System.Type object for an F# type definition.
According to typical implementations of the CLI execution environment, the System.Type object in turn can beused to access further information about the compiled form of F# member declarations. If this operation is
supported in a particular implementation of F#, then the following rules describe which declaration elements havecorresponding System.Reflection objects:All member declarations are present as corresponding methods, properties or events.

Private and internal members and types are included.

Type abbreviations are not given corresponding System.Type definitions.

In addition:
F# modules are compiled to provide a corresponding compiled CLI type declaration and System.Type
object, although the System.Type object is not accessible by using the typeof operator.

However:Internal and private function and value definitions are not guaranteed to be given corresponding compiled
CLI metadata definitions. They may be removed by optimization.

Additional internal and private compiled type and member definitions may be present in the compiled CLI
assembly as necessary for the correct implementation of F# programs.



The System.Reflection operations return results that are consistent with the erasure of F# typeabbreviations and F# unit-of-measure annotations.



The definition of new units of measure results in corresponding compiled CLI type declarations with anassociated System.Type.

181

Inference Procedures
14.1 Name Resolution
14.1.1 Name Environments
Each point in the interpretation of an F# program is subject to an environment. The environment encompasses:
All referenced external DLLs (assemblies).

ModulesAndNamespaces: a table that maps long-idents to a list of signatures. Each signature is either anamespace declaration group signature or a module signature.
For example, System.Collections may map to one namespace declaration group signature for eachreferenced assembly that contributes to the System.Collections namespace, and to a module signature, if
a module called System.Collections is declared or in a referenced assembly.
If the program references multiple assemblies, the assemblies are added to the name resolution
environment in the order in which the references appear on the command line. The order is important only if
ambiguities occur in referencing the contents of assemblies—for example, if two assemblies define the type
MyNamespace.C.



ExprItems: a table that maps names to the following items:

A valueA union case for use when constructing dataAn active pattern result tag for use when returning results from active patterns

A type name for each class or struct type

FieldLabels: a table that maps names to sets of field references for record types



PatItems: a table that maps names to the following items:

A union case, for use when pattern matching on dataAn active pattern case name, for use when specifying active patterns



A literal definition

Types: a table that maps names to type definitions. Two queries are supported on this table:

Find a type by name alone. This query may return multiple types. For example, in the default typechecking environment, the resolution of System.Tuple returns multiple tuple types.



Find a type by name and generic arity n. This query returns at most one type. For example, in thedefault type-checking environment, the resolution of System.Tuple with n = 2 returns a single type.



ExtensionsInScope: a table that maps type names to one or more member definitions

The dot notation is resolved during type checking by consulting these tables.

14.1.2 Name Resolution in Module and Namespace Paths
Given an input long-ident and environment env, Name Resolution in Module and Namespace Paths computes
the result of interpreting long-ident as a module or namespace. The procedure returns a list of modules andnamespace declaration groups.

Name Resolution in Module and Namespace Paths proceeds through the following steps:
1.

Consult the ModulesAndNamespaces table to resolve the long-ident prefix to a list of modules andnamespace declaration group signatures.

2.

If any identifiers remain unresolved, recursively consult the declared modules and sub-modules of thesenamespace declaration groups.

3.

Concatenate all the results.

If the long-ident starts with the special pseudo-identifier keyword global, the identifier is resolved byconsulting the ModulesAndNamespaces table and ignoring all open directives, including those implied by
AutoOpen attributes.
For example, if the environment contains two referenced DLLs, and each DLL has namespace declarationgroups for the namespaces System, System.Collections, and System.Collections.Generic, Name
Resolution in Module and Namespace Paths for System.Collections returns the two namespace declarationgroups named System.Collections, one from each assembly.

14.1.3 Opening Modules and Namespace Declaration Groups
When a module or namespace declaration group F is opened, the compiler adds items to the name environment
as follows:1.

Add each exception label for each exception type definition (§8.11) in F to the ExprItems and PatItems tablesin the original order of declaration in F.

2.

Add each type definition in the original order of declaration in F. Adding a type definition involves thefollowing procedure:a.

If the type is a class or struct type (or an abbreviation of such a type), add the type name to the
ExprItems table.

b.

If the type definition is a record, add the record field labels to the FieldLabels table, unless the type has
the RequireQualifiedAccess attribute.

c.

If the type is a union, add the union cases to the ExprItems and PatItems tables, unless the type has theRequireQualifiedAccess attribute.

d.

Add the type to the TypeNames table. If the type has a CLI-encoded generic name such as List`1, add
an entry under both List and List`1.

3.

Add each value in the original order of declaration in F, as follows:a.

Add the value to the ExprItems table.

b.

If any value is an active pattern, add the tags of that active pattern to the PatItems table according to theoriginal order of declaration.

c.
4.

If the value is a literal, add it to the PatItems table.

Add the member contents of each type extension in Fi to the ExtensionsInScope table according to theoriginal order of declaration in Fi.

5.

Add each sub-module or sub-namespace declaration group in Fi to the ModulesAndNamespaces tableaccording to the original order of declaration in Fi.

6.

Open any sub-modules that are marked with the Microsoft.FSharp.Core.AutoOpen attribute.

18414.1.4 Name Resolution in Expressions
Given an input long-ident, environment env, and an optional count n of the number of subsequent typearguments <_,...,_>, Name Resolution in Expressions computes a result that contains the interpretation of thelong-ident<_,...,_> prefix as a value or other expression item, and a residue path rest.
How Name Resolution in Expressions proceeds depends on whether long-ident is a single identifier or iscomposed of more than one identifier.
If long-ident is a single identifier ident:
1.

Look up ident in the ExprItems table. Return the result and empty rest.

2.

If ident does not appear in the ExprItems table, look it up in the Types table, with generic arity that matches
n if available. Return this type and empty rest.

3.

If ident does not appear in either the ExprItems table or the Types table, fail.

If long-ident is composed of more than one identifier ident.rest, Name Resolution in Expressions proceeds
as follows:1.

If ident exists as a value in the ExprItems table, return the result, with rest as the residue.

2.

If ident does not exist as a value in the ExprItems table, perform a backtracking search as follows:a.

Consider each division of long-ident into [namespace-or-module-path].ident[.rest], in which thenamespace-or-module-path becomes successively longer.

b.

For each such division, consider each module signature or namespace declaration group signature F in
the list that is produced by resolving namespace-or-module-path by using Name Resolution in Moduleand Namespace Paths.

c.

For each such F, attempt to resolve ident[.rest] in the following order. If any resolution succeeds, thenterminate the search:
1)

A value in F. Return this item and rest.

2)

A union case in F. Return this item and rest.

3)

An exception constructor in F. Return this item and rest.

4)

A type in F. If rest is empty, then return this type; if not, resolve using Name Resolution for
Members.

5)
3.

A [sub-]module in F. Recursively resolve rest against the contents of this module.

If steps 1 and 2 do not resolve long-ident, look up ident in the Types table.
a.

If the generic arity n is available, then look for a type that matches both ident and n.

b.

If no generic arity n is available, and rest is not empty:
1)

If the Types table contains a type ident that does not have generic arguments, resolve to this
type.

2)

If the Types table contains a unique type ident that has generic arguments, resolve to thistype. However, if the overall result of the Name Resolution in Expressions operation is amember, and the generic arguments do not appear in either the return or argument types of theitem, warn that the generic arguments cannot be inferred from the type of the item.

3)
c.
4.

If neither of the preceding steps resolves the type, give an error.

If rest is empty, return the type, otherwise resolve using Name Resolution for Members.

If steps 1-3 do not resolve long-ident, look up ident in the ExprItems table and return the result andresidue rest.

185

5.

Otherwise, if ident is a symbolic operator name, resolve to an item that indicates an implicitly resolved
symbolic operator.

6.

Otherwise, fail.

If the expression contains ambiguities, Name Resolution in Expressions returns the first result that the process
generates.
For example, consider the following cases:
module M =type C =| C of string| D of string
member x.Prop1 = 3
type Data =| C of string| E
member x.Prop1 = 3
member x.Prop2 = 3
let C = 5open M
let C = 4
let D = 6
let test1 = C// resolves to the value C
let test2 = C.ToString() // resolves to the value C with residue ToString
let test3 = M.C// resolves to the value M.C
let test4 = M.Data.C// resolves to the union case M.Data.C
let test5 = M.C.C// error: first part resolves to the value M.C,//
and this contains no field or property "C"
let test6 = C.Prop1// error: the value C does not have a property Prop
let test7 = M.E.Prop2// resolves to M.E, and then a property lookup
The following example shows the resolution behavior for type lookups that are ambiguous by generic arity:
module M =type C<'T>() =static member P = 1
type C<'T,'U>() =static member P = 1
let _ = new M.C()
let _ = new M.C<int>()
let _ = M.C()
let _ = M.C<int>()
let _ = M.C<int,int>()
let _ = M.C<_>()
let _ = M.C<_,_>()
let _ = M.C.P
let _ = M.C<_>.P
let _ = M.C<_,_>.P// gives an error// no error, resolves to C<'T>// gives an error// no error, resolves to C<'T>// no error, resolves to C<'T,'U>// no error, resolves to C<'T>// no error, resolves to C<'T,'U>// gives an error// no error, resolves to C<'T>// no error, resolves to C<'T,'U>

186

The following example shows how the resolution behavior differs slightly if one of the types has no genericarguments.
module M =type C() =static member P = 1
type C<'T>() =static member P = 1
let _ = new M.C()
let _ = new M.C<int>()
let _ = M.C()
let _ = M.C< >()let _ = M.C<int>()
let _ = M.C< >()let _ = M.C<_>()
let _ = M.C.P
let _ = M.C< >.P
let _ = M.C<_>.P// no error, resolves to C// no error, resolves to C<'T>// no error, resolves to C// no error, resolves to C// no error, resolves to C<'T>// no error, resolves to C// no error, resolves to C<'T>// no error, resolves to C// no error, resolves to C// no error, resolves to C<'T>

In the following example, the procedure issues a warning for an incomplete type. In this case, the type parameter'T cannot be inferred from the use M.C.P, because 'T does not appear at all in the type of the resolved element
M.C<'T>.P.
module M =type C<'T>() =static member P = 1
let _ = M.C.P// no error, resolves to C<'T>.P, warning given

The effect of these rules is to prefer value names over module names for single identifiers. For example, consider
this case:let Foo = 1
module Foo =let ABC = 2
let x1 = Foo // evaluates to 1
The rules, however, prefer type names over value names for single identifiers, because type names appear in the
ExprItems table. For example, consider this case:let Foo = 1
type Foo() =static member ABC = 2
let x1 = Foo.ABC // evaluates to 2
let x2 = Foo() // evaluates to a new Foo()

14.1.5 Name Resolution for Members
Name Resolution for Members is a sub-procedure used to resolve .member-ident[.rest] to a member, in thecontext of a particular type type.
Name Resolution for Members proceeds through the following steps:
1.

Search the hierarchy of the type from System.Object to type.

2.

At each type, try to resolve member-ident to one of the following, in order:a.

A union case of type.

b.

A property group of type.
187

c.

A method group of type.

d.

A field of type.

e.

An event of type.

f.

A property group of extension members of type, by consulting the ExtensionsInScope table.

g.

A method group of extension members of type, by consulting the ExtensionsInScope table.

h.

A nested type type-nested of type. Recursively resolve .rest if it is present, otherwise return typenested.

3.

At any type, the existence of a property, event, field, or union case named member-ident causes any
methods or other entities of that same name from base types to be hidden.

4.

Combine method groups with method groups from base types. For example:
type A() =member this.Foo(i : int) = 0
type B() =inherit A()
member this.Foo(s : string) = 1
let b = new B()
b.Foo(1)// resolves to method in A
b.Foo("abc") // resolves to method in B

14.1.6 Name Resolution in Patterns
Name Resolution for Patterns is used to resolve long-ident in the context of pattern expressions. The longident must resolve to a union case, exception label, literal value, or active pattern case name. If it does not, thelong-ident may represent a new variable definition in the pattern.
Name Resolution for Patterns follows the same steps to resolve the member-ident as Name Resolution in
Expressions (§14.1.4) except that it consults the PatItems table instead of the ExprItems table. As a result,values are not present in the namespace that is used to resolve identifiers in patterns. For example:
let C = 3
match 4 with
| C -> sprintf "matched, C = %d" C| _ -> sprintf "no match, C = %d" C
results in "matched, C = 4", because C is not present in the PatItems table, and hence becomes a value
pattern. In contrast,[<Literal>]let C = 3
match 4 with
| C -> sprintf "matched, C = %d" C| _ -> sprintf "no match, C = %d" C
results in "no match, C = 3", because C is a literal and therefore is present in the PatItems table.

14.1.7 Name Resolution for Types
Name Resolution for Types is used to resolve long-ident in the context of a syntactic type. A generic arity thatmatches n is always available. The result is a type definition and a possible residue rest.
Name Resolution for Types proceeds through the following steps:

188

1.

Given ident[.rest], look up ident in the Types table, with generic arity n. Return the result and residue
rest.

2.

If ident is not present in the Types table:a.

Divide long-ident into [namespace-or-module-path].ident[.rest], in which the namespace-ormodule-path becomes successively longer.

b.

For each such division, consider each module and namespace declaration group F in the list that resultsfrom resolving namespace-or-module-path by using Name Resolution in Module and Namespace
Paths (§14.1.2).

c.

For each such F, attempt to resolve ident[.rest] in the following order. Terminate the search when theexpression is successfully resolved.
1)

A type in F. Return this type and residue rest.

2)

A [sub-]module in F. Recursively resolve rest against the contents of this module.

In the following example, the name C on the last line resolves to the named type M.C<_,_> because C is applied
to two type arguments:
module M =type C<'T, 'U> = 'T * 'T * 'U
module N =type C<'T> = 'T * 'T
open M
open N
let x : C<int, string> = (1, 1, "abc")

14.1.8 Name Resolution for Type Variables
Whenever the F# compiler processes syntactic types and expressions, it assumes a context that maps identifiers
to inference type variables. This mapping ensures that multiple uses of the same type variable name map to thesame type inference variable. For example, consider the following function:
let f x y = (x:'T), (y:'T)
In this case, the compiler assigns the identifiers x and y the same static type—that is, the same type inference
variable is associated with the name 'T. The full inferred type of the function is:val f<'T> : 'T -> 'T -> 'T * 'T
The map is used throughout the processing of expressions and types in a left-to-right order. It is initially empty forany member or any other top-level construct that contains expressions and types. Entries are eliminated from the
map after they are generalized. As a result, the following code checks correctly:
let f () =let g1 (x:'T) = x
let g2 (y:'T) = (y:string)
g1 3, g1 "3", g2 "4"The compiler generalizes g1, which is applied to both integer and string types. The type variable 'T in (y:'T) on
the third line refers to a different type inference variable, which is eventually constrained to be type string.

14.1.9 Field Label Resolution
Field Label Resolution specifies how to resolve identifiers such as field1 in { field1 = expr; ... fieldN =expr }.

189

Field Label Resolution proceeds through the following steps:
1.

Look up all fields in all available types in the Types table.

2.

Return the set of field declarations.

14.2 Resolving Application Expressions
Application expressions that use dot notation—such as x.Y<int>.Z(g).H.I.j—are resolved according to a setof rules that take into account the many possible shapes and forms of these expressions and the ambiguities that
may occur during their resolution. This section specifies the exact algorithmic process that is used to resolvethese expressions.
Resolution of application expressions proceeds as follows:1.

Repeatedly decompose the application expression into a leading expression expr and a list of projections
projs. Each projection has the following form:.long-ident-or-op is a dot lookup projection.



expr



<types> is a type application projection.

is an application projection.

For example:


x.y.Z(g).H.I.j decomposes into x.y.Z and projections (g), .H.I.j.



x.M<int>(g) decomposes into x.M and projections <int>, (g).

f x decomposes into f and projection x.
Note: In this specification we write sequences of projections by juxtaposition; for example,(expr).long-ident<types>(expr). We also write (.rest + projs) to refer to adding a residue
long identifier to the front of a list of projections, which results in projs if rest is empty and .rest
projs otherwise.

2.

After decomposition:If expr is a long identifier expression long-ident, apply Unqualified Lookup (§14.2.1) on long-ident
with projections projs.



If expr is not such an expression, check the expression against an arbitrary initial type ty, to generate
an elaborated expression expr. Then process expr, ty, and projs by using Expression-Qualified
Lookup (§14.2.3)

14.2.1 Unqualified Lookup
Given an input long-ident and projections projs, Unqualified Lookup computes the result of “looking up” longident.projs in an environment env. The first part of this process resolves a prefix of the information in longident.projs, and recursive resolutions typically use Expression-Qualified Resolution to resolve the remainder.
For example, Unqualified Lookup is used to resolve the vast majority of identifier references in F# code, from
simple identifiers such as sin, to complex accesses such as
System.Environment.GetCommandLineArgs().Length.
Unqualified Lookup proceeds through the following steps:
1.

Resolve long-ident by using Name Resolution in Expressions (§14.1). This returns a name resolution item
item and a residue long identifier rest.

190

For example, the result of Name Resolution in Expressions for v.X.Y may be a value reference v along witha residue long identifier .X.Y. Likewise, N.X(args).Y may resolve to an overloaded method N.X and aresidue long identifier.Y.
Name Resolution in Expressions also takes as input the presence and count of subsequent type argumentsin the first projection. If the first projection in projs is <tyargs>, Unqualified Lookup invokes Name
Resolution in Expressions with a known number of type arguments. Otherwise, it is invoked with an unknown
number of type arguments.
2.

Apply Item-Qualified Lookup for item and (rest + projs).

14.2.2 Item-Qualified Lookup
Given an input item item and projections projs, Item-Qualified Lookup computes the projection item.projs.
This computation is often a recursive process: the first resolution uses a prefix of the information in item.projs,
and recursive resolutions resolve any remaining projections.
Item-Qualified Lookup proceeds as follows:
1. If item is not one of the following, return an error:2.



A named value

A union case

A group of named types



A group of methodsA group of indexer getter properties

A single non-indexer getter property

A static F# fieldA static CLI fieldAn implicitly resolved symbolic operator name

If the first projection is <types>, then we say the resolution has a type application <types> with remaining
projections.

3.

Otherwise, checking proceeds as shown in the table.

If item is:

ActionA value reference v

1. Instantiate the type scheme of v, which results in a type ty. Apply these rules: If the first projection is <types>, process the types and use the results as thearguments to instantiate the type scheme.
 If the first projection is not <types>, the type scheme is freshly instantiated.
 If the value has the RequiresExplicitTypeArguments attribute, the first projection
must be <types>.
 If the value has type byref<ty2>, add a byref dereference to the elaborated
expression.
 Insert implicit flexibility for the use of the value (§14.4.2).

A type name, where
projs begins with
<types>.long-ident

2. Apply Expression-Qualified Lookup for type ty and any remaining projections.
1. Process the types and use the results as the arguments to instantiate the named typereference, thus generating a type ty.
2. Apply Name Resolution for Members to ty and long-ident, which generates a new
item.
3. Apply Item-Qualified Lookup to the new item and any remaining projections.

191

If item is:

ActionA group of type
names where projs

1. Process the types and use the results as the arguments to instantiate the named typereference, thus generating a type ty.
2. Process the object construction ty(expr) as an object constructor call in the same
way as new ty(expr).
3. Apply Expression-Qualified Lookup to item and any remaining projections.

begins with <types>
or exprA group of methodreferences

A group of property
indexer references

A static field reference

A union case tag,
exception tag, or
active pattern result
element tag
A CLI event reference

An implicitly resolved
symbolic operator
name op

1. Apply Method Application Resolution for the method group. Method Application
Resolution accepts an optional set of type arguments and a syntactic expression
argument. Determine the arguments based on what projs begins with: <types> expr, then use <types> as the type arguments and expr as theexpression argument.
 expr, then use expr as the expression argument.
 anything else, use no expression argument or type arguments.
2. If the result of Method Application Resolution is labeled with theRequiresExplicitTypeArguments attribute, then explicit type arguments are
required.
3. Let fty be the actual return type that results from Method Application Resolution.
Apply Expression-Qualified Lookup to fty and any remaining projections.
1. Apply Method Application Resolution, and use the underlying getter indexer methods
for the method group.
2. Determine the arguments to Method Application Resolution as described for a groupof methods.
1. Check the field for accessibility and attributes.
2. Let fty be the actual type of the field, taking into account the type ty via which thefield was accessed in the case where this is a field in a generic type.
3. Apply Expression-Qualified Lookup to fty and projs.
1. Check the tag for accessibility and attributes.
2. If projs begins with expr, use expr as the expression argument.
3. Otherwise, use no expression argument or type arguments. In this case, build afunction expression for the union case.
4. Let fty be the actual type of the union case.
5. Apply Expression-Qualified Lookup to fty and remaining projs.
1. Check the event for accessibility and attributes.
2. Let fty be the actual type of the event.
3. Apply Expression-Qualified Lookup to fty and projs.
1. If op is a unary, binary or the ternary operator ?<-, resolve to the following
expressions, respectively:(fun (x:^a) -> (^a : static member (op) : ^a -> ^b) x)(fun (x:^a) (y:^b) ->((^a or ^b) : static member (op) : ^a * ^b -> ^c) (x,y))(fun (x:^a) (y:^b) (z:^c) ->((^a or ^b or ^c) : static member (op) : ^a * ^b * ^c -> ^d) (x,y,z))

2. The resulting expressions are static member constraint invocation expressions(§6.4.8), which enable the default interpretation of operators by using type-directed
member resolution.
3. Recheck the entire expression with additional subsequent projections .projs.

14.2.3 Expression-Qualified Lookup
Given an elaborated expression expr of type ty, and projections projs, Expression-Qualified Lookup computes
the “lookups or applications” for expr.projs.

192

Expression-Qualified Lookup proceeds through the following steps:
1.

Inspect projs and process according to the following table.

projs

ActionComments

Empty

Assert that the type of the overall,original application expression is ty.

Checking is complete.

Starts with(expr2)

Apply Function Application Resolution

Checking is complete when Function
Application Resolution returns.

(§14.3).
Starts with<types>

Fail.

Type instantiations may not be applied
to arbitrary expressions; they can apply
only to generic types, generic methods,
and generic values.

Starts with .long-ident

Resolve long-ident using Name

For example, for ty = string and

Resolution for Members (§14.1.4).

long-ident = Length , Name

Return a name resolution item item andResolution for Members returns aa residue long identifier rest. Continue

property reference to the CLI instanceproperty System.String.Length.

processing at step 2.
2.

3.

If Step 1 returned an item and rest, report an error if item is not one of the following:A group of methods.



A group of instance getter property indexers.

A single instance, non-indexer getter property.



A single instance F# field.



A single instance CLI field.

Proceed based on item as shown in the table:

If item is:

ActionGroup of methods

1. Apply Method Application Resolution for the method group. Method
Application Resolution accepts an optional set of type arguments and asyntactic expression argument. If projs begins with: <types>(arg), then use <types> as the type arguments and arg as
the expression argument.
 (arg), then use arg as the expression argument.
 otherwise, use no expression argument or type arguments.

Group of indexer properties

Non-indexer getter property

2. Let fty be the actual return type resulting from Method Application
Resolution. Apply Expression-Qualified Lookup to fty and any
remaining projections.
1. Apply Method Application Resolution and use the underlying getter
indexer methods for the method group.
2. Determine the arguments to Method Application Resolution as describedfor a group of methods.
Apply Method Application Resolution for the method group that contains
only the getter method for the property, with no type arguments and one ()argument.

193

If item is:

ActionInstance intermediate language (IL)or F# field F

1. Check the field for accessibility and attributes.
2. Let fty be the actual type of the field (taking into account the type ty bywhich the field was accessed).
3. Assert that ty is a subtype of the actual containing type of the field.
4. Produce an elaborated form for expr.F. If F is a field in a value type thentake the address of expr by using the AddressOf (expr , NeverMutates)
operation §6.9.4.
5. Apply Expression-Qualified Lookup to fty and projs.

14.3 Function Application Resolution
Given expressions f and expr where f has type ty, and given subsequent projections projs, Function
Application Resolution does the following:
1.

Asserts that f has type ty1 -> ty2 for new inference variables ty1 and ty2.

2.

If the assertion succeeds:

3.

a.

Check expr with the initial type ty1.

b.

Process projs using Expression-Qualified against ty2.

If the assertion fails, and expr has the form { computation-expr }:a.

Check the expression as the computation expression form f { computation-expr }, giving resulttype ty1.

b.

Process projs using Expression-Qualified Lookup against ty1.

14.4 Method Application Resolution
Given a method group M, optional type arguments <ActualTypeArgs>, an optional syntactic argument obj, an
optional syntactic argument arg, and overall initial type ty, Method Application Resolution resolves theoverloading based on the partial type information that is available. It also:

Resolves optional and named arguments.



Resolves “out” arguments.



Resolves post-hoc property assignments.

Applies method application resolution.

Inserts ad hoc conversions that are only applied for method calls.

If no syntactic argument is supplied, Method Application Resolution tries to resolve the use of the method as afirst class value, such asthe method call in the following example:
List.map System.Environment.GetEnvironmentVariable ["PATH"; "USERNAME"]
Method Application Resolution proceeds through the following steps:
1.

Restrict the candidate method group M to those methods that are accessible from the point of resolution.

2.

If an argument arg is present, determine the sets of unnamed and named actual arguments,
UnnamedActualArgs and NamedActualArgs:a.

Decompose arg into a list of arguments:

194

b.



If arg is a syntactic tuple arg1 ,..., argN, use these arguments.



If arg is a syntactic unit value (),use a zero-length list of arguments.

For each argument:

If arg is a binary expression of the form name=expr, it is a named actual argument.



Otherwise, arg is an unnamed actual argument.

If there are no named actual arguments, and M has only one candidate method, which accepts only one
required argument, ignore the decomposition of arg to tuple form. Instead,arg itself is the only named actualargument.
All named arguments must appear after all unnamed arguments.
Examples:x.M(1, 2) has two unnamed actual arguments.
x.M(1, y = 2) has one unnamed actual argument and one named actual argument.
x.M(1, (y = 2)) has two unnamed actual arguments.
x.M( printfn "hello"; ()) has one unnamed actual argument.
x.M((a, b)) has one unnamed actual argument.
x.M(()) has one unnamed actual argument.
3.

Determine the named and unnamed prospective actual argument types, called ActualArgTypes.


If an argument arg is present, the prospective actual argument types are fresh type inference variables
for each unnamed and named actual argument.


If the argument has the syntactic form of an address-of expression &expr after ignoring
parentheses around the argument, equate this type with a type byref<ty> for a fresh type ty.



If the argument has the syntactic form of a function expression fun pat1 … patn -> expr after
ignoring parentheses around the argument, equate this type with a type ty1 -> … tyn -> rty for
fresh types ty1 and ty2.



If no argument arg is present:a.

If the method group contains a single method, the prospective unnamed argument types are one
fresh type inference variable for each required, non-“out” parameter that the method accepts.

b.

If the method group contains more than one method, the expected overall type of the expression isasserted to be a function type dty -> rty.


If dty is a tuple type (dty1 * .. * dtyN), the prospective argument types are (dty1, ..
,dtyN).

c.



If dty is unit, then the prospective argument types are an empty list.

If dty is any other type, the prospective argument types are dty alone.

Subsequently:

The method application is considered to have one unnamed actual argument for eachprospective unnamed actual argument type.


4.

The method application is considered to have no named actual arguments.

For each candidate method in M, attempt to produce zero, one, or two prospective method calls Mpossible asfollows:

195

a.

If the candidate method is generic and has been generalized, generate fresh type inference variables for
its generic parameters. This results in the FormalTypeArgs for Mpossible.

b.

Determine the named and unnamed formal parameters, called NamedFormalArgs andUnnamedFormalArgs respectively, by splitting the formal parameters for M into parameters that have amatching argument in NamedActualArgs and parameters that do not.

c.

If the number of UnnamedFormalArgs exceeds the number of UnnamedActualArgs, then modify
UnnamedFormalArgs as follows:

Determine the suffix of UnnamedFormalArgs beyond the number of UnnamedActualArgs.



If all formal parameters in the suffix are “out” arguments with byref type, remove the suffix from
UnnamedFormalArgs and call it ImplicitlyReturnedFormalArgs.



If all formal parameters in the suffix are optional arguments, remove the suffix from
UnnamedFormalArgs and call it ImplicitlySuppliedFormalArgs.

d.

If the last element of UnnamedFormalArgs has the ParamArray attribute and type pty[] for some pty,
then modify UnnamedActualArgs as follows:

If the number of UnnamedActualArgs exceeds the number of UnnamedFormalArgs-1, produce aprospective method call named ParamArrayActualArgs that has the excess ofUnnamedActualArgs removed.



If the number of UnnamedActualArgs equals the number of UnnamedFormalArgs-1, produce two
prospective method calls:One has an empty ParamArrayActualArgs.



One has no ParamArrayActualArgs.

If ParamArrayActualArgs has been produced, then Mpossible is said to use ParamArray conversion
with type pty.

e.

Associate each name=arg in NamedActualArgs with a target. A target is a named formal parameter, asettable return property, or a settable return field as follows:

If one of the arguments in NamedFormalArgs has name name, that argument is the target.



If the return type of M, before the application of any type arguments ActualTypeArgs, contains asettable property name,then M is the target.



If the return type of M, before the application of any type arguments ActualTypeArgs, contains asettable field name, then M is the target.

f.

No prospective method call is generated if any of the following are true:A named argument cannot be associated with a target.



The number of UnnamedActualArgs is less than the number of UnnamedFormalArgs after steps 4a-e.

The number of ActualTypeArgs, if any actual type arguments are present, does not precisely
equal the number of FormalTypeArgs for M.



The candidate method is static and the optional syntactic argument obj is present, or the candidate
method is an instance method and obj is not present.

5.

Attempt to apply initial types before argument checking. If only one prospective method call Mpossible exists,
assert Mpossible by performing the following steps:a.

Verify that each ActualTypeArgi is equal to its corresponding FormalTypeArgi.

196

b.

Verify that the type of obj is a subtype of the containing type of the method M.

c.

For each UnnamedActualArgi and UnnamedFormalArgi, verify that the corresponding ActualArgType
coerces to the type of the corresponding argument of M.

d.

If Mpossible uses ParamArray conversion with type pty, then for each ParamArrayActualArgi, verify thatthe corresponding ActualArgType coerces to pty.

e.

For each NamedActualArgi that has an associated formal parameter target, verify that thecorresponding ActualArgType coerces to the type of the corresponding argument of M.

f.

For each NamedActualArgi that has an associated property or field setter target, verify that thecorresponding ActualArgType coerces to the type of the property or field.

g.

Verify that the prospective formal return type coerces to the expected actual return type. If the method M
has return type rty, the formal return type is defined as follows:

If the prospective method call contains ImplicitlyReturnedFormalArgs with type ty1, ..., tyN, theformal return type is rty * ty1 * ... * tyN. If rty is unit then the formal return type is ty1 *... * tyN.

6.

Otherwise the formal return type is rty.

Check and elaborate argument expressions. If arg is present:

Check and elaborate each unnamed actual argument expression argi. Use the corresponding type inActualArgTypes as the initial type.



Check and elaborate each named actual argument expression argi. Use the corresponding type inActualArgTypes as the initial type.

7.

Choose a unique Mpossible according to the following rules:For each Mpossible, determine whether the method is applicable by attempting to assert Mpossible as
described in step 4a). If the actions in step 4a detect an inconsistent constraint set (§14.5), the method
is not applicable. Regardless, the overall constraint set is left unchanged as a result of determining theapplicability of each Mpossible.



If a unique applicable Mpossible exists, choose that method. Otherwise, choose the unique best Mpossible
by applying the following criteria, in order:
1)

Prefer candidates whose use does not constrain the use of a user-introduced generic type
annotation to be equal to another type.

2)

Prefer candidates that do not use ParamArray conversion. If two candidates both use ParamArrayconversion with types pty1 and pty2, and pty1 feasibly subsumes pty2, prefer the second; that is,use the candidate that has the more precise type.

3)

Prefer candidates that do not have ImplicitlyReturnedFormalArgs.

4)

Prefer candidates that do not have ImplicitlySuppliedFormalArgs.

5)

If two candidates have unnamed actual argument types ty11 ... ty1n and ty21 ... ty2n, and each ty1i
eithera.

feasibly subsumes ty2i, orb.

ty2i is a System.Func type and ty1i is some other delegate type,then prefer the second candidate. That is, prefer any candidate that has the more specific actualargument types, and consider any System.Func type to be more specific than any other delegate
type.
6)

Prefer candidates that are not extension members over candidates that are.

197

7)

To choose between two extension members, prefer the one that results from the most recent use of
open.

8)

Prefer candidates that are not generic over candidates that are generic—that is, prefer candidates
that have empty ActualArgTypes.

Report an error if steps 1) through 8) do not result in the selection of a unique better method.
8.

Once a unique best Mpossible is chosen, commit that method.

9.

Apply attribute checks.

10. Build the resulting elaborated expression by following these steps:a.

If the type of obj is a variable type or a value type, take the address of obj by using the AddressOf(obj,
PossiblyMutates) operation (§6.9.4).

b.

Build the argument list by:Passing each argument corresponding to an UnamedFormalArgs where the argument is an optional
argument as a Some value.

c.

Passing a None value for each argument that corresponds to an ImplicitlySuppliedFormalArgs.



Applying coercion to arguments.

Bind ImplicitlyReturnedFormalArgs arguments by introducing mutable temporaries for eachargument, passing them as byref parameters, and building a tuple from these mutable temporaries andany method return value as the overall result.

d.

For each NamedActualArgs whose target is a settable property or field, assign the value into theproperty.

e.

If arg is not present, return a function expression that represents a first class function value.

Two additional rules apply when checking arguments (see §8.13.6 for examples):

If a formal parameter has delegate type D, an actual argument farg has known typety1 -> ... -> tyn -> rty, and the number of arguments of the Invoke method of delegate type D is
precisely n, interpret the formal parameter in the same way as the following:
new D(fun arg1 ... argn -> farg arg1 ... argn).
For more information on the conversions that are automatically applied to arguments, see §8.13.6.



If a formal parameter is an “out” parameter of type byref<ty>, and an actual argument type is not a byref
type, interpret the actual parameter in the same way as type ref<ty>. That is, an F# reference cell can bepassed where a byref<ty> is expected.

One effect of these additional rules is that a method that is used as a first class function value can resolve even if
a method is overloaded and no further information is available. For example:
let r = new Random()
let roll = r.Next;;
Method Application Resolution results in the following, despite the fact that in the standard CLI library,
System.Random.Next is overloaded:
val roll : int -> int
The reason is that if the initial type contains no information about the expected number of arguments, the F#compiler assumes that the method has one argument.

198

14.4.1 Conditional Compilation of Member Calls
If a member definition has the System.Diagnostics.Conditional attribute, then any application of the member
is adjusted as follows:

The Conditional("symbol") attribute may apply to methods only.

Methods that have the Conditional attribute must have return type unit. The return type may be checked
either on use of the method or definition of the method.



If symbol is not in the current set of conditional compilation symbols, the compiler eliminates application
expressions that resolve to calls to members that have the Conditional attribute and ensures thatarguments are not evaluated. Elimination of such expressions proceeds first with static members and thenwith instance members, as follows:

Static members: Type.M(args)  ()

Instance members: expr.M(args)  ()14.4.2 Implicit Insertion of Flexibility for Uses of Functions and Members
At each use of a data constructor, named function, or member that forms an expression, flexibility is implicitly
added to the expression. This flexibility is associated with the use of the function or member, according to theinferred type of the expression. The added flexibility allows the item to accept arguments that are statically known
to be subtypes of argument types to a function without requiring explicit upcasts
The flexibility is added by adjusting each expression expr which represents a use of a function or member asfollows:


The type of the function or member is decomposed to the following form:ty11 * ... * ty1n -> ... -> tym1 * ... * tymn -> rty

If the type does not decompose to this form, no flexibility is added.

The positions tyij are called the “parameter positions” for the type. For each parameter position where tyij
is not a sealed type, and is not a variable type, the type is replaced by a fresh type variable ty'ij with acoercion constraint ty'ij :> tyij.

After the addition of flexibility, the expression elaborates to an expression of typety'11 * ... * ty'1n -> ... -> ty'm1 * ... * ty'mn -> rty
but otherwise is semantically equivalent to expr by creating an anonymous function expression and inserting
appropariate coercions on arguments where necessary.

This means that F# functions whose inferred type includes an unsealed type in argument position may be passed
subtypes when called, without the need for explicit upcasts. For example:
type Base() =member b.X = 1
type Derived(i : int) =inherit Base()
member d.Y = i
let d = new Derived(7)let f (b : Base) = b.X// Call f: Base -> int with an instance of type Derived
let res = f d

199

// Use f as a first-class function value of type : Derived -> int
let res2 = (f : Derived -> int)
The F# compiler determines whether to insert flexibility after explicit instantiation, but before any arguments arechecked. For example, given the following:
let M<'b>(c :'b, d :'b) = 1
let obj = new obj()
let str = ""these expressions pass type-checking:M<obj>(obj, str)
M<obj>(str, obj)
M<obj>(obj, obj)
M<obj>(str, str)
M(obj, obj)
M(str, str)
These expressions do not, because the target type is a variable type:M(obj, str)
M(str, obj)

14.5 Constraint Solving
Constraint solving involves processing (“solving”) non-primitive constraints to reduce them to primitive,
normalized constraints on type variables. The F# compiler invokes constraint solving every time it adds a
constraint to the set of current inference constraints at any point during type checking.
Given a type inference environment, the normalized form of constraints is a list of the following primitive
constraints where typar is a type inference variable:
typar :> typetypar : null
(type or ... or type) : (member-sig)
typar : (new : unit -> 'T)
typar : structtypar : unmanaged
typar : comparison
typar : equality
typar : not structtypar : enum<type>typar : delegate<type, type>
Each newly introduced constraint is solved as described in the following sections.

14.5.1 Solving Equational Constraints
New equational constraints in the form typar = type or type = typar, where typar is a type inference
variable, cause type to replace typar in the constraint problem; typar is eliminated. Other constraints that areassociated with typar are then no longer primitive and are solved again.
New equational constraints of the form type<tyarg11,..., tyarg1n> = type<tyarg21,..., tyarg2n> are
reduced to a series of constraints tyarg1i = tyarg2i on identical named types and solved again.

200

14.5.2 Solving Subtype Constraints
Primitive constraints in the form typar :> obj are discarded.
New constraints in the form type1 :> type2, where type2 is a sealed type, are reduced to the constraint type1= type2 and solved again.
New constraints in either of these two forms are reduced to the constraints tyarg11 = tyarg21 ... tyarg1n =tyarg2n and solved again:
type<tyarg11,..., tyarg1n> :> type<tyarg21,..., tyarg2n>
type<tyarg11,..., tyarg1n> = type<tyarg21,..., tyarg2n>
Note: F# generic types do not support covariance or contravariance. That is, although singledimensional array types in the CLI are effectively covariant, F# treats these types as invariant
during constraint solving. Likewise, F# considers CLI delegate types as invariant and ignores any
CLI variance type annotations on generic interface types and generic delegate types.
New constraints of the form type1<tyarg11,..., tyarg1n> :> type2<tyarg21,..., tyarg2n> where type1
and type2 are hierarchically related, are reduced to an equational constraint on two instantiations of type2
according to the subtype relation between type1 and type2, and solved again.
For example, if MySubClass<'T> is derived from MyBaseClass<list<'T>>, then the constraint
MySubClass<'T> :> MyBaseClass<int>
is reduced to the constraint
MyBaseClass<list<'T>> :> MyBaseClass<list<int>>
and solved again, so that the constraint 'T = int will eventually be derived.
Note: Subtype constraints on single-dimensional array types ty[] :> ty are reduced to residual
constraints, because these types are considered to be subtypes of System.Array,
System.Collections.Generic.IList<'T>, System.Collections.Generic.ICollection<'T>,
and System.Collections.Generic.IEnumerable<'T>. Multidimensional array types ty[,…,]
are also subtypes of System.Array.
Types from other CLI languages may, in theory, support multiple instantiations of the sameinterface type, such as C : I<int>, I<string>. Consequently, it is more difficult to solve a
constraint such as C :> I<'T>. Such constraints are rarely used in practice in F# coding. To solve
this constraint, the F# compiler reduces it to a constraint C :> I<'T>, where I<'T> is the firstinterface type that occurs in the tree of supported interface types, when the tree is ordered frommost derived to least derived, and iterated left-to-right in the order of the declarations in the CLI
metadata.
The F# compiler ignores CLI variance type annotations on interfaces.
New constraints of the form type :> 'b are solved again as type = 'b.
Note: Such constraints typically occur only in calls to generic code from other CLI languages wherea method accepts a parameter of a “naked” variable type—for example, a C# 2.0 function with asignature such as T Choose<'T>(T x, T y).

14.5.3 Solving Nullness, Struct, and Other Simple Constraints
New constraints in any of the following forms, where type is not a variable type, are reduced to further
constraints:
type : null
type : (new : unit -> 'T)
type : structtype : not struct201

type : enum<type>
type : delegate<type, type>
type : unmanaged
The compiler then resolves them according to the requirements for each kind of constraint listed in §5.2 and§5.4.8.

14.5.4 Solving Member Constraints
New constraints in the following form ) are solved as member constraints (§5.2.3):(type1 or ... or typen) : (member-sig)
A member constraint is satisfied if one of the types in the support set type1 ... typen satisfies the member
constraint. A static type type satisfies a member constraint in the form
(staticopt member ident : arg-type1 * ... * arg-typen -> ret-type)if all of the following are true:type is a named type whose type definition contains the following member, which takes n arguments:
staticopt member ident : formal-arg-type1 * ... * formal-arg-typen -> ret-type

The type and the constraint are both marked static or neither is marked static .

The assertion of type inference constraints on the arguments and return types does not result in a type
inference error.

As mentioned in §5.2.3, a type variable may not be involved in the support set of more than one member
constraint that has the same name, staticness, argument arity, and support set. If a type variable is in the support
set of more than one such constraint, the argument and return types are themselves constrained to be equal.

14.5.4.1 Simulation of Solutions for Member Constraints
Certain types are assumed to implicitly define static members even though the actual CLI metadata for types
does not define these operators. This mechanism is used to implement the extensible conversion and mathfunctions of the F# library including sin, cos, int, float, (+), and (-). The following table shows the static
members that are implicitly defined for various types.
Type

Implicitly defined static members

Integral types:
byte, sbyte, int16, uint16, int32, uint32, int64,

op_BitwiseAnd, op_BitwiseOr, op_ExclusiveOr,

uint64, nativeint, unativeint

op_UnaryNegation, op_Increment, op_Decrement,

op_LeftShift, op_RightShift, op_UnaryPlus,
op_LogicalNot, op_OnesComplement
op_Addition, op_Subtraction, op_Multiply,
op_Division, op_Modulus, op_UnaryPlus
op_Explicit: takes the type as an argument andreturns byte, sbyte, int16, uint16, int32, uint32,
int64, uint64, float32, float, decimal,
nativeint, or unativeint

Signed integral CLI types:
sbyte, int16, int32, int64 and nativeint

op_UnaryNegation
Sign
Abs

202

Type

Implicitly defined static members

Floating-point CLI types:

Sin, Cos, Tan, Sinh, Cosh, Tanh, Atan, Acos, Asin,

float32 and float

Exp, Ceiling, Floor, Round, Log10, Log, Sqrt,
Atan2, Pow
op_Addition, op_Subtraction, op_Multiply,
op_Division, op_Modulus, op_UnaryPlus
op_UnaryNegation
Sign
Abs
op_Explicit: takes the type as an argument andreturns byte, sbyte, int16, uint16, int32, uint32,
int64, uint64, float32, float, decimal,
nativeint, or unativeint

decimal type

Sign

Note: The decimal type is included only for the Sign
static member. This is deliberate: in the CLI,
System.Decimal includes the definition of static
members such as op_Addition and the F# compiler
does not need to simulate the existence of thesemethods.
String type string

op_Explicit: takes the type as an argument andreturn byte, sbyte, int16, uint16, int32, uint32,
int64, uint64, float32, float or decimal.

14.5.5 Over-constrained User Type Annotations
An implementation of F# must give a warning if a type inference variable that results from a user type annotationis constrained to be a type other than another type inference variable. For example, the following results in awarning because 'T has been constrained to be precisely string:
let f (x:'T) = (x:string)
During the resolution of overloaded methods, resolutions that do not give such a warning are preferred overresolutions that do give such a warning.

14.6 Checking and Elaborating Function, Value, and Member
Definitions
This section describes how function, value, and member definitions are checked, generalized, and elaborated.
These definitions occur in the following contexts:


Module declarations

Class type declarations

203



Expressions

Computation expressions

Recursive definitions can also occur in each of these locations. In addition, member definitions in a mutually
recursive group of type declarations are implicitly recursive.
Each definition is one of the following:A function definition :inlineopt ident1 pat1 ... patn :opt return-typeopt = rhs-expr

A value definition, which defines one or more values by matching a pattern against an expression:
mutableopt pat :opt typeopt = rhs-exprA member definition:
staticopt member identopt ident pat1 ... patn = expr

For a function, value, or member definition in a class:1.

2.

If the definition is an instance function, value or member, checking uses an environment to which both of thefollowing have been added:

The instance variable for the class, if one is present.

All previous function and value definitions for the type, whether static or instance.

If the definition is static (that is, a static function, value or member defeinition), checking uses anenvironment to which all previous static function, value, and member definitions for the type have been
added.

14.6.1 Ambiguities in Function and Value Definitions
In one case, an ambiguity exists between the syntax for function and value definitions. In particular, ident pat =expr can be interpreted as either a function or value definition. For example, consider the following:type OneInteger = Id of int
let Id x = x
In this case, the ambiguity is whether Id x is a pattern that matches values of type OneInteger or is the function
name and argument list of a function called Id. In F# this ambiguity is always resolved as a function definition. In
this case, to make a value definition, use the following syntax in which the ambiguous pattern is enclosed inparentheses:
let v = if 3 = 4 then Id "yes" else Id "no"let (Id answer) = v

14.6.2 Mutable Value Definitions
Value definitions may be marked as mutable. For example:
let mutable v = 0
while v < 10 dov <- v + 1
printfn "v = %d" v
These variables are subject to the same restrictions as values of type byref<_> (§14.9), and are similarly
implicitly dereferenced.

14.6.3 Processing Value Definitions
A value definition pat = rhs-expr with optional pattern type type is processed as follows:

204

1.

The pattern pat is checked against a fresh initial type ty (or type if such a type is present). This check
results in zero or more identifiers ident1 ... identm, each of type ty1 ... tym.

2.

The expression rhs-expr is checked against initial type ty, resulting in an elaborated form expr.

3.

Each identi (of type tyi) is then generalized (§14.6.7) and yields generic parameters <typarsj>.

4.

The following rules are checked:5.

All identj must be distinct.



Value definitions may not be inline.

If pat is a single value pattern, the resulting elaborated definition is:
ident <typars1> = expr
body-expr

6.

Otherwise, the resulting elaborated definitions are the following, where tmp is a fresh identifier and each
expri results from the compilation of the pattern pat (§7) against input tmp.
tmp <typars1… typarsn> = expr
ident1 <typars1> = expr1…
identn <typarsn> = exprn

14.6.4 Processing Function Definitions
A function definition ident1 pat1 ... patn = rhs-expr is processed as follows:1.

If ident1 is an active pattern identifier then active pattern result tags are added to the environment (§10.2.4).

2.

The expression (fun pat1 ... patn : return-type -> rhs-expr) is checked against a fresh initial typety1 and reduced to an elaborated form expr1. The return type is omitted if the definition does not specify it.

3.

The ident1 (of type ty1) is then generalized (§14.6.7) and yields generic parameters <typars1>.

4.

The following rules are checked:
Function definitions may not be mutable. Mutable function values should be written as follows:
let mutable f = (fun args -> ...)

5.

The patterns of functions may not include optional arguments (§8.13.5).

The resulting elaborated definition is:
ident1 <typars1> = expr1

14.6.5 Processing Recursive Groups of Definitions
A group of functions and values may be declared recursive through the use of let rec. Groups of members in arecursive set of type definitions are also implicitly recursive. In this case, the defined values are available for use
within their own definitions—that is, within all the expressions on the right-hand side of the definitions.
For example:
let rec twoForward count =printfn "at %d, taking two steps forward" countif count = 1000 then "got there!"
else oneBack (count + 2)
and oneBack count =printfn "at %d, taking one step back " count
twoForward (count – 1)

205

When one or more definitions specifies a value, the recursive expressions are analyzed for safety (§14.6.6). This
analysis may result in warnings—including some reported at compile time—and runtime checks.
Within recursive groups, each definition in the group is checked (§14.6.7) and then the definitions are generalized
incrementally. In addition, any use of an ungeneralized recursive definition results in immediate constraints on therecursively defined construct. For example, consider the following declaration:
let rec countDown count x =if count > 0 thenlet a = countDown (count - 1) 1
let b = countDown (count – 1) "Hello"
a + b
else
1

// constrains "x" to be of type int// constrains "x" to be of type string

In this example, the definition is not valid because the recursive uses of f result in inconsistent constraints on x.
If a definition has a full signature, early generalization applies and recursive calls at different types are permitted(§14.6.7). For example:
module M =let rec f<'T> (x:'T) : 'T =let a = f 1
let b = f "Hello"x
In this example, the definition is valid because f is subject to early generalization, and so the recursive uses of f
do not result in inconsistent constraints on x.

14.6.6 Recursive Safety Analysis
A set of recursive definitions may include value definitions. For example:
type Reactor = React of (int -> React) * int
let rec zero = React((fun c-> zero), 0)

let const n =let rec r = React((fun cr-> r), n)Recursive value definitions may result in invalid recursive cycles, such as the following:
let rec x = x + 1
The Recursive Safety Analysis process partially checks the safety of these definitions and convert thems to aform that uses lazy initialization, where runtime checks are inserted to check initialization.
A right-hand side expression is safe if it is any of the following:A function expression, including those whose bodies include references to variables that are definedrecursively.

An object expression that implements an interface, including interfaces whose member bodies include
references to variables that are being defined recursively.



A lazy delayed expression.

A record, tuple, list, or data construction expression whose field initialization expressions are all safe.



A value that is not being recursively bound.



A value that is being recursively bound and appears in one of the following positions:

As a field initializer for a field of a record type where the field is marked mutable.

206



As a field initializer for an immutable field of a record type that is defined in the current assembly.
If record fields contain recursive references to values being bound, the record fields must be initialized in
the same order as their declared type, as described later in this section.

Any expression that refers only to earlier variables defined by the sequence of recursive definitions.

Other right-hand side expressions are elaborated by adding a new definition. If the original definition isu = expr
then a fresh value (say v) is generated with the definition:v = lazy expr
and occurrences of the original variable u on the right-hand side are replaced by Lazy.force v. The following
definition is then added at the end of the definition list:u = v.Force()
Note: This specification implies that recursive value definitions are executed as an initializationgraph of delayed computations. Some recursive references may be checked at runtime because
the computations that are involved in evaluating the definitions might actually execute the delayedcomputations. The F# compiler gives a warning for recursive value definitions that might involve a
runtime check. If runtime self-reference does occur then an exception will be raised.
Recursive value definitions that involve computation are useful when defining objects such as
forms, controls, and services that respond to various inputs. For example, GUI elements that store
and retrieve the state of the GUI elements as part of their specification typically involve recursive
value definitions. A simple example is the following menu item, which prints out part of its state
when invoked:
open System.Windows.Form
let rec menuItem : MenuItem =new MenuItem("&Say Hello",
new EventHandler(fun sender e ->printfn "Text = %s" menuItem.Text),
Shortcut.CtrlH)
This code results in a compiler warning because, in theory, the
new MenuItem(...) constructor might evaluate the callback as part of the construction process.
However, because the System.Windows.Forms library is well designed, in this example this does
not happen in practice, and so the warning can be suppressed or ignored by using compiler
options.
The F# compiler performs a simple approximate static analysis to determine whether immediate cyclic
dependencies are certain to occur during the evaluation of a set of recursive value definitions. The compiler
creates a graph of definite references and reports an error if such a dependency cycle exists. All references
within function expressions, object expressions, or delayed expressions are assumed to be indefinite, whichmakes the analysis an under-approximation. As a result, this check catches naive and direct immediate recursion
dependencies, such as the following:
let rec A = B
and B = A + 1+ 1

Here, a compile-time error is reported. This check is necessarily approximate because dependencies underfunction expressions are assumed to be delayed, and in this case the use of a lazy initialization means thatruntime checks and forces are inserted.
Note: In F# 2.0 this check does not apply to value definitions that are generic through generalization
because a generic value definition is not executed immediately, but is instead represented as a
generic method. For example, the following value definitions are generic because each right-handside is generalizable:

207let rec a = b
and b = a
In compiled code they are represented as a pair of generic methods, as if the code had been written
as follows:
let rec a<'T>() = b<'T>()
and b<'T>() = a<'T>()
As a result, the definitions are not executed immediately unless the functions are called. Suchdefinitions indicate a programmer error, because executing such generic, immediately recursive
definitions results in an infinite loop or an exception. In practice these definitions only occur inpathological examples, because value definitions are generalizable only when the right-hand-side isvery simple, such as a single value. Where this issue is a concern, type annotations can be added
to existing value definitions to ensure they are not generic. For example:
let rec a : int = b
and b : int = a
In this case, the definitions are not generic. The compiler performs immediate dependency analysis
and reports an error.
In addition, a limitation specific to F# 2.0 means that record fields in recursive data expressions
must be initialized in the order they are declared. For example:
type Foo = {x: inty: intparent: Foo option
children: Foo list}
let rec parent = { x = 0; y = 0; parent = None; children = children }and children = [{ x = 1; y = 1; parent = Some parent; children = [] }]printf "%A" parent
Here, if the order of the fields x and y is swapped, a type-checking error occurs.

14.6.7 Generalization
Generalization is the process of inferring a generic type for a definition where possible, thereby making theconstruct reusable with multiple different types. Generalization is applied by default at all function, value, andmember definitions, except where listed later in this section. Generalization also applies to member definitions
that implement generic virtual methods in object expressions.
Generalization is applied incrementally to items in a recursive group after each item is checked.
Generalization takes a set of ungeneralized but type-checked definitions checked-defns that form part of arecursive group, plus a set of unchecked definitions unchecked-defns that have not yet been checked in therecursive group, and an environment env. Generalization involves the following steps:
1.

Choose a subset generalizable-defns of checked-defns to generalize.
A definition can be generalized if its inferred type is closed with respect to any inference variables that arepresent in the types of the unchecked-defns that are in the recursive group and that are not yet checked or
which, in turn, cannot be generalized. A greatest-fixed-point computation repeatedly removes definitions
from the set of checked-defns until a stable set of generalizable definitions remains.

208

2.

Generalize all type inference variables that are not otherwise ungeneralizable and for which any of thefollowing is true:The variable is present in the inferred types of one or more of generalizable-defns.



The variable is a type parameter copied from the enclosing type definition (for members and “let”
definitions in classes).



The variable is explicitly declared as a generic parameter on an item.

The following type inference variables cannot be generalized:

A type inference variable ^typar that is part of the inferred or declared type of a definition, unless thedefinition is marked inline.



A type inference variable in an inferred type in the ExprItems or PatItems tables of env, or in an inferred
type of a module in the ModulesAndNamespaces table in env.



A type inference variable that is part of the inferred or declared type of a definition in which theelaborated right-hand side of the definition is not a generalizable expression, as described later in thissection.



A type inference variable that appears in a constraint that itself refers to an ungeneralizable typevariable.

Generalizable type variables are computed by a greatest-fixed-point computation, as follows:
1.

Start with all variables that are candidates for generalization.

2.

Determine a set of variables U that cannot be generalized because they are free in the environment orpresent in ungeneralizable definitions.

3.

Remove the variables in U from consideration.

4.

Add to U any inference variables that have a constraint that involves a variable in U.

5.

Repeat steps 2 through 4.

Informally, generalizable expressions represent a subset of expressions that can be freely copied andinstantiated at multiple types without affecting the typical semantics of an F# program. The following expressions
are generalizable:

A function expression

An object expression that implements an interface

A delegate expression

A “let” definition expression in which both the right-hand side of the definition and the body of the expression
are generalizableA “let rec” definition expression in which the right-hand sides of all the definitions and the body of theexpression are generalizable

A tuple expression, all of whose elements are generalizableA record expression, all of whose elements are generalizable, where the record contains no mutable fields



A union case expression, all of whose arguments are generalizableAn exception expression, all of whose arguments are generalizableAn empty array expression



A constant expression

An application of a type function that has the GeneralizableValue attribute.

209

Explicit type parameter definitions on value and member definitions can affect the process of type inference andgeneralization. In particular, a declaration that includes explicit generic parameters will not be generalized beyond
those generic parameters. For example, consider this function:
let f<'T> (x : 'T) y = x
During type inference, this will result in a function of the following type, where '_b is a type inference variable thatis yet to be resolved.
f<'T> : 'T -> '_b -> '_b
To permit generalization at these definitions, either remove the explicit generic parameters (if they can beinferred), or use the required number of parameters, as the following example shows:
let throw<'T,'U> (x:'T) (y:'U) = x

14.6.8 Condensation of Generalized Types
After a function or member definition is generalized, its type is condensed by removing generic type parametersthat apply subtype constraints to argument positions. (The removed flexibility is implicitly reintroduced at eachuse of the defined function; see §14.4.2).
Condensation decomposes the type of a value or member to the following form:ty11 * ... * ty1n -> ... -> tym1 * ... * tymn -> rty
The positions tyij are called the parameter positions for the type.
Condensation applies to a type parameter 'a if all of the following are true:'a is not an explicit type parameter.

'a occurs at exactly one tyij parameter position.

'a has a single coercion constraint 'a :> ty and no other constraints. However, one additional nullness
constraint is permitted if ty satisfies the nullness constraint.

'a does not occur in any other tyij, nor in rty.

'a does not occur in the constraints of any condensed typar.

Condensation is a greatest-fixed-point computation that initially assumes all generalized type parameters arecondensed, and then progressively removes type parameters until a minimal set remains that satisfies the above
rules.
The compiler removes all condensed type parameters and replaces them with their subtype constraint ty. For
example:let F x = (x :> System.IComparable).CompareTo(x)After generalization, the function is inferred to have the following type:
F : 'a -> int when 'a :> System.IComparable
In this case, the actual inferred, generalized type for F is condensed to:
F : System.IComparable -> R
Condensation does not apply to arguments of unconstrained variable type. For example:
let ignore x = ()with typeignore: 'a -> unit
In particular, this is not condensed to210

ignore: obj -> unit
In rare cases, condensation affects the points at which value types are boxed. In the following example, thevalue 3 is now boxed at uses of the function:
F 3
If a function is not generalized, condensation is not applied. For example, consider the following:
let test1 =let ff = Seq.map id >> Seq.length(ff [1], ff [| 1 |]) // error here
In this example, ff is not generalized, because it is not defined by using a generalizable expression—computed
functions such as Seq.map id >> Seq.length are not generalizable. This means that its inferred type, after
processing the definition, is
F : '_a -> int when '_a :> seq<'_b>where the type variables are not generalized and are unsolved inference variables. The application of ff to [1]equates 'a with int list, making the following the type of F:
F : int list -> int
The application of ff to an array type then causes an error. This is similar to the error returned by the following:
let test1 =let ff = Seq.map id >> Seq.length(ff [1], ff ["one"]) // error here
Again, ff is not generalized, and its use with arguments of type int list and string list is not permitted.

14.7 Dispatch Slot Inference
The F# compiler applies Dispatch Slot Inference to object expressions and type definitions before it processes
their members. For both object expressions and type definitions, the following are input to Dispatch Slot
Inference:

A type ty0 that is being implemented.



A set of members override x.M(arg1...argN).



A set of additional interface types ty1 ... tyn.



A further set of members override x.M(arg1...argN) for each tyi.

Dispatch slot inference associates each member with a unique abstract member or interface member that thecollected types tyi define or inherit.
The types ty0 ... tyn together imply a collection of required types R, each of which has a set of required dispatch
slots SlotsR of the form abstract M : aty1...atyN -> atyrty. Each dispatch slot is placed under the mostspecific tyi relevant to that dispatch slot. If there is no most-specific type for a dispatch slot, an error occurs.
For example, assume the following definitions:type IA = interface abstract P : int end
type IB = interface inherit IA end
type ID = interface inherit IB end

211

With these definitions, the following object expression is legal. Type IB is the most-specific implemented type thatencompasses IA, and therefore the implementation mapping for P must be listed under IB:
let x = { new ID
interface IB withmember x.P = 2 }But given:
type IA = interface abstract P : int end
type IB = interface inherit IA end
type IC = interface inherit IB end
type ID = interface inherit IB inherit IC end
then the following object expression causes an error, because both IB and IC include the interface IA, andconsequently the implementation mapping for P is ambiguous.
let x = { new ID
interface IB withmember x.P = 2
interface IC withmember x.P = 2 }The ambiguity can be resolved by explicitly implementing interface IA.
After dispatch slots are assigned to types, the compiler tries to associate each member with a dispatch slot basedon name and number of arguments. This is called dispatch slot inference, and it proceeds as follows:For each member x.M(arg1...argN) in type tyi, attempt to find a single dispatch slot in the form
abstract M : aty1...atyN -> rtywith name M, argument count N, and most-specific implementing type tyi.


To determine the argument counts, analyze the syntax of patterns and look specifically for tuple and unit
patterns. Thus, the following members have argument count 1, even though the argument type is unit:
member obj.ToString(() | ()) = ...
member obj.ToString(():unit) = ...
member obj.ToString(_:unit) = ...

A member may have a return type, which is ignored when determining argument counts:
member obj.ToString() : string = ...

For example, given
let obj1 ={ new System.Collections.Generic.IComparer<int> withmember x.Compare(a,b) = compare (a % 7) (b % 7) }the types of a and b are inferred by looking at the signature of the implemented dispatch slot, and are hence bothinferred to be int.

14.8 Dispatch Slot Checking
Dispatch Slot Checking is applied to object expressions and type definitions to check consistency properties,
such as ensuring that all abstract members are implemented.
After the compiler checks all bodies of all methods, it checks that a one-to-one mapping exists between dispatch
slots and implementing members based on exact signature matching.
The interface methods and abstract method slots of a type are collectively known as dispatch slots. Each object
expression and type definition results in an elaborated dispatch map. This map is keyed by dispatch slots, which212

are qualified by the declaring type of the slot. This means that a type that supports two interfaces I and I2, bothof which contain the method m, may supply different implementations for I.m() and I2.m().
The construction of the dispatch map for any particular type is as follows:

If the type definition or extension has an implementation of an interface, mappings are added for eachmember of the interface,

If the type definition or extension has a default or override member, a mapping is added for the
associated abstract member slot.

14.9 Byref Safety Analysis
Byref arguments are pointers that can be stack-bound and are used to pass values by reference to procedures
in CLI languages, often to simulate multiple return values. Byref pointers are not often used in F#; more typically,tuple values are used for multiple return values. However, a byref value can result from calling or overriding a CLI
method that has a signature that involves one or more byref values.
To ensure the safety of byref arguments, the following checks are made:

Byref types may not be used as generic arguments.



Byref values may not be used in any of the following:

The argument types or body of function expressions (fun … -> …).



The member implementations of object expressions.



The signature or body of let-bound functions in classes.

The signature or body of let-bound functions in expressions.

Note that function expressions occur in:

The elaborated form of sequence expressions.



The elaborated form of computation expressions.



The elaborated form of partial applications of module-bound functions and members.

In addition:


A generic type cannot be instantiated by a byref type.



An object field cannot have a byref type.



A static field or module-bound value cannot have a byref type.

As a result, a byref-typed expression can occur only in these situations:

As an argument to a call to a module-defined function or class-defined function.

On the right-hand-side of a value definition for a byref-typed local.

These restrictions also apply to uses of the prefix && operator for generating native pointer values.

21314.10Arity Inference

During checking, members within types and function definitions within modules are inferred to have an arity. An
arity includes both of the following:The number of iterated (curried) arguments nA tuple length for these arguments [A1;...;An]. A tuple length of zero indicates that the correspondingargument is of type unit.

Arities are inferred as follows. A function definition of the following form is given arity [A1;...;An], where each Ai
is derived from the tuple length for the final inferred types of the patterns:
let ident pat1 ... patn = ...
For example, the following is given arity [1; 2]:
let f x (y,z) = x + y + z
Arities are also inferred from function expressions that appear on the immediate right of a value definition. For
example, the following has an arity of [1]:let f = fun x -> x + 1
Similarly, the following has an arity of [1;1]:
let f x = fun y -> x + yArity inference is applied partly to help define the elaborated form of a function definition. This is the form that
other CLI languages see. In particular:

A function value F in a module that has arity [A1;...;An] and the typety1,1 * ... * ty1,A1 -> ... -> tyn,1 * ... * tyn,An -> rty
elaborates to a CLI static method definition with signature
rty F(ty1,1, ..., ty1,A1, ..., tyn,1, ..., tyn,An).



F# instance (respectively static) methods that have arity [A1;...;An] and typety1,1 * ... * ty1,A1 -> ... -> tyn,1 * ... * tyn,An -> rty
elaborate to a CLI instance (respectively static) method definition with signature
rty F(ty1,1, ..., ty1,A1), subject to the syntactic restrictions that result from the patterns that define themember, as described later in this section.

For example, consider a function in a module with the following definition:
let AddThemUp x (y, z) = x + y + z
This function compiles to a CLI static method with the following C# signature:
int AddThemUp(int x, int y, int z);
Arity inference applies differently to function and member definitions. Arity inference on function definitions is fully
type-directed. Arity inference on members is limited if parentheses or other patterns are used to specify themember arguments. For example:
module Foo =// compiles as a static method taking 3 arguments
let test1 (a1: int, a2: float, a3: string) = ()// compiles as a static method taking 3 arguments
let test2 (aTuple : int * float * string) = ()// compiles as a static method taking 3 arguments
let test3 ( (aTuple : int * float * string) ) = ()214

// compiles as a static method taking 3 arguments
let test4 ( (a1: int, a2: float, a3: string) ) = ()// compiles as a static method taking 3 arguments
let test5 (a1, a2, a3 : int * float * string) = ()type Bar() =// compiles as a static method taking 3 arguments
static member Test1 (a1: int, a2: float, a3: string) = ()// compiles as a static method taking 1 tupled argument
static member Test2 (aTuple : int * float * string) = ()// compiles as a static method taking 1 tupled argument
static member Test3 ( (aTuple : int * float * string) ) = ()// compiles as a static method taking 1 tupled argument
static member Test4 ( (a1: int, a2: float, a3: string) ) = ()// compiles as a static method taking 1 tupled argument
static member Test5 (a1, a2, a3 : int * float * string) = ()14.11

Additional Constraints on CLI Methods

F# treats some CLI methods and types specially, because they are common in F# programming and cause
extremely difficult-to-find bugs. For each use of the following constructs, the F# compiler imposes additional ad
hoc constraints:
x.Equals(yobj) requires type ty : equality for the static type of x
x.GetHashCode() requires type ty : equality for the static type of x
new Dictionary<A,B>() requires A : equality, for any overload that does not take an
IEqualityComparer<T>No constraints are added for the following operations. You should consider writing wrappers around thesefunctions to improve the type safety of the operations.
System.Array.BinarySearch<T>(array,value) requiring C : comparison, for any overload that does
not take an IComparer<T>
System.Array.IndexOf requiring C : equality
System.Array.LastIndexOf(array,T) requiring C : equality
System.Array.Sort<'T>(array) requiring C : comparison, for any overload that does not take an
IEqualityComparer<T>
new SortedList<A,B>() requiring A : comparison, for any overload that does not take an
IEqualityComparer<T>
new SortedDictionary<A,B>() requiring C : comparison, for any overload that does not take an
IEqualityComparer<_>215

Lexical Filtering
15.1 Lightweight Syntax
F# supports lightweight syntax, in which whitespace makes indentation significant.
The lightweight syntax option is a conservative extension of the explicit language syntax, in the sense that it
simply lets you leave out certain tokens such as in and ;; because the parser takes indentation into account.
Indentation can make a surprising difference in the readability of code. Compiling your code with the indentationaware syntax option is useful even if you continue to use explicit tokens, because the compiler reports many
indentation problems with your code and ensures a regular, clear formatting style.
In the processing of lightweight syntax, comments are considered pure whitespace. This means that the compiler
ignores the indentation position of comments. Comments act as if they were replaced by whitespace characters.
Tab characters cannot be used in F# files.

15.1.1 Basic Lightweight Syntax Rules by Example
The basic rules that the F# compiler applies when it processes lightweight syntax are shown below, illustrated byexample.

;; delimiter
When the lightweight syntax option is enabled, top level expressions do not require the ;; delimiter because
every construct that starts in the first column is implicitly a new declaration. The ;; delimiter is still required toterminate interactive entries to fsi.exe, but not when using F# Interactive from Visual Studio.
Lightweight Syntax

Normal Syntax

printf "Hello"

printf "Hello";;printf "World"

printf "World";;

in keyword
When the lightweight syntax option is enabled, the in keyword is optional. The token after the "=" in a 'let'definition begins a new block, and the pre-parser inserts an implicit separating in token between each definition
that begins at the same column as that token.
Lightweight Syntax

Normal Syntax

let SimpleSample() =#indent "off"let x = 10 + 12 - 3

let SimpleSample() =let y = x * 2 + 1

let x = 10 + 12 - 3 inlet r1,r2 = x/3, x%3

let y = x * 2 + 1 in

(x,y,r1,r2)

let r1,r2 = x/3, x%3 in(x,y,r1,r2)

done keyword
When the lightweight syntax option is enabled, the done keyword is optional. Indentation establishes the scope ofstructured constructs such as match, for, while and if/then/else.
Lightweight Syntax

Normal Syntax

let FunctionSample() =#indent "off"let tick x = printfn "tick %d" x

let FunctionSample() =let tock x = printfn "tock %d" x

let tick x = printfn "tick %d" x inlet choose f g h x =let tock x = printfn "tock %d" x in

if f x then g x else h x

let choose f g h x =for i = 0 to 10 do

if f x then g x else h x inchoose (fun n -> n%2 = 0) tick tock i

for i = 0 to 10 doprintfn "done!"choose (fun n -> n%2 = 0) tick tock i
done;
printfn "done!"

if/then/else Scope
When the lightweight syntax option is enabled, the scope of if/then/else is implicit from indentation. Without thelightweight syntax option, begin/end or parentheses are often required to delimit such constructs.
Lightweight Syntax

Normal Syntax

let ArraySample() =#indent "off"let numLetters = 26

let ArraySample() =let results = Array.create numLetters 0

let numLetters = 26 inlet data = "The quick brown fox"

let results = Array.create numLetters 0 in

for i = 0 to data.Length - 1 do

let data = "The quick brown fox" in

let c = data.Chars(i)for i = 0 to data.Length - 1 do

let c = Char.ToUpper(c)let c = data.Chars(i) in

if c >= 'A' && c <= 'Z' thenlet c = Char.ToUpper(c)let i = Char.code c - Char.code 'A'in

if c >= 'A' && c <= 'Z' then begin

results.[i] <- results.[i] + 1

let i = Char.code c - Char.code 'A' inprintfn "done!"

results.[i] <- results.[i] + 1
end
done;
printfn "done!"

15.1.2 Inserted Tokens
Lexical filtering inserts the following hidden tokens :token $in
token $done
token $begin
token $end
token $sep
token $app
token $tyapp// Note: also called ODECLEND// Note: also called ODECLEND// Note: also called OBLOCKBEGIN// Note: also called OEND, OBLOCKEND and ORIGHT_BLOCK_END// Note: also called OBLOCKSEP// Note: also called HIGH_PRECEDENCE_APP// Note: also called HIGH_PRECEDENCE_TYAPP

Note: The following tokens are also used in the Microsoft F# implementation. They are translationsof the corresponding input tokens and help provide better error messages for lightweight syntax
code:
tokens $let $use

$let! $use! $do $do! $then $else $with $function $fun

15.1.3 Grammar Rules Including Inserted Tokens
Additional grammar rules take into account the token transformations performed by lexical filtering:
expr +:=| let function-defn $in expr
| let value-defn $in expr
| let rec function-or-value-defns $in expr
| while expr do expr $done| if expr then $begin expr $end
| for pat in expr do expr $done| for expr to expr do expr $done218

| try expr $end with expr $done| try expr $end finally expr $done| expr $app expr// equivalent to "expr(expr)"| expr $sep expr// equivalent to "expr; expr"| expr $tyapp < types >// equivalent to "expr<types>"| $begin expr $end // equivalent to “expr”
elif-branch +:=| elif expr then $begin expr $end
else-branch +:=| else $begin expr $end
class-or-struct-type-body +:=| $begin class-or-struct-type-body $end// equivalent to class-or-struct-type-bodymodule-elems +:=| $begin module-elem ... module-elem $end
module-abbrev +:=| module ident = $begin long-ident $end
module-defn +:=| module ident = $begin module-defn-body $end
module-signature-elements +:=| $begin module-signature-element ... module-signature-element $end
module-signature +:=| module ident = $begin module-signature-body $end

15.1.4 Offside Lines
Lightweight syntax is sometimes called the “offside rule”. In F# code, offside lines occur at column positions. For
example, an = token associated with let introduces an offside line at the column of the first non-whitespace
token after the = token.
Other structured constructs also introduce offside lines at the following places:

The column of the first token after then in an if/then/else construct.

The column of the first token after try, else, ->, with (in a match/with or try/with), or with (in a typeextension).



The column of the first token of a (, { or begin token.

The start of a let, if or module token.

Here are some examples of how the offside rule applies to F# code. In the first example, let and typedeclarations are not properly aligned, which causes F# to generate a warning.
// "let" and "type" declarations in// modules must be precisely aligned.
let x = 1
let y = 2 <-- unmatched 'let'let z = 3<-- warning FS0058: possible
incorrect indentation: this token is offside ofcontext at position (2:1)

219

In the second example, the | markers in the match patterns do not align properly:// The "|" markers in patterns must align.
// The first "|" should always be inserted.
let f () =match 1+1 with
| 2 -> printf "ok"| _ -> failwith "no!"<-- syntax error

15.1.5 The Pre-Parse StackF# implements the lightweight syntax option by preparsing the token stream that results from a lexical analysis of
the input text according to the lexical rules in §15.1.3. Pre-parsing for lightweight syntax uses a stack of contexts.
When a column position becomes an offside line, a context is pushed.

The closing bracketing tokens ), }, and end terminate offside contexts up to and including the context thatthe corresponding opening token introduced.

15.1.6 Full List of Offside Contexts
This section describes the full list of offside contexts that is kept on the pre-parse stack.
The SeqBlock context is the primary context of the analysis.It indicates a sequence of items that must be columnaligned. Where necessary for parsing, the compiler automatically inserts a delimiter that replaces the regular in
and ; tokens between the syntax elements. The SeqBlock context is pushed at the following times:

Immediately after the start of a file, excluding lexical directives such as #if.

Immediately after an = token is encountered in a Let or Member context.

Immediately after a Paren, Then, Else, WithAugment, Try, Finally, Do context is pushed.

Immediately after an infix token is encountered.

Immediately after a -> token is encountered in a MatchClauses context.

Immediately after an interface, class, or struct token is encountered in a type declaration.

Here “immediately after” refers to the fact that the column position associated with the SeqBlock is the first token
following the significant token.
The following contexts are associated with nested constructs that are introduced by the specified keywords:
Context

Pushed when the token stream contains…

Let

The let keyword

If

The if or elif keyword

Try

The try keyword

Lazy

The lazy keyword

Fun

The fun keyword

Function

The function keyword

WithLetThe with keyword as part of a record expression or an object expression whose
members use the syntax{ new Foo with M() = 1 and N() = 2 }WithAugment

The with keyword as part of an extension, interface, or object expression whose
members use the syntax { new Foo member x.M() = 1 member x. N() = 2 }Match

the match keyword

For

the for keyword

220

Context

Pushed when the token stream contains…

While

The while keyword

Then

The then keyword

Else

The else keyword

Do

The do keyword

Type

The type keyword

Namespace

The namespace keyword

ModuleThe module keyword

Member The member, abstract, default, or override keyword, if the Member context is
not already active, because multiple tokens may be present.
—or—Paren(token)

 ( is the next token after the new keyword. This distinguishes the member
declaration new(x) = ... from the expression new x()(, begin, struct, sig, {, [, [|, or quote-op-left

MatchClauses

The with keyword in a Try or Match context immediately after a function keyword.

VanillaAn otherwise unprocessed keyword in a SeqBlock context.

15.1.7 Balancing Rules
When the compiler processes certain tokens, it pops contexts off the offside stack until the stack reaches aparticular condition. When it pops a context, the compiler may insert extra tokens to indicate the end of theconstruct. This procedure is called balancing the stack.
The following table lists the contexts that the compiler pops and the balancing condition for each:Token

Contexts Popped and Balancing Conditions:End

Enclosing context is one of the following: WithAugment
 Paren(interface) Paren(class) Paren(sig) Paren(struct) Paren(begin);;

Pop all contexts from stack

else

If

elif

If

done

Do

in

For or Let

eith

Match, Member, Interface, Try, Type

finally

Try)

Paren(()}

Paren({)]

Paren([)|]

Paren([|)

quote-op-rightParen(quote-op-left)22115.1.8 Offside Tokens, Token Insertions, and Closing Contexts
The offside limit for the current offside stack is the rightmost offside line for the offside contexts on the context
stack. The following figure shows the offside limits:

let FunctionSample() =let tick x = printfn "tick %d" x
let tock x = printfn "tock %d" x
let choose f g h x =if f x then g x else h x
for i = 0 to 10 dochoose (fun n -> n%2 = 0) tick tock i
printfn "done!"
Offside limit for inner let and for contexts
Offside limit for outer let context

When a token occurs on or before the offside limit for the current offside stack, and a permitted undentation does
not apply, enclosing contexts are closed until the token is no longer offside. This may result in the insertion of
extra delimiting tokens.
Contexts are closed as follows:When a Fun context is closed, the $end token is inserted.

When a SeqBlock, MatchClauses, Let, or Do context is closed, the $end token is inserted, with the exceptionof the first SeqBlock pushed for the start of the file.

When a While or For context is closed, and the offside token that forced the close is not done, the $done
token is inserted.

When a Member context is closed, the $end token is inserted.

When a WithAugment context is closed, the $end token is inserted.

If a token is offside and a context cannot be closed, then an “undentation” warning or error is issued to indicatethat the construct is badly formatted.
Tokens are also inserted in the following situations:When a SeqBlock context is pushed, the $begin token is inserted, with the exception of the first SeqBlockpushed for the start of the file.

When a token other than and appears directly on the offside line of Let context, and the next surrounding
context is a SeqBlock, the $in token is inserted.

When a token occurs directly on the offside line of a SeqBlock on the second or subsequent lines of theblock, the $sep token is inserted. This token plays the same role as ; in the grammar rules.
For example, consider this source text:
let x = 1x
The raw token stream contains let, x, =, 1, x and the end-of-file marker eof. An initial SeqBlock is pushed
immediately after the start of the file, at the first token in the file, with an offside line on column 0. The let
token pushes a Let context. The = token in a Let context pushes a SeqBlock context and inserts a $begin
token. The 1 pushes a Vanilla context. The final token, x, is offside from the Vanilla context, which pops that
context. It is also offside from the SeqBlock context, which pops the context and inserts $end. It is also
offside from the Let context, which inserts another $end token. It is directly aligned with the SeqBlockcontext, so a $seq token is inserted.

222

15.1.9 Exceptions to the Offside Rules
The compiler makes some exceptions to the offside rules when it analyzes av token to determine whether it isoffside from the current context. The following table summarizes the exceptions and shows examples of each.
Context

Exception

ExampleSeqBlockAn infix token may be offside by thesize of the token plus one.

let x =

SeqBlockAn infix token may align precisely withthe offside line of the SeqBlock.

SeqBlock

The infix |> token that begins the last

expr + expr
+ expr + expr
let x =
expr|> f expr|> f expr
let someFunction(someCollection) =someCollection|> List.map (fun x -> x + 1)
new MenuItem("&Open...",
new EventHandler(fun _ _ ->...
))line is not considered as a newelement in the sequence block on theright-hand side of the definition. The
same also applies to end, and, with,then, and right-parenthesis operators.
In the example, the first ) token does
not indicate a new element in asequence of items, even though italigns precisely with the sequence
block that starts at the beginning of theargument list.
Let

The and token may align precisely withthe let keyword.

TypeThe }, end, and, and | tokens mayalign precisely with the type keyword.

For

The done token may align precisely
with the for keyword.

SeqBlock;Match

On the right-hand side of an arrow fora match expression, a token may alignprecisely with the match keyword.
This exception allows the last
expression to align with the match, sothat a long series of matches does notincrease indentation.

Interface

The end token may align precisely with
the interface keyword.

let rec x = 1
and y = 2x + ytype X =
| A
| B
with
member x.Seven = 21 / 3
endand Y = {x : int}
and Z() = classmember x.Eight = 4 + 4
endfor i = 1 to 3 do
expr
done
match x with| Some(_) -> 1
| None ->match y with| Some(_) -> 2
| None ->3interface IDisposable withmember x.Dispose() = printfn disposing!"
end

223

Context

Exception

ExampleIf

The then, elif, and else tokens may

if bigthen callSomeFunction()
elif smallthen callSomeOtherFunction()else doSomeCleanup()
Example 1:
try
callSomeFunction()
finally
doSomeCleanup()
Example 2:align precisely with the if keyword.

Try

The finally and with tokens mayalign precisely with the try keyword.

try

Do

The done token may align precisely
with the do keyword.

15.1.10

callSomeFunction()with Failure(s) ->doSomeCleanup()
for i = 1 to 3
do
expr
done

Permitted Undentations

As a general rule, incremental indentation requires that nested expressions occur at increasing column positionsin indentation-aware code. Warnings or syntax errors are issued when this is not the case. However, undentationis permitted for the following constructs:


Bodies of function expressionsBranches of if/then/else expressions



Bodies of modules and module types

15.1.10.1 Undentation of Bodies of Function Expressions
The bodies of functions may be undented from the fun or function symbol. As a result, the compiler ignores the
symbol when determining whether the body of the function satisfies the incremental indentation rule. For
example, the printf expression in the following example is undented from the fun symbol that delimits thefunction definition:
let HashSample(tab: Collections.HashTable<_,_>) =tab.Iterate (fun c v ->printfn "Entry (%O,%O)" c v)
However, the block must not undent past other offside lines. Thefollowing is not permitted because the secondline breaks the offside line established by the = in the first line:
let x = (function (s, n) ->(fun z ->s+n+z))
Constructs enclosed in brackets may be undented.

15.1.10.2 Undentation of Branches of If/Then/Else Expressions
The body of a ( ... ) or begin ... end block in an if/then/else expression may be undented when the body ofthe block follows the then or else keyword but may not undent further than the if keyword. In this example,
the parenthesized block follows then, so the body can be undented to the offside line established by if:
let IfSample(day: System.DayOfWeek) =if day = System.DayOfWeek.Monday then (printf "I don't like Mondays")
224

15.1.10.3 Undentation of Bodies of Modules and Module Types
The bodies of modules and module types that are delimited by begin and end may be undented. For example, inthe following example the two let statements that comprise the module body are undented from the =.
module MyNestedModule = begin
let one = 1
let two = 2
endSimilarly, the bodies of classes, interfaces, and structs delimited by { ... }, class ... end, struct ... end, orinterface ... end may be undented to the offside line established by the type keyword. For example:
type MyNestedModule = interface
abstract P : int
end

15.2 High Precedence Application
The entry f x in the precedence table in §4.4.2 refers to a function application in which the function andargument are separated by spaces. The entry "f(x)" indicates that in expressions and patterns, identifiers thatare followed immediately by a left parenthesis without intervening whitespace form a “high precedence”
application. Such expressions are parsed with higher precedence than prefix and dot-notation operators.
Conceptually this means that
Example 1:B(e)is analyzed lexically as
Example 1: B $app (e)where $app is an internal symbol inserted by lexical analysis. We do not show this symbol in the remainder ofthis specification and simply show the original source text.
This means that the following two statements
Example 1:
Example 2:

B(e).CB (e).C

are parsed as
Example 1: (B(e)).C
Example 2: B ((e).C)
respectively.
Furthermore, arbitrary chains of method applications, property lookups, indexer lookups (.[]), field lookups, andfunction applications can be used in sequence if the arguments of method applications are parenthesized and
immediately follow the method name, with no intervening spaces. For example:
e.Meth1(arg1,arg2).Prop1.[3].Prop2.Meth2()
Although the grammar and these precedence rules technically allow the use of high-precedence application
expressions as direct arguments, an additional check prevents such use. Instead, such expressions must be
surrounded by parentheses. For example,
fe.Meth1(arg1,arg2) e.Meth2(arg1,arg2)

must be writtenf(e.Meth1(arg1,arg2)) (e.Meth2(arg1,arg2))

225

However, indexer, field, and property dot-notation lookups may be used as arguments without adding
parentheses. For example:
fe.Prop1 e.Prop2.[3]15.3 Lexical Analysis of Type Applications
The entry f<types> x in the precedence table (§4.4.2) refers to any identifier that is followed immediately by a <symbol and a sequence of all of the following:_, ,, *, ', [, ], whitespace, or identifier tokens.

A parentheses ( or < token followed by any tokens until a matching parentheses ) or > is encountered.



A final > token.

During this analysis, any token that is composed only of the > character (such as >, >>, or >>>) is treated as aseries of individual > tokens. Likewise, any token composed only of > characters followed by a period (such as>., >>., or >>>.) is treated as a series of individual > tokens followed by a period.
If such a sequence of tokens follows an identifier, lexical analysis marks the construct as a high precedence typeapplication and subsequent grammar rules ensure that the enclosed text is parsed as a type. Conceptually thismeans that
Example 1:

B<int>.C<int>(e).C

is returned as the following stream of tokens:
Example 1: B $app <int> .C $app <int>(e).C
where $app is an internal symbol inserted by lexical analysis. We do not show this symbol elsewhere in thisspecification and simply show the original source text.
The lexical analysis of type applications does not apply to the character sequence “<>”. A character sequencesuch as “< >” with intervening whitespace should be used to indicate an empty list of generic arguments.
type Foo() =member this.Value = 1
let b = new Foo< >() // valid
let c = new Foo<>() // invalid

226

Special Attributes and Types
This chapter describes attributes and types that have special significance to the F# compiler.

16.1 Custom Attributes Recognized by F#
The following custom attributes have special meanings recognized by the F# compiler. Except where indicated,
the attributes may be used in F# code, in referenced assemblies authored in F#, or in assemblies that are
authored in other CLI languages.

Attribute

Description

System.ObsoleteAttributeIndicates that the construct is obsolete and[<Obsolete(...)>]

gives a warning or error depending on thesettings in the attribute.
This attribute may be used in both F# andimported assemblies.

System.ParamArrayAttribute[<ParamArray(...)>]

When applied to an argument of a method,
indicates that the method can accept a variable
number of arguments.
This attribute may be used in both F# andimported assemblies.

System.ThreadStaticAttribute[<ThreadStatic(...)>]

Marks a mutable static value in a class as
thread static.
This attribute may be used in both F# andimported assemblies.

System.ContextStaticAttribute[<ContextStatic(...)>]

Marks a mutable static value in a class as
context static.
This attribute may be used in both F# andimported assemblies.

System.AttributeUsageAttribute[<AttributeUsage(...)>]

Specifies the attribute usage targets for an
attribute.
This attribute may be used in both F# andimported assemblies.

System.Diagnostics.ConditionalAttribute[<Conditional(...)>]

Emits code to call the method only if thecorresponding conditional compilation symbol isdefined.
This attribute may be used in both F# andimported assemblies.

Attribute

Description

System.Reflection.AssemblyInformationalVersionAttrib

Attaches additional version metadata to thecompiled form of the assembly.

ute[<AssemblyInformationalVersion(...)>]

This attribute may be used in both F# andimported assemblies.

System.Reflection.AssemblyFileVersionAttribute

Attaches file version metadata to the compiled
form of the assembly.

[<AssemblyFileVersion(...)>]

This attribute may be used in both F# andimported assemblies.
System.Reflection.AssemblyDescriptionAttribute[<AssemblyDescription(...)>]

Attaches descriptive metadata to the compiled
form of the assembly, such as the “Comments”
attribute in the Win32 version resource for theassembly.
This attribute may be used in both F# andimported assemblies.

System.Reflection.AssemblyTitleAttribute

Attaches title metadata to the compiled form of
the assembly, such as the “ProductName”
attribute in the Win32 version resource for theassembly.

[<AssemblyTitle(...)>]

This attribute may be used in both F# andimported assemblies.
System.Reflection.AssemblyCopyrightAttribute

Attaches copyright metadata to the compiled
form of the assembly, such as the“LegalCopyright” attribute in the Win32 version
resource for the assembly.

[<AssemblyCopyright(...)>]

This attribute may be used in both F# andimported assemblies.
System.Reflection.AssemblyTrademarkAttribute

Attaches trademark metadata to the compiled
form of the assembly, such as the“LegalTrademarks” attribute in the Win32
version resource for the assembly.

[<AssemblyTrademark(...)>]

This attribute may be used in both F# andimported assemblies.
System.Reflection.AssemblyCompanyAttribute

Attaches company name metadata to thecompiled form of the assembly, such as the“CompanyName” attribute in the Win32 version
resource for the assembly.

[<AssemblyCompany(...)>]

This attribute may be used in both F# andimported assemblies.

228

Attribute

Description

System.Reflection.AssemblyProductAttribute

Attaches product name metadata to thecompiled form of the assembly, such as the“ProductName” attribute in the Win32 version
resource for the assembly.

[<AssemblyProduct(...)>]

This attribute may be used in both F# andimported assemblies.
System.Reflection.AssemblyKeyFileAttributeIndicates to the F# compiler how to sign anassembly.

[<AssemblyKeyFile(...)>]

This attribute may be used in both F# andimported assemblies.
System.Reflection.DefaultMemberAttribute

When applied to a type, specifies the name of
the indexer property for that type.

[<DefaultMember(...)>]

This attribute may be used in both F# andimported assemblies.
System.Runtime.CompilerServices.InternalsVisibleToAt
tributeDirects the F# compiler to permit access to theinternals of the assembly.

[<InternalsVisibleTo(...)>]

This attribute may be used in both F# andimported assemblies.

System.Runtime.CompilerServices.TypeForwardedToAttri

Indicates a type redirection.

buteThis attribute may be used only in imported
non-F# assemblies. It is not permitted in F#
code.

[<TypeForwardedTo(...)>]

System.Runtime.CompilerServices.ExtensionAttribute[<Extension(...)>]

Indicates the compiled form of a C# extension
member.
This attribute may be used only in imported
non-F# assemblies. It is not permitted in F#
code.

System.Runtime.InteropServices.DllImportAttribute[<DllImport(...)>]

When applied to a function definition in amodule, causes the F# compiler to ignore theimplementation of the definition, and instead
compile it as a CLI P/Invoke stub declaration.
This attribute may be used in both F# andimported assemblies.

System.Runtime.InteropServices.MarshalAsAttribute[<MarshalAs(...)>]

When applied to a parameter or return type,specifies the marshalling attribute for a CLI
P/Invoke stub declaration.
This attribute may be used in both F# andimported assemblies. However, F# does notsupport the specification of "custom"
marshallers.

229

Attribute

Description

System.Runtime.InteropServices.InAttribute

When applied to a parameter, specifies the CLI
In attribute.

[<In>]

This attribute may be used in both F# andimported assemblies. However, in F# its only
effect is to change the corresponding attributein the CLI compiled form.
System.Runtime.InteropServices.OutAttribute

When applied to a parameter, specifies the CLI
Out attribute.

[<Out>]

This attribute may be used in both F# andimported assemblies. However, in F# its only
effect is to change the corresponding attributein the CLI compiled form.
System.Runtime.InteropServices.OptionalAttribute

When applied to a parameter, specifies the CLI
Optional attribute.

[<Optional(...)>]

This attribute may be used in both F# andimported assemblies. However, in F# its only
effect is to change the corresponding attributein the CLI compiled form.
System.Runtime.InteropServices.FieldOffsetAttribute[<FieldOffset(...)>]

When applied to a field, specifies the field offsetof the underlying CLI field.
This attribute may be used in both F# andimported assemblies.

System.NonSerializedAttribute

When applied to a field, sets the "not serialized"
bit for the underlying CLI field.

[<NonSerialized>]

This attribute may be used in both F# andimported assemblies.
System.Runtime.InteropServices.StructLayoutAttribute

Specifies the layout of a CLI type.

[<StructLayout(...)>]

This attribute may be used in both F# andimported assemblies.

Microsoft.FSharp.Core.AutoSerializableAttribute

When added to a type with value false, disables[<AutoSerializable(false)>]default serialization, so that F# does not makethe type serializable.
This attribute should be used only in F#
assemblies.

230

Attribute

Description

Microsoft.FSharp.Core.AutoOpenAttribute

When applied to an assembly and given a stringargument, causes the namespace or module tobe opened automatically when the assembly isreferenced.

[<AutoOpen>]

When applied to a module without a stringargument, causes the module to be opened
automatically when the enclosing namespace or
module is opened.
This attribute should be used only in F#
assemblies.
Microsoft.FSharp.Core.

Adjusts the runtime representation of a type .

CompilationRepresentationAttribute

This attribute should be used only in F#
assemblies.

[<CompilationRepresentation(...)>]
Microsoft.FSharp.Core.CompiledNameAttribute

Changes the compiled name of an F# language
construct.

[<CompiledName(...)>]

This attribute should be used only in F#
assemblies.
Microsoft.FSharp.Core.CustomComparisonAttribute[<CustomComparison>]

When applied to an F# structural type, indicatesthat the type has a user-specified comparison
implementation.
This attribute should be used only in F#
assemblies.

Microsoft.FSharp.Core.CustomEqualityAttribute[<CustomEquality>]

When applied to an F# structural type, indicatesthat the type has a user-defined equality
implementation.
This attribute should be used only in F#
assemblies.

Microsoft.FSharp.Core.DefaultAugmentationAttribute[<DefaultAugmentation(...)>]

When applied to an F# discriminated union typewith value false, turns off the generation ofstandard helper member tester, constructor andaccessor members for the generated CLI classfor that type.
This attribute should be used only in F#
assemblies.

Microsoft.FSharp.Core.DefaultValueAttribute

When added to a field declaration, specifies thatthe field is not initialized. During type checking,a constraint is asserted that the field typesupports null. If the argument to the attribute isfalse, the constraint is not asserted.

[<DefaultValue(...)>]

This attribute should be used only in F#
assemblies.

231

Attribute

Description

Microsoft.FSharp.Core.GeneralizableValueAttribute

When applied to an F# value, indicates thatuses of the attribute can result in generic code
through the process of type inference. For
example, Set.empty. The value must typicallybe a type function whose implementation has
no observable side effects.

[<GeneralizableValue>]

This attribute should be used only in F#
assemblies.
Microsoft.FSharp.Core.LiteralAttribute

When applied to a value, compiles the value asa CLI literal.

[<Literal>]This attribute should be used only in F#
assemblies.
Microsoft.FSharp.Core.NoDynamicInvocationAttribute

When applied to an inline function or member
definition, replaces the generated code with astub that throws an exception at runtime. This
attribute is used to replace the default
generated implementation of unverifiable inline
members with a verifiable stub.

[<NoDynamicInvocation>]

This attribute should be used only in F#
assemblies.
Microsoft.FSharp.Core.CompilerMessageAttribute[<CompilerMessage(...)>]

When applied to an F# construct, indicates that
the F# compiler should report a message when
the construct is used.
This attribute should be used only in F#
assemblies.

Microsoft.FSharp.Core.StructAttributeIndicates that a type is a struct type.

[<Struct>]This attribute should be used only in F#
assemblies.

Microsoft.FSharp.Core.ClassAttributeIndicates that a type is a class type.

[<Class>]

This attribute should be used only in F#
assemblies.

Microsoft.FSharp.Core.InterfaceAttributeIndicates that a type is an interface type.

[<Interface>]

This attribute should be used only in F#
assemblies.

Microsoft.FSharp.Core.MeasureAttributeIndicates that a type or generic parameter is aunit of measure definition or annotation.

[<Measure>]

This attribute should be used only in F#
assemblies.

232

Attribute

Description

Microsoft.FSharp.Core.ReferenceEqualityAttribute

When applied to an F# record or union type,indicates that the type should use reference
equality for its default equality implementation.

[<ReferenceEquality>]This attribute should be used only in F#
assemblies.
Microsoft.FSharp.Core.ReflectedDefinitionAttribute

Makes the quotation form of a definition[<ReflectedDefinition>]

available at runtime through theMicrosoft.FSharp.Quotations.
Expr.GetReflectedDefinition method.
This attribute should be used only in F#
assemblies.

Microsoft.FSharp.Core.

When applied to an F# module, warns if an
attempt is made to open the module name.

RequireQualifiedAccessAttribute

When applied to an F# union or record type,indicates that the field labels or union cases
must be referenced by using a qualified paththat includes the type name.

[<RequireQualifiedAccess>]

This attribute should be used only in F#
assemblies.
Microsoft.FSharp.Core.

When applied to an F# function or method,
indicates that the function or method must be
invoked with explicit type arguments, such as
typeof<int>.

RequiresExplicitTypeArgumentsAttribute[<RequiresExplicitTypeArguments>]

This attribute should be used only in F#
assemblies.
Microsoft.FSharp.Core.StructuralComparisonAttribute[<StructuralComparison>]

When added to a record, union, exception, or
structure type, confirms the automatic
generation of implementations for IComparable
for the type.
This attribute should only be used in F#
assemblies.

Microsoft.FSharp.Core.StructuralEqualityAttribute[<StructuralEquality>]

When added to a record, union, or struct type,confirms the automatic generation of overrides
for Equals and GetHashCode for the type.
This attribute should be used only in F#
assemblies.

Microsoft.FSharp.Core.VolatileFieldAttribute

When applied to an F# field or mutable value
definition, controls whether the CLI volatile[<VolatileField>]

prefix is emitted before accesses to the field.
This attribute should be used only in F#
assemblies.

233

16.2 Custom Attributes Emitted by F#
The F# compiler can emit the following custom attributes:
Attribute

Description

System.Diagnostics.DebuggableAttribute

Improves debuggability of F# code.

System.Diagnostics.DebuggerHiddenAttribute

Improves debuggability of F# code.

System.Diagnostics.DebuggerDisplayAttribute

Improves debuggability of F# code.

System.Diagnostics.DebuggerBrowsableAttribute

Improves debuggability of F# code.

System.Runtime.CompilerServices.

Enables extra JIT optimizations.

CompilationRelaxationsAttribute
System.Runtime.CompilerServices.

Indicates that a method, type, or property
is generated by the F# compiler, anddoes not correspond directly to user
source code.

CompilerGeneratedAttribute

System.Reflection.DefaultMemberAttribute

Specifies the name of the indexer
property for a class.

Microsoft.FSharp.Core.CompilationMappingAttributeIndicates how a CLI constructcorresponds to an F# source language
construct.

Microsoft.FSharp.Core.FSharpInterfaceDataVersionAttribute

Defines the schema number for theembedded binary resource for F#-specificinterface and optimization data.

Microsoft.FSharp.Core.OptionalArgumentAttributeIndicates optional arguments to F#
members.

16.3 Custom Attributes Not Recognized by F#
The following custom attributes are defined in some CLI implementations and may appear to be relevant to F#.
However, they either do not affect the behavior of the F# compiler, or result in an error when used in in F# code.
Attribute

Description

System.Runtime.CompilerServices.DecimalConstantAttribute

The F# compiler ignores this attribute.
However, if used in F# code, it cancause some other CLI languages tointerpret a decimal constant as acompile-time literal.

System.Runtime.CompilerServices.RequiredAttributeAttribute

Do not use this attribute in F# code.
The F# compiler ignores it or returns anerror.

System.Runtime.InteropServices.

Do not use this attribute in F# code.
The F# compiler ignores it or returns anerror.

DefaultParameterValueAttribute234

Attribute

Description

System.Runtime.InteropServices.

Do not use this attribute in F# code.

UnmanagedFunctionPointerAttribute

The F# compiler ignores it or returns anerror.

System.Runtime.CompilerServices.FixedBufferAttribute

Do not use this attribute in F# code.
The F# compiler ignores it or returns anerror.

System.Runtime.CompilerServices.UnsafeValueTypeAttribute

Do not use this attribute in F# code.
The F# compiler ignores it or returns anerror.

System.Runtime.CompilerServices.SpecialNameAttribute

Do not use this attribute in F# code.
The F# compiler ignores it or returns anerror.

16.4 Exceptions Thrown by F# Language Primitives
Certain F# language and primitive library operations throw the following exceptions.
Attribute

Description

System.ArithmeticException

An arithmetic operation failed. This is the base class for exceptions
such as System.DivideByZeroException and
System.OverflowException.

System.ArrayTypeMismatchException

An attempt to store an element in an array failed because the runtime
type of the stored element is incompatible with the runtime type of thearray.

System.DivideByZeroException

An attempt to divide an integral value by zero occurred.

System.IndexOutOfRangeException

An attempt to index an array failed because the index is less thanzero or outside the bounds of the array.

System.InvalidCastException

An explicit conversion from a base type or interface to a derived type
failed at run time.

System.NullReferenceException

A null reference was used in a way that caused the referenced object
to be required.

System.OutOfMemoryException

An attempt to use new to allocate memory failed.

System.OverflowException

An arithmetic operation in a checked context overflowed.

System.StackOverflowException

The execution stack was exhausted because of too many pending
method calls, which typically indicates deep or unbounded recursion.

System.TypeInitializationException

F# initialization code for a type threw an exception that was notcaught.

235

The F# Library FSharp.Core.dll
All compilations reference the following two base libraries:

The CLI base library mscorlib.dll.



The F# base library FSharp.Core.dll

The following namespaces are automatically opened for all F# code:
open Microsoft.FSharp
open Microsoft.FSharp.Core
open Microsoft.FSharp.Core.LanguagePrimitives
open Microsoft.FSharp.Core.Operators
open Microsoft.FSharp.Text
open Microsoft.FSharp.Collections
open Microsoft.FSharp.Core.ExtraTopLevelOperators
A compilation may open additional namespaces may be opened if the referenced F# DLLs contain
AutoOpenAttribute declarations.
See also the online documentation at http://msdn.microsoft.com/library/ee353567.aspx.

17.1 Basic Types (Microsoft.FSharp.Core)
This section provides details about the basic types that are defined in Microsoft.FSharp.Core.

17.1.1 Basic Type Abbreviations
Type Name

Description

obj

System.Object

exn

System.Exception

nativeint

System.IntPtr

unativeint

System.UIntPtr

string

System.String

float32, single

System.Single

float, double

System.Double

sbyte, int8

System.SBytebyte, uint8

System.Byte

int16

System.Int16

uint16

System.UInt16

int32, int

System.Int32

uint32

System.UInt32

int64

System.Int64

Type Name

Description

uint64

System.UInt64

char

System.Char

bool

System.Boolean

decimalSystem.Decimal

17.1.2 Basic Types that Accept Unit of Measure Annotations
Type Name

Description

sbyte<_>

Underlying representation System.SByte, but accepts a unit of measure.

int16<_>

Underlying representation System.Int16, but accepts a unit of measure.

int32<_>

Underlying representation System.Int32, but accepts a unit of measure.

int64<_>

Underlying representation System.Int64, but accepts a unit of measure.

float32<_>

Underlying representation System.Single, but accepts a unit of measure.

float<_>

Underlying representation System.Double, but accepts a unit of measure.

decimal<_>Underlying representation System.Decimal, but accepts a unit of measure.

17.1.3 The nativeptr<_> Type
When the nativeptr<type> is used in method argument or return position, it is represented in compiled CIL
code as either:

A CLI pointer type type*, if type does not contain any generic type parameters.



T CLI type System.IntPtr otherwise.
Note: CLI pointer types are rarely used. In CLI metadata, pointer types sometimes appear in CLI
metadata unsafe object constructors for the CLI type System.String.
You can convert between System.UIntPtr and nativeptr<'T> by using the inlined unverifiable
functions in Microsoft.FSharp.NativeInterop.NativePtr.
nativeptr<_> compiles in different ways because CLI restricts where pointer types can appear.

17.2 Basic Operators and Functions(Microsoft.FSharp.Core.Operators)
17.2.1 Basic Arithmetic Operators
The following operators are defined in Microsoft.FSharp.Core.Operators:
Operator or Function
Name

Expression Form

Description

(+)

x + y

Overloaded addition.

238

Operator or Function
Name

Expression Form

Description

(-)

x - y

Overloaded subtraction.

(*)

x * y

Overloaded multiplication.

(/)

x / y

Overloaded division.
For negative numbers, the behavior of this operator follows
the definition of the corresponding operator in the C#
specification.

(%)

x % y

Overloaded remainder.
For integer types, the result of x % y is the value produced
by x – (x / y) * y. If y is zero,
System.DivideByZeroException is thrown. The
remainder operator never causes an overflow. This follows
the definition of the remainder operator in the C#
specification.
For floating-point types, the behavior of this operator also
follows the definition of the remainder operator in the C#
specification.

(~-)

-x

Overloaded unary negation.

not

not x

Boolean negation.

17.2.2 Generic Equality and Comparison Operators
The following operators are defined in Microsoft.FSharp.Core.Operators:
Operator or Function
Name

Expression Form

Description

(<)

x < y

Generic less-than(<=)

x <= yGeneric less-than-or-equal

(>)

x > y

Generic greater-than(>=)

x >= yGeneric greater-than-or-equal

(=)

x = y

Generic equality(<>)

x <> yGeneric disequality

max

max x yGeneric maximummin

min x yGeneric minimum

17.2.3 Bitwise Operators
The following operators are defined in Microsoft.FSharp.Core.Operators:
Operator or Function
Name

Expression Form

Description

239

Operator or Function
Name

Expression Form

Description(<<<)

x <<< yOverloaded bitwise left-shift(>>>)

x >>> yOverloaded bitwise arithmetic right-shift(^^^)

x ^^^ yOverloaded bitwise exclusive or (XOR)(&&&)

x &&& yOverloaded bitwise and(|||)

x ||| yOverloaded bitwise or

(~~~)

~~~x

Overloaded bitwise negation

17.2.4 Math Operators
The following operators are defined in Microsoft.FSharp.Core.Operators:
Operator or Function
Name

Expression Form

Description

abs

abs x

Overloaded absolute value

acos

acos xOverloaded inverse cosine

asin

asin xOverloaded inverse sineatan

atan xOverloaded inverse tangent

atan2

atan2 x y

Overloaded inverse tangent of x/y

ceil

ceil xOverloaded floating-point ceiling

cos

cos x

Overloaded cosinecoshcosh xOverloaded hyperbolic cosine

exp

exp x

Overloaded exponent

floor

floor xOverloaded floating-point floor

log

log x

Overloaded natural logarithm

log10

log10 xOverloaded base-10 logarithm(**)

x ** yOverloaded exponential

pown

pown x y

Overloaded integer exponential

round

round xOverloaded rounding

sign

sign xOverloaded sign function

sin

sin x

Overloaded sine functionsinh

sinh xOverloaded hyperbolic sine function

sqrt

sqrt xOverloaded square root function

tan

tan x

Overloaded tangent functiontanhtanh xOverloaded hyperbolic tangent function240

17.2.5 Function Pipelining and Composition Operators
The following operators are defined in Microsoft.FSharp.Core.Operators:
Operator/Function
Name

Expression Form

Description(|>)

x |> fPipelines the value x to the function f (forward pipelining)(>>)

f >> gComposes two functions, so that they are applied in orderfrom left to right(<|)

f <| xPipelines the value x to the function f (backward pipelining)(<<)

g << fComposes two functions, so that they are applied in orderfrom right to left (backward function composition)

ignoreignore x

Computes and discards a value

17.2.6 Object Transformation Operators
The following operators are defined in Microsoft.FSharp.Core.Operators:
Operator/Function
Name

Expression Form

Description

box

box x

Converts to object representation.

hashhash xGenerates a hash value.

sizeofsizeof<type>Computes the size of a value of the given type.

typeoftypeof<type>Computes the System.Type representation of the given
type.

typedefof

typedefof<type>Computes the System.Type representation of type andcalls GetGenericTypeDefinition if it is a generic type.

unbox

unbox xConverts from object representation.

ref

ref x

Allocates a mutable reference cell.

(!)

!x

Reads a mutable reference cell.

17.2.7 Pair Operators
The following operators are defined in Microsoft.FSharp.Core.Operators:
Operator or Function
Name

Expression Form

Description

fst

fst p

Returns the first element of a pair.

snd

snd p

Returns the second element of a pair17.2.8 Exception Operators
The following operators are defined in Microsoft.FSharp.Core.Operators:

241

Operator/Function
Name

Expression Form

Description

failwith

failwith x

Raises a FailureException exception.

invalidArg

invalidArg x

Raises an ArgumentException exception.

raise

raise xRaises an exception.

reraisereraise()Rethrows the current exception.

17.2.9 Input/Output Handles
The following operators are defined in Microsoft.FSharp.Core.Operators:
Operator or Function
Name

Expression Form

Description

stdin

Stdin

Computes System.Console.In.

stdout

StdoutComputes System.Console.Out.

stderrStderrComputes System.Console.Error.

17.2.10Overloaded Conversion Functions

The following operators are defined in Microsoft.FSharp.Core.Operators:
Operator or Function
Name

Expression Form

Description

byte

byte xOverloaded conversion to a byte

sbyte

sbyte xOverloaded conversion to a signed byte

int16

int16 xOverloaded conversion to a 16-bit integer

uint16

uint16 x

Overloaded conversion to an unsigned 16-bit integer

int32, int

int32 xOverloaded conversion to a 32-bit integer

int x
uint32

uint32 x

Overloaded conversion to an unsigned 32-bit integer

int64

int64 xOverloaded conversion to a 64-bit integer

uint64

uint64 x

Overloaded conversion to an unsigned 64-bit integer

nativeint

nativeint x

Overloaded conversion to an native integer

unativeint

unativeint x

Overloaded conversion to an unsigned native integer

float, double

float xOverloaded conversion to a 64-bit IEEE floating-point
numberdouble x
float32, single

float32 x
single x

decimaldecimal x

Overloaded conversion to a 32-bit IEEE floating-point
numberOverloaded conversion to a System.Decimal number

242

Operator or Function
Name

Expression Form

Description

char

char xOverloaded conversion to a System.Char value

enum

enum xOverloaded conversion to a typed enumeration value

17.3 Checked Arithmetic Operators
The module Microsoft.FSharp.Core.Operators.Checked defines runtime-overflow-checked versions of thefollowing operators:
Operator or Function
Name

Expression Form

Description

(+)

x + y

Checked overloaded addition

(-)

x – y

Checked overloaded subtraction

(*)

x * y

Checked overloaded multiplication

(~-)

-x

Checked overloaded unary negation

byte

byte xChecked overloaded conversion to a byte

sbyte

sbyte xChecked overloaded conversion to a signed byte

int16

int16 xChecked overloaded conversion to a 16-bit integer

uint16

uint16 x

Checked overloaded conversion to an unsigned 16-bit
integer

int32, int

int32 xChecked overloaded conversion to a 32-bit integer

int x
uint32

uint32 x

Checked overloaded conversion to an unsigned 32-bit
integer

int64

int64 xChecked overloaded conversion to a 64-bit integer

uint64

uint64 x

Checked overloaded conversion to an unsigned 64-bit
integernativeint

nativeint x

Checked overloaded conversion to an native integer

unativeint

unativeint x

Checked overloaded conversion to an unsigned native
integerchar

char xChecked overloaded conversion to a System.Char value

24317.4 List and Option Types
17.4.1 The List TypeThe following shows the elements of the F# type Microsoft.FSharp.Collections.list referred to in thisspecification:
type 'T list =| ([])| (::) of 'T * 'T list
static member Empty : 'T list
member Length : int
member IsEmpty : boolmember Head : 'T
member Tail : 'T list
member Item :int -> 'T with getstatic member Cons : 'T * 'T list -> 'T list
interface System.Collections.Generic.IEnumerable<'T>
interface System.Collections.IEnumerable

17.4.2 The Option TypeThe following shows the elements of the F# type Microsoft.FSharp.Core.option referred to in thisspecification:[<DefaultAugmentation(false)>][<CompilationRepresentation(CompilationRepresentationFlags.UseNullAsTrueValue)>]type 'T option =| None| Some of 'T
static member None : 'T option
static member Some : 'T -> 'T option[<CompilationRepresentation(CompilationRepresentationFlags.Instance)>]member Value : 'T
member IsSome : boolmember IsNone : bool

17.5 Lazy Computations (Lazy)See http://msdn.microsoft.com/library/ee353813.aspx

17.6 Asynchronous Computations (Async)
See http://msdn.microsoft.com/library/ee370232.aspx

17.7 Agents (MailboxProcessor)
See http://msdn.microsoft.com/library/ee370357.aspx

244

17.8 Event Types
See http://msdn.microsoft.com/library/ee370608.aspx

17.9 Immutable Collection Types (Map, Set)
See http://msdn.microsoft.com/library/ee353413.aspx

17.10

Text Formatting (Printf)

See http://msdn.microsoft.com/library/ee370560.aspx

17.11

Reflection

See http://msdn.microsoft.com/library/ee353491.aspx

17.12

Quotations

See http://msdn.microsoft.com/library/ee370558.aspx

17.13

Native Pointer Operations

The Microsoft.FSharp.Core.NativeIntrop namespace contains functionality for interoperating with nativecode.
Use of these functions is unsafe, and incorrect use may generate invalid IL code.
Operator or Function
Name

Description

NativePtr.ofNativeInt

Returns a typed native pointer for a machine address.

NativePtr.toNativeInt

Returns a machine address for a typed native pointer.

NativePtr.add

Computes an indexed offset from the input pointer.

NativePtr.read

Reads the memory that the input pointer references.

NativePtr.write

Writes to the memory that the input pointer references.

NativePtr.get

Reads the memory at an indexed offset from the input pointer.

NativePtr.set

Writes the memory at an indexed offset from the input pointer.

NativePtr.stackalloc

Allocates a region of memory on the stack.

245

17.13.1

Stack Allocation

The NativePtr.stackalloc function works as follows. Given
stackalloc<ty> n
the unmanaged type ty specifies the type of the items that will be stored in the newly allocated location, and nindicates the number of these items. Taken together, these establish the required allocation size.
The stackalloc function allocates n * sizeof<ty> bytes from the call stack and returns a pointer of typenativeptr<ty> to the newly allocated block. The content of the newly allocated memory is undefined. If n is anegative value, the behavior of the function is undefined. If n is zero, no allocation is made, and the returned
pointer is implementation-defined. If insufficient memory is available to allocate a block of the requested size, theSystem.StackOverflowException is thrown.
Use of this function is unsafe, and incorrect use might generate invalid IL code. For example, the function should
not be used in with or finally blocks in try/with or try/finally expressions. These conditions are notchecked by the F# compiler, because this primitive is rarely used from F# code.
There is no way to explicitly free memory that is allocated using stackalloc. All stack-allocated memory blocks
that are created during the execution of a function or member are automatically discarded when that function ormember returns. This behavior is similar to that of the alloca function, an extension commonly found in C andC++ implementations.

246

Features for ML Compatibility
F# has its roots in the Caml family of programming languages and its core constructs are similar to some otherML-family languages. As a result, F# supports some constructs for compatibility with other implementations of
ML-family languages.

18.1 Conditional Compilation for ML Compatibility
F# supports the following constructs for conditional compilation:
token start-fsharp-token = "(*IF-FSHARP" | "(*F#"
token end-fsharp-token = "ENDIF-FSHARP*)" | "F#*)"
token start-ml-token = "(*IF-OCAML*)"token end-ml-token = "(*ENDIF-OCAML*)"F# ignores the start-fsharp-token and end-fsharp-token tokens. This means that sections marked(*IF-FSHARP... ENDIF-FSHARP*)

—or—
(*F#

... F#*)are included during tokenization when compiling with the F# compiler. The intervening text is tokenized andreturned in the token stream as normal.
In addition, the start-ml-token token is discarded and the following text is tokenized as string, _ (anycharacter), and end-ml-token until an end-ml-token is reached. Comments are not treated as special during
this process and are simply processed as “other text”. This means that text surrounded by the following is
excluded when compiling with the F# compiler:(*IF-CAML*) ... (*ENDIF-CAML*)or (*IF-OCAML*) ... (*ENDIF-OCAML*)
The intervening text is tokenized as “strings and other text” and the tokens are discarded until the corresponding
end token is reached. Comments are not treated as special during this process and are simply processed as“other text.”
The converse holds when programs are compiled using a typical ML compiler.

18.2 Extra Syntactic Forms for ML Compatibility
The following identifiers are also keywords primarily because they are keywords in OCaml. Although F# reserves
several OCaml keywords for future use, the /mlcompatibility option enables the use of these keywords as
identifiers.
token ocaml-ident-keyword =asr land lor lsl lsr lxor modNote: In F# the following alternatives are available. The precedence of these operators differs fromthe precedence that OCaml uses.
asr
land
lor
lsl
lsr
lxor
mod
sig

>>> (on signed type)
&&&
|||
<<<
>>> (on unsigned type)^^^%
begin (that is, begin/end may be used instead of sig/end)

F# includes the following additional syntactic forms for ML compatibility:
expr :=| ...
| expr.(expr)| expr.(expr) <- expr// array lookup// array assignment

type :=| ...
| (type,...,type) long-ident // generic type instantiation
module-implementation :=| ...
| module ident = struct ... end
module-signature :=| ...
| module ident : sig ... end
An ML compatibility warning occurs when these constructs are used.
Note that the for-expression form for var = expr1 downto expr2 do expr3 is also permitted for ML
compatibility.
The following expression forms
expr :=| …
| expr.(expr)| expr.(expr) <- expr// array lookup// array assignment

Are equivalent to the following uses of library-defined operators:
e1.(e2)e1.(e2) <- e3→ (.()) e1 e2→ (.()<-) e1 e2 e3

18.3 Extra Operators
F# defines the following two additional shortcut operators:
e1 or e2
e1 & e2→ (or) e1 e2→ (&) e1 e2

18.4 File Extensions and Lexical Matters
F# supports the use of the.ml and .mli extensions on the command line. The “indentation awareness off” syntax
option is implicitly enabled when using either of these filename extensions.

248

Lightweight syntax can be explicitly disabled in .fs, .fsi, .fsx, and .fsscript files by specifying #indent"off" as the first declaration in a file:#indent "off"When lightweight syntax is disabled, whitespace can include tab characters:
regexp whitespace = [ ' ' '\t' ]+Appendix A: F# Grammar Summary
This appendix summarizes the grammar of the F# language. The following table describes the notation
conventions used in the grammar.

Notation Conventions in Grammar Rules
Notation

Description

Exampleelement-nameopt

The opt subscript indicates that element-name

let recopt

is optional.
...

An ellipsis indicates that the preceding nonterminal construct and the separator token can
repeat any number of times.

expr ',' ... ',' expr

keywordBoldface type identifies a language keyword
that must appear verbatim.

module long-ident module-element-nameItalics identify an element that is defined in thegrammar.

script-fragment :module-elems[ char1 – char2 ]All ASCII characters in the range from char1 to[ a – z ]elems

char2, inclusive.
[ ^ char1 – char2 ]All ASCI characters except those in thespecified range.

[ ^ A – Z ]‘symbol’ or “symbol”

The literal symbol is used in the grammar.

'(', "if"(spec)

Parentheses enclose required individual
grammar elements.

(+|-)$token

Lexical analysis inserts $token as a hidden$app

symbol.

A.1 Lexical Grammar
A.1.1 Whitespace
whitespace : ' '+newline :'\n''\r' '\n'249

whitespace-or-newline :whitespace
newlineA.1.2 Comments
block-comment-start : "(*"
block-comment-end : "*)"
end-of-line-comment : "//" [^'\n' '\r']*A.1.3 Conditional Compilation
if-directive : "#if" whitespace ident-text
else-directive : "#else"
endif-directive : "#endif"

A.1.4 Identifiers and Keywords
A.1.4.1 Identifiers
digit-char : [0-9]letter-char :
'\Lu'
'\Ll'
'\Lt'
'\Lm'
'\Lo'
'\Nl'
connecting-char : '\Pc'
combining-char :
'\Mn'
'\Mc'
formatting-char : '\Cf'
ident-start-char :letter-char_
ident-char :letter-chardigit-charconnecting-charcombining-charformatting-char'_
ident-text : ident-start-char ident-char*
ident :
ident-text`` [^ '\n' '\r' '\t']+[^ '\n' '\r' '\t'] ``250

A.1.4.2 Long Identifiers
long-ident : ident '.' ... '.' ident
long-ident-or-op :long-ident '.' ident-or-op
ident-or-op

A.1.4.3 Keywords
ident-keyword : one ofabstract and as assert base begin class default delegate do done
downcast downto elif else end exception extern false finally forfun function global if in inherit inline interface internal lazy let
match member module mutable namespace new null of open or
override private public rec return sig static struct then totrue try type upcast use val void when while with yieldreserved-ident-keyword : one ofatomic break checked component const constraint constructor
continue eager fixed fori functor include
measure method mixin object parallel params process protected purerecursive sealed tailcall trait virtual volatilereserved-ident-formats :ident-text ( '!' | '#')

A.1.4.4 Symbolic Keywords
symbolic-keyword : one oflet! use! do! yield! return!
| -> <- . : ( ) [ ] [< >] [| |] { }' # :?> :? :> .. :: := ;; ; =_ ? ?? (*) <@ @> <@@ @@>reserved-symbolic-sequence :~ `A.1.5 Strings and Characters
escape-char :'\' ["\'ntbr]

non-escape-chars :'\' [^"\'ntbr]

simple-char-char : any char except'\n' '\t' '\r' '\b' ' \ "unicodegraph-short : '\' 'u' hexdigit hexdigit hexdigit hexdigit
unicodegraph-long :'\' 'U' hexdigit hexdigit hexdigit hexdigit
hexdigit hexdigit hexdigit hexdigit

char-char :simple-char-charescape-char
trigraph
unicodegraph-shortstring-char :simple-string-charescape-charnon-escape-chars
trigraph251

unicodegraph-shortunicodegraph-longnewlinestring-elem :string-char'\' newline whitespace* string-elemchar : ' char-char 'string : " string-char* "verbatim-string-char :simple-string-charnon-escape-chars
newline\""verbatim-string : @" verbatim-string-char* "bytechar:' simple-or-escape-char 'B

bytearray:" string-char* "Bverbatim-bytearray : @" verbatim-string-char* "B
simple-or-escape-char :escape-char
simple-char
simple-char : any char except
newline, return, tab, backspace,',\,"

A.1.6 Numeric Literals
digit: [0-9]hexdigit
digit
[A-F]
[a-f]

:

octaldigit : [0-7]bitdigit : [0-1]int : digit+
xint :
int
0 (x|X) hexdigit+
0 (o|O) octaldigit+
0 (b|B) bitdigit+
sbyte : xint 'y'
byte: xint 'uy'

int16 : xint 's'
252uint16 : xint 'us'
int32 : xint 'l'uint32 :xint 'ul'
xint 'u'nativeint : xint 'n'unativeint : xint 'un'int64 : xint 'L'
uint64 :xint 'UL'
xint 'uL'
ieee32 :float [Ff]xint 'lf'
ieee64 :float
xint 'LF'
bignum : int ('Q' | 'R' | 'Z' | 'I' | 'N' | 'G')
decimal : (float|int) [Mm]float :
digit+ . digit*
digit+ (. digit* )? (e|E) (+|-)? digit+
reserved-literal-formats :(xint | ieee32 | ieee64) ident-char+

A.1.7 Line Directives
line-directive :# int# int string# int verbatim-string#line int
#line int string#line int verbatim-string

A.1.8 Identifier Replacements__SOURCE_DIRECTORY____SOURCE_FILE____LINE__

253

A.1.9 Operators
A.1.9.1 Operator Names
ident-or-op :ident( op-name )(*)
op-name :symbolic-op
range-op-nameactive-pattern-op-namerange-op-name :..
.. ..
active-pattern-op-name :| ident | ... | ident || ident | ... | ident | _ |A.1.9.2 Symbolic Operators
first-op-char : one of!%&*+-./<=>@^|~
op-char :first-op-char?
quote-op-left :<@ <@@
quote-op-right :@> @@>
symbolic-op:?
?<first-op-char op-char*quote-op-leftquote-op-rightA.1.9.3 Infix and Prefix Operators
The OP marker represents all symbolic-op tokens that begin with the indicated prefix, except for tokens thatappear elsewhere in the table.
infix-or-prefix-op : one of+, -, +., -., %, &, &&prefix-op :infix-or-prefix-op~ ~~ ~~~(and any repetitions of ~)!OP(all tokens that begin with ! except !=)infix-op :infix-or-prefix-op-OP +OP || <OP >OP = |OP &OP ^OP *OP /OP %OP !=(or any of these preceded by one or more ‘.’)
:=
254

::
$
or
?

A.1.9.4 Constants
const :
sbyte
int16
int32
int64
byte
uint16
uint32
int
uint64
ieee32
ieee64bignumchar
stringverbatim-string
bytestring
verbatim-bytearray
bytechar
false
true
()
sbyte < measure-literal >int16 < measure-literal >int32 < measure-literal >int64 < measure-literal >ieee32 < measure-literal >ieee64 < measure-literal >decimal < measure-literal >A.2 Syntactic Grammar
In general, this syntax summary describes full syntax. By default, however, .fs, .fsi, .fsx, and .fsscript files
support lightweight syntax, in which indentation replaces begin/end and done tokens. This appendix uses
beginopt, endopt, and doneopt to indicate that these tokens are omitted in lightweight syntax. Complete rules forlightweight syntax appear in §15.1.
To disable lightweight syntax:#indent "off"When lightweight syntax is disabled, whitespace can include tab characters:
whitespace : [ ' ' '\t' ]+A.2.1 Program Format
implementation-file :namespace-decl-group ... namespace-decl-groupnamed-module
anonynmous-module

255

script-file : implementation-filesignature-file:namespace-decl-group-signature ... namespace-decl-group-signature
anonynmous-module-signature
named-module-signature
named-module : module long-ident module-elems
anonymous-module : module-elems
named-module-signature : module long-ident module-signature-elements
anonymous-module-signature : module-signature-elements
script-fragment : module-elems

A.2.1.1 Namespaces and Modules
namespace-decl-group :namespace long-ident module-elems
namespace global module-elems
module-defn : attributesopt module accessopt ident = beginopt module-defn-body endop
module-defn-body : begin module-elemsopt end
module-elem :module-function-or-value-defn
type-defns
exception-defn
module-defn
module-abbrev
import-decl
compiler-directive-decl
module-function-or-value-defn :attributesopt let function-defn
attributesopt let value-defn
attributesopt let recopt function-or-value-defns
attributesopt do expr
import-decl : open long-ident
module-abbrev : module ident = long-ident
compiler-directive-decl : # ident string ... string
module-elems : module-elem ... module-elem
access :privateinternal
publicA.2.1.2 Namespace and Module Signatures
namespace-decl-group-signature : namespace long-ident module-signature-elements
module-signature : module ident = beginopt module-signature-body endopt

256

module-signature-element :val mutableopt curried-sig
val value-defn
type type-signatures
exception exception-signature
module-signature
module-abbrev
import-decl
module-signature-elements :beginopt module-signature-element ... module-signature-element endopt
module-signature-body : begin module-signature-elements end
type-signature :abbrev-type-signaturerecord-type-signature
union-type-signatureanon-type-signatureclass-type-signature
struct-type-signatureinterface-type-signatureenum-type-signaturedelegate-type-signature
type-extension-signature
type-signatures : type-signature ... and ... type-signature
type-signature-element :attributesopt accessopt new : uncurried-sig
attributesopt member accessopt member-sig
attributesopt abstract accessopt member-sig
attributesopt override member-sig
attributesopt default member-sig
attributesopt static member accessopt member-sig
interface typeabbrev-type-signature : type-name '=' typeunion-type-signature : type-name '=' union-type-cases type-extension-elements-signatureopt
record-type-signature :type-name '=' '{' record-fields '}' type-extension-elements-signatureopt
anon-type-signature : type-name '=' begin type-elements-signature end
class-type-signature : type-name '=' class type-elements-signature end
struct-type-signature : type-name '=' struct type-elements-signature end
interface-type-signature : type-name '=' interface type-elements-signature end
enum-type-signature : type-name '=' enum-type-cases
delegate-type-signature :type-name '=' delegate-sig

type-extension-signature : type-name type-extension-elements-signature
type-extension-elements-signature : with type-elements-signature end
257

A.2.2 Types and Type Constraints
type :( type )type -> type
type * ... * typetypar
long-ident
long-ident<types>
long-ident< >type long-ident
type[ , ... , ]type lazy
type typar-defns
typar :> type
#type
types :

type, ..., typeatomic-type :type : one of#type typar ( type ) long-ident long-ident<types>)
typar :_
'ident^identconstraint :typar :> typetypar : nullstatic-typars : (member-sig )typar : (new : unit -> 'T)
typar : structtypar : not structtypar : enum<type>typar : unmanaged
typar : delegate<type, type>typar-defn : attributesopt typar
typar-defns: < typar-defn, ..., typar-defn typar-constraintsopt >typar-constraints : when constraint and ... and constraint
static-typars :^ident(^ident or ... or ^ident)

A.2.2.1 Equality and Comparison Constraints
typar : equality
typar : comparison

A.2.3 Expressions
expr :
const( expr )begin expr end

258

long-ident-or-op
expr '.' long-ident-or-op
expr expr
expr(expr)
expr<types>
expr infix-op expr
prefix-op expr
expr.[expr]
expr.[slice-range]
expr.[slice-range, slice-range]
expr <- expr
expr , ... , expr
new type expr
{ new base-call object-members interface-impls }{ field-initializers }{ expr with field-initializers }[ expr ; ... ; expr ][| expr ; ... ; expr |]expr { comp-or-range-expr }[ comp-or-range-expr][| comp-or-range-expr |]lazy expr
null
expr : type
expr :> type
expr :? type
expr :?> typeupcast expr
downcast expr
In the following four expression forms, the in token is optional if expr appears on a subsequent line and isaligned with the let token.
let function-defn in expr
let value-defn in expr
let rec function-or-value-defns in expr
use ident = expr in expr
fun argument-pats -> expr
function rules
match expr with rules
try expr with rules
try expr finally expr
if expr then expr elif-branchesopt else-branchopt
while expr do expr doneopt
for ident = expr to expr do expr doneopt
for pat in expr-or-range-expr do expr doneopt
assert expr<@ expr @><@@ expr @@>%expr%%expr(static-typars : (member-sig) expr)
expr $app expr// equivalent to "expr(expr)"
expr $sep expr// equivalent to "expr; expr"
expr $tyapp < types > // equivalent to "expr<types>"
expr< >
exprs : expr ',' ... ',' expr

259

expr-or-range-expr :expr
range-expr
elif-branches : elif-branch ... elif-branchelif-branch : elif expr then expr
else-branch : else expr
function-or-value-defn :function-defn
value-defnfunction-defn :inlineopt accessopt ident-or-op typar-defnsopt argument-pats return-typeopt = expr
value-defn :mutableopt accessopt pat typar-defnsopt return-typeopt = expr
return-type :: typefunction-or-value-defns :function-or-value-defn and ... and function-or-value-defn
argument-pats: atomic-pat ... atomic-pat
field-initializer : long-ident = expr
field- initializer s : fieldinitializer ; ... ; field-initializer
object-construction :type expr
typebase-call :object-construction
object-construction as ident
interface-impls : interface-impl ... interface-impl
interface-impl : interface type object-membersopt
object-members : with member-defns end
member-defns :member-defn ... member-defn

A.2.3.1 Computation and Range Expressions
comp-or-range-expr :comp-expr
short-comp-expr
range-expr

260

comp-expr :let! pat = expr in comp-expr
do! expr in comp-expr
use! pat = expr in comp-expr
yield! expr
yield expr
return! expr
return expr
expr
short-comp-expr : for pat in expr-or-range-expr -> expr
range-expr :expr .. expr
expr .. expr .. expr
slice-range :expr..
..exprexpr..expr'*'

A.2.3.2 Computation Expressions
expr { for ... }expr { let ... }expr { let! ... }expr { use ... }expr { while ... }expr { yield ... }expr { yield! ... }expr { try ... }expr { return ... }expr { return! ... }A.2.3.3 Sequence Expressions
seq { comp-expr }seq { short-comp-expr }A.2.3.4 Range Expressions
seq { e1 .. e2 }seq { e1 .. e2 .. e3 }A.2.3.5 Copy and Update Record Expression{ expr with field-label1 = expr1 ; … ; field-labeln = exprn }A.2.3.6 Dynamic Operator Expressions
expr ? ident
expr1 ? (expr2)
expr1 ? ident <- expr2
expr1 ? (expr2) <- expr3→ (?) expr "ident"→ (?) expr1 expr2→ (?<-) expr1 "ident" expr2→ (?<-) expr1 expr2 expr3"ident" is a string literal containing the text of ident.

A.2.3.7 AddressOf Operators&expr&&expr261

A.2.3.8 Lookup Expressions
e1.[e2]e1.[e2, e3]
e1.[e2, e3, e4]
e1.[e2, e3, e4, e5]
e1.[e2] <- e3
e1.[e2, e3] <- e4
e1.[e2, e3, e4] <- e5
e1.[e2, e3, e4, e5] <- e6→ e1.get_Item(e2)→ e1.get_Item(e2, e3)→ e1.get_Item(e2, e3, e4)→ e1.get_Item(e2, e3, e4, e5)→ e1.set_Item(e2, e3)→ e1.set_Item(e2, e3, e4)→ e1.set_Item(e2, e3, e4, e5)→ e1.set_Item(e2, e3, e4, e5, e6)

A.2.3.9 Shortcut Operator Expressions
expr1 && expr2
expr1 || expr2→ if expr1 then expr2 else false→ if expr1 then true else expr2

A.2.3.10 Deterministic Disposal Expressions
use ident = expr1 in expr2

A.2.4 Patterns
rule : pat pattern-guardopt -> expr
pattern-guard : when expr
pat :
const
long-ident pat-paramopt patopt_
pat as ident
pat '|' patpat '&' patpat :: pat
pat : typepat,...,pat(pat)list-patarray-patrecord-pat:? atomic-type:? atomic-type as ident
null
attributes pat
list-pat :[ ][ pat ; ... ; pat ]array-pat :[| |][| pat ; ... ; pat |]record-pat : { field-pat ; ... ; field-pat }atomic-pat :patone ofconst long-ident:? atomic-typenull _ _list-patrecord-pat

262

array-pat(pat)field-pat : long-ident = pat
pat-param :constlong-ident[ pat-param ; ... ; pat-param ]( pat-param, ..., pat-param )long-ident pat-param
pat-param : type<@ expr @><@@ expr @@>nullpats :

pat , ... , patfield-pats : field-pat ; ... ; field-pat
rules : '|'opt rule '|' ... '|' rule

A.2.5 Type Definitions
type-defn :abbrev-type-defn
record-type-defn
union-type-defn
anon-type-defn
class-type-defn
struct-type-defn
interface-type-defn
enum-type-defn
delegate-type-defn
type-extension
type-name : attributesopt accessopt ident typar-defnsopt
abbrev-type-defn : type-name = typeunion-type-defn : type-name '=' union-type-cases type-extension-elementsopt
union-type-cases : '|'opt union-type-case '|' ... '|' union-type-caseunion-type-case : attributesopt union-type-case-data
union-type-case-data :ident-- nullary union caseident of type * ... * type-- n-ary union caseident : uncurried-sig -- n-ary union caseanon-type-defn :type-name primary-constr-argsopt object-valopt '=' begin class-type-body end
record-type-defn : type-name = '{' record-fields '}' type-extension-elementsopt
record-fields : record-field ; ... ; record-field ;opt
record-field :attributesopt mutableopt accessopt ident : typeclass-type-defn :type-name primary-constr-argsopt object-valopt '=' class class-type-body end
263

as-defn : as ident
class-type-body :beginopt class-inherits-declopt class-function-or-value-defnsopt type-defn-elementsopt
endoptclass-inherits-decl : inherit type exproptclass-function-or-value-defn :attributesopt staticopt let recopt function-or-value-defns
attributesopt staticopt do expr
struct-type-defn :type-name primary-constr-argsopt as-defnopt '=' struct struct-type-body end
struct-type-body : type-defn-elementsinterface-type-defn : type-name '=' interface interface-type-body end
interface-type-body : type-defn-elements
exception-defn :attributesopt exception union-type-case-dataattributesopt exception ident = long-ident
enum-type-defn : type-name '=' enum-type-cases
enum-type-cases : '|'opt enum-type-case '|' ... '|' enum-type-caseenum-type-case : ident '=' constdelegate-type-defn : type-name '=' delegate-sig
delegate-sig : delegate of uncurried-sig
type-extension : type-name type-extension-elements
type-extension-elements : with type-defn-elements end
type-defn-element :member-defn
interface-impl
interface-signature
type-defn-elements : type-defn-element ... type-defn-element
primary-constr-args : attributesopt accessopt (simple-pat, ... , simplepat)
simple-pat :| ident| simple-pat : typeadditional-constr-defn :attributesopt accessopt new pat as-defn = additional-constr-expr
additional-constr-expr :stmt ';' additional-constr-expr
additional-constr-expr then expr

264

if expr then additional-constr-expr else additional-constr-expr
let val-decls in additional-constr-expr
additional-constr-init-expr
additional-constr-init-expr :'{' class-inherits-decl field-initializers '}'new type expr
member-defn :attributesopt staticopt member accessopt method-or-prop-defn
attributesopt abstract memberopt accessopt member-sig
attributesopt override accessopt method-or-prop-defn
attributesopt default accessopt method-or-prop-defn
attributesopt staticopt val mutableopt accessopt ident : typeadditional-constr-defn
method-or-prop-defn :identopt function-defn
identopt value-defn
identopt ident with function-or-value-defns
member-sig :ident typar-defnsopt : curried-sig
ident typar-defnsopt : curried-sig with get
ident typar-defnsopt : curried-sig with set
ident typar-defnsopt : curried-sig with get,set
ident typar-defnsopt : curried-sig with set,get
curried-sig : args-spec -> ... -> args-spec -> typeuncurried-sig : args-spec -> typeargs-spec : arg-spec * ... * arg-spec
arg-spec : attributesopt arg-name-specopt typearg-name-spec : ?opt ident :interface-spec : interface type

A.2.5.1 Property Members
staticopt member ident.opt ident = expr
staticopt member ident.opt ident with get pat = expr
staticopt member ident.opt ident with set patopt pat= expr
staticopt member ident.opt ident with get pat = expr and set patopt pat = expr
staticopt member ident.opt ident with set patopt pat = expr and get pat = expr

A.2.5.2 Method Members
staticopt member ident.opt ident pat1 ... patn = expr

A.2.5.3 Abstract Members
abstract accessopt member-sig
member-sig :ident typar-defnsopt : curried-sig
ident typar-defnsopt : curried-sig with get
ident typar-defnsopt : curried-sig with set
ident typar-defnsopt : curried-sig with get, set
ident typar-defnsopt : curried-sig with set, get265curried-sig : args-spec1 -> ... -> args-specn -> type

A.2.5.4 Implementation Members
override ident.ident pat1 ... patn = expr
default ident.ident pat1 ... patn = expr

A.2.6 Units Of Measure
measure-literal-atom :long-ident( measure-literal-simp )measure-literal-power :measure-literal-atommeasure-literal-atom ^ int32
measure-literal-seq :measure-literal-power
measure-literal-power measure-literal-seq
measure-literal-simp :measure-literal-seq
measure-literal-simp * measure-literal-simp
measure-literal-simp / measure-literal-simp/ measure-literal-simp
1
measure-literal :_measure-literal-simp
const :...
sbyte < measure-literal >int16 < measure-literal >int32 < measure-literal >int64 < measure-literal >ieee32 < measure-literal >ieee64 < measure-literal >decimal < measure-literal >measure-atom :typar
long-ident( measure-simp )measure-power :measure-atommeasure-atom ^ int32
measure-seq :measure-power
measure-power measure-seq

266measure-simp :measure-seq
measure-simp * measure-simp
measure-simp / measure-simp/ measure-simp
1
measure :_measure-simp

A.2.7 Custom Attributes and Reflection
attribute : attribute-target:opt object-construction
attribute-set : [< attribute ; ... ; attribute >]attributes : attribute-set ... attribute-set
attribute-target :assembly
modulereturnfield
property
param
type
constructor
event

A.2.8 Compiler Directives
Compiler directives in non-nested modules or namespace declaration groups:# id string ... string

A.3 ML Compatibility Features
A.3.1 Conditional Compilation
start-fsharp-token :"(*IF-FSHARP""(*F#"
end-fsharp-token :"ENDIF-FSHARP*)""F#*)"
start-ml-token : "(*IF-OCAML*)"
end-ml-token : "(*ENDIF-OCAML*)"A.3.2 Extra Syntactic Forms
ocaml-ident-keyword : one ofasr land lor lsl lsr lxor mod267

expr :...
expr.(expr)
expr.(expr) <- expr// array lookup// array assignment

type :...
(type,...,type) long-ident // generic type instantiation
module-implementation :...
module ident = struct ... end
module-signature :...
module ident : sig ... end

A.3.3 Extra Operators
e1 or e2
e1 & e2→ (or) e1 e2→ (&) e1 e2268

References
Ecma International. Standard ECMA-335, Common Language Infrastructure (CLI)
http://www.ecma-international.org/publications/standards/Ecma-335.htm
The French National Institute for Research in Computer Science and Control (INRIA). The Caml Language.
http://caml.inria.fr/
Microsoft Corporation. The C# Language Specification
http://msdn.microsoft.com/library/ms228593.aspx269

Glossary
This section contains terminology that is specific to F#. It provides a reference for terms that are used elsewherein this document.

A
abstract member
A member in a type that represents a promise that an object will provide an implementation for a dispatch
slot.

accessibility
The program text that has access to a particular declaration element or member. You can specify
accessibilities on declaration elements in namespace declaration groups and modules, and on members intypes. F# supports public, private, and internal accessibility.

and pattern
A pattern that consists of two patterns joined by an ampersand (&). An and pattern matches the input
against both patterns and binds any variables that appear in either pattern.

anonymous implementation file
A file that lacks either a leading module or namespace declaration. Only the scripts and the last file within an
implementation group for an executable image can be anonymous. An anonymous implementation file cancontain module definitions that are implicitly placed in a module, the name of which is implicit from the nameof the source file that contains the module.

anonymous variable type with a subtype constraint
A type in the form #type. This is equivalent to 'a when 'a :> type where 'a is a fresh type inference
variable.

anonymous signature file
A signature file that does not have either a leading module or namespace declaration. The name of theimplied module signature is derived from the file name of the signature file.

anonymous variable type
A type in the form _.

application expression
An expression that involves variable names, dot-notation lookups, function applications, method applications,type applications, and item lookups

assignment expression
An expression in the form expr1 <- expr2.

arity
The number of arguments that a method or function has.

array expression
An expression in the form [|expr1;...; exprn |].

array pattern
The pattern [|pat ; ... ; pat|] , which matches arrays of a specified length.

270

array sequence expression
An expression that describes a series of elements in an array, in one of the following forms:[| comp-expr |][| short-comp-expr |][| range-expr |]as pattern
A pattern in the form pat as ident. The as pattern binds the name ident to the input value and matches the
input against the pattern.

automatic generalization
A technique that, during type inference, automatically makes code generic when possible, which means thatthe code can be used on many types of data.

Bbase type declarations
A declaration that represents an additional, encapsulated type that is supported by any values that areformed by using the type definition.

block comments
Comments that are delimited by (* and *), can span more than one line, and can be nested.

block expression
An expression in the form begin expr end.

C
class type definition
The definition of a type that encapsulates values that are themselves constructed by using one or moreobject constructors. A class type typically describes an object that can have properties, methods, andevents.

coercion
The changing of data from one type to another.

comparison constraint
A constraint of the form typar : comparison.

compiled name
The name that appears in the compiled form of an F# program for a symbolic operator or certain symbolic
keywords.

conditional expression
An expression in the following form
if expr1a then expr1b
elif expr3a then expr2b…
elif exprna then exprnb
else exprlast
The elif and else branches are optional.

271

cons pattern
The pattern pat :: pat, which is used to decompose a list into two parts: the head, which consists of thefirst element, and the tail, which contains the remaining elements.

constraint
See type constraint.

constraint solving
The process of reducing constraints to a normalized form so that variables can be solved by deducing
equations.

copy-and-update record expression
An expression in the following form:{ expr with field-label1 = expr1 ; … ; field-labeln = exprn }current inference constraints
The set of type constraints that are in effect at a particular point in the program as a result of type checking
and elaboration.

curried method members
Arguments to a method that are written in an interated form.

custom attribute
A class that encapsulates information, often metadata that describes or supplements an F# declaration.
Custom attributes derive from System.Attribute in the .NET framework and can be used in any languagethat targets the common language runtime.

D
default constructor constraint
A constraint of the form typar : (new : unit -> 'T).

default initialization
The practice of setting the values of particular types to zero at the beginning of execution. Unlike many
programming languages, F# performs default initialization in only limited circumstances.

definitely equivalent types
Static types that match exactly in definition, form, and number; or variable types that refer to the samedeclaration or are the same type inference variable.

delayed expression
An expression in the form lazy expr, which is evaluated on demand in response to a .Value operation on
the lazy value.

delegate constraint.
A constraint of the form typar : delegate<tupled-arg-type, return-type>.

dispatch slot
A key representing part of the contract of an interface or class type. Each object that implements the typeprovides a dictionary mapping dispatch slots to member implementations.

dynamic type test pattern
The patterns :? type and :? type as ident, which match any value whose runtime type is the given
type or a subtype of the given type.

272

E
elaborated expression
An expression that the F# compiler generates in a simpler, reduced language. An elaborated expression
contains a fully resolved and annotated form of the source expression and carries more explicit information
than the source expression.

enumerable extraction
The process of getting sequential values of a static type by using CLI library functions that retrieve individual,
enumerable values.

enumeration constraint
A constraint in the form typar : enum<underlying-type>, which limits the type to an enumeration of thespecified underlying type.

equality constraint.
A constraint in the form typar : equality, which limits the type to one that supports equality operations.

event
A configurable object that has a set of callbacks that can be triggered, often by some external action such as
a mouse click or timer tick. The F# library supports the Microsoft.FSharp.Control.IEvent<_,_> type
and the Microsoft.FSharp.Control.Event module to support the use of events.

F
F# Interactive
An F# dynamic compiler that runs in a command window and executes script fragments as well as complete
programs.

feasible coercion
Indicates that one type either coerces to another, or could become coercible through the addition of further
constraints to the current inference constraints.

feasibly equivalent types
Types that are not definitely equivalent but could become so by the addition of further constraints to thecurrent inference constraints.

floating type variable environment
The set of types that are currently defined, for use during type inference.

fresh typeA static type that is formed from a fresh type inference variable.

fresh type inference variable
A variable that is created during type inference and has a unique identity.

function expression
An expression of the form fun pat1 ... patn -> expr.

function value
The value that results at runtime from the evaluation of function expressions.

273

G
generic type definition
A type definition that has one or more generic type parameters. For example:
System.Collections.Generic.Dictionary<'Key,'Value>.

guarded pattern matching rule
A rule of the form pat when expr that occurs as part of a pattern matching expression such as match
expr0 with rule1 -> expr1 | … | rulen -> exprn. The guard expression expr is executed only if thevalue of expr0 successfully matches the pattern pat.

I
identifier
A sequence of characters that is enclosed in `` `` double-backtick marks, excluding newlines, tabs, anddouble-backtick pairs themselves.

immutable value
A named value that cannot be changed.

imperative programming
One of several primary programming paradigms; others include declarative, functional, procedural, among
others. An imperative program consists of a sequence of actions for the computer to perform, and thestatements change the state of the program.

implementation member
An abstract member that implements a dispatch slot or CLI property.

import declaration
A declaration that makes the elements of another namespace’s declarations and modules accessible by theuse of unqualified names. Import definitions can appear in namespace declaration groups and module
definitions.

inference type variable
A type variable that does not have a declaration site.

instance member
A member that is declared without static.

interface type definition
A declaration that represents an encapsulated type that specifies groups of related members that otherclasses implement.

K
keywordA word that has a defined meaning in F# and is used as part of the language itself.

Llambda expression
See function expression.

274

lightweight syntax
A simplified, indentation-aware syntax in which lines of code that form a sequence of declarations arealigned on the same column, and the in and ;; separators can be omitted. Lightweight syntax is the default
for all F# code in files with extension .fs, .fsx, .fsi and .fsscript.

list
An F# data structure that consists of a sequence of items. Each item contains a pointer to the next item inthe sequence.

list expression
An expression of the form [expr1;...; exprn].

list pattern
A data recognizer pattern that describes a list. Examples are pat :: pat, which matches the 'cons' case ofF# list values; [], which matches the empty list; and [pat ; ... ; pat], which represents a series of ::and empty list patterns.

list sequence expression
An expression that evaluates to a sequence that is essentially a list. List sequence expressions can have thefollowing forms:[ comp-expr ][ short-comp-expr ][ range-expr ]literal constant expression
An expression that consists of a simple constant expression or a reference to another literal. Literal constant
expressions cannot include (), native integer literals, unsigned native integer literals, byte array literals,
BigInteger literals, or user-defined numeric literals.

M
member
A function that is associated with a type definition or with a value of a particular type. Member definitions canbe used in type definitions. F# supports property members and method members.

member constraint
A constraint that specifies the signature that is required for a particular member function. Member constraints
have the form (typar or ... or typar) : (member-sig).

member signature
The “footprint” of a property or method that is visible outside the defining module.

method member
An operation that is associated with a type or an object.

moduleA named collection of declarations such as values, types, and function values.

module abbreviation
A statement that defines a local name for a long identifier in a module. For example:
module Ops = Microsoft.FSharp.Core.Operators

module signature
A description of the contents of a module that the F# compiler generates during type inference. The module
signature describes the externally visible elements of the module.

275

N
name resolution environment
The collection of names that have been defined at the current point, which F# can use in furthy type
inference and checking. The name resolution environment includes namespace declaration groups fromimported namespaces in addition to names that have been defined in the current code.

named type
A type that has a name, in the form long-ident<ty1,…,tyn>, where long-ident resolves to a a typedefinition that has formal generic parameters and formal constraints.

namespace
A way of organizing the modules and types in an F# program, so that they form logical groups that areassociated with a name. Identifiers must be unique within a namespace.

namespace declaration group
The basic declaration unit within an F# implementation file. It contains a series of module and type definitions
that contribute to the indicated namespace. An implementation can contain multiple namespace declarationgroups.

namespace declaration group signature
The “footprint” of a namespace declaration group, which describes the externally visible elements of thegroup.

null expression
An expression of the form null.

nullness constraint
A constraint in the form typar: null, which indicates that the type must support the Null literal.

null pattern
The pattern null, which matches the values that the CLI value null represents.

numeric literal
A sequence of Unicode characters or an unsigned byte array that represents a numeric value.

O
object construction expression
An expression in the form new ty(e1 ... en), which constructs a new instance of a type, usually by callinga constructor method on the type.

object constructor
A member of a class that can create a value of the type and partially initialize an object. The primaryconstructor contains function and value definitions that appear at the start of the class definition, and its
parameters appear in parentheses immediately after the type name. Any additional object constructors are
specified with the new keyword, and they must call the primary constructor.

object expression
An expression that creates a new instance of a dynamically created, anonymous object type that is based on
an existing base type, interface, or set of interfaces.

offside lines
Lines that occur at column positions in lightweight syntax. Offside lines are introduced by other structured
constructs, such asthe = token associated with let, and the first token after then in an if/then/else
construct.

offside rule
Another term for lightweight or indentation-aware syntax.
276

P
parenthesized expression
An expression in the form (expr).

pattern matching
A switch construct that supports branched control flow and the definition of new values.

pipeline operator
The |> operator, which directs the value of one function to be input to the next function in a pipeline.

property member
A function in a type that gets or sets data about the type.

Q
quoted expression
An expression that is delimited in such a way that it is not compiled as part of your program, but instead iscompiled into an object that represents an F# expression.

Rrange expression
An expression that generates a sequence over a given range of values.

record construction expression
An expression that builds a record, in the form { field-initializer1 ; … ; field-initializern }.

record pattern
The pattern { long-ident1 = pat1; ... ; long-identn = patn}.

recursive definition
A definition in which the bound functions and values can be used within their own definitions.

reference type constraint
A constraint of the form typar : not struct.

reference typeA class, interface delegate, function, tuple, record, or union type. A type is a reference type if its outermost
named type definition is a reference type, after expanding type definitions.

referenced assemblies
The existing assemblies to which an F# program makes static references.

rigid type variable
A type variable that refers to one or more explicit type parameter definitions.

runtime typeAn object of type System.Type that is the runtime representation of some or all of the information carried in
type definitions and static types. The runtime type associated with an objects is accessed by using the
obj.GetType() method, which is available on all F# values.

277

S
scriptA fragment of an F# program that can be run in F# Interactive.

sealed type definition
A type definition that is concrete and cannot be extended. Record, union, function, tuple, struct, delegate,
enum, and array types are all sealed types, as are class types marked with the SealedAttribute attribute.

sequence expression
An expression that evaluates to a sequence of values, in one of the following forms
seq { comp-expr }seq { short-comp-expr }sequential execution expression
An expression that represents the sequential execution of one statement followed by another. The
expression has the form expr1; expr2.

signature file
A file that contains information about the public signatures and accessibility of a set of F# program elements.

simple constant expressions
A numeric, string, Boolean, or unit constant.

single-line comments
A comment that begins with // and extends to the end of a line.

slice expression
An expression that describes a subset of an array.

static type
The type that is inferred for an expression as the result of type checking, constraint solving, and inference.

static member
A member that is prefixed by static and is associated with the type, rather than with any particular object.

statically resolved type variable
A type parameter in the form ^ident. Such a parameter is replaced with an actual type at compile time
instead of runtime.

stringA type that represents immutable text as a sequence of Unicode characters.

string literal
A Unicode string or an unsigned byte array that is treated as a string.

strong name
A cryptographic signature for an assembly that provides a unique name, guarantees the publisher oversubsequent versions, and ensures the integrity of the contents.

subtype constraint
A constraint of the form typar :> type, which limits the type of typar to the specified type, or to a typethat is derived from that type. If type is an interface, typar must implement the interface.

symbolic keyword
A symbolic or partially symbolic character sequence that is treated as a keyword.

structural type
A record, union, struct, or exception type definition.

symbolic operator
A user-defined or library-defined function that has one or more symbols as a name.

278syntactic sugar
Syntax that makes code easier to read or express; often a shortcut way of expressing a more complicatedrelationship for which one or more other syntactic options exist.

syntactic typeThe form of a type specification that appears in program source code, such as the text “option<_>”.
Syntactic types are converted to static types during the process of type checking and inference.

T
tuple
An ordered collection of values that is treated as an atomic unit. A tuple allows you to keep data organizedby grouping related values together, without introducing a new type.

tuple expression
An expression in the form expr1, ..., exprn, which describes a tuple value.

tuple type
A type in the form ty1 * ... * tyn, which defines a tuple. The elaborated form of a tuple type is shorthandfor a use of the family of F# library types System.Tuple<_,...,_>.

type abbreviation
An alias or alternative name for a type.

type annotation
An addition to an expression that specifies the type of the expression. A type annotation has the form expr
: type.

type constraint
A restriction on a generic type parameter or type variable that limits the types that may be used to instantiatethat parameter. Example type constraint include subtype constraints, null constraints, value type constraints,
comparison constraints and equality constraints.

type definition kind
A class, interface, delegate, struct, record, union, enum, measure, or abstract type.
The kind of type refers to the kind of its outermost named type definition, after expanding abbreviations.

type extension
A definition that associates additional dot-notation members with an existing type.

type function
A value that has explicit generic parameters but arity []—that is, it has no explicit function parameters.

type inference
A feature of F# that determines the type of a language construct when the type is not specified in the sourcecode.

type inference environment
The set of definitions and constraints that F# uses to infer the type of a value, variable, function, orparameter, or similar language construct.

type parameter definition
In a generic function, method, or type, a placeholder for a specific type that is specified when the genericfunction, method, or type is instantiated.

type variable
A variable that represents a type, rather than data.

279

U
undentation
The opposite of indentation.

underlying type
The type of the constant values of an enumeration. The underlying type of an enum must be sbyte, int16,
int32, int64, byte, uint16, uint32, uint64, or char.

union pattern
The pattern pat | pat attempts to match the input value against the first pattern, and if that fails matches
instead the second pattern. Both patterns must bind the same set of variables with the same types.

union type
A type that can hold a value that satisfies one of a number of named cases.

unit of measure
A construct similar to a type that represents a measure, such as kilogram or meters per second. Like types,
measures can appear as parameters to other types and values, can contain variables, and are checked forconsistency by the type-checker. Unlike types, however, measures are erased at runtime, have special
equivalence rules, and are supported by special syntax.

unmanaged type
The primitive types (sbyte, byte, char, nativeint, unativeint, float32, float, int16, uint16, int32,
uint32, int64, uint64, and decimal), enumeration types, and nativeptr<_>, or a non-generic structure
whose fields are all unmanaged types.

unmanaged constraint
An addition to a type parameter that limits the type to an unmanaged type.

Vvalue signature
The “footprint” of a value in a module, which indicates that the value exists and is externally visible.

value type
A type that is allocated on the stack or inline in an object or array. Value types include primitive integers,
floating-point numbers, and any value of a struct type.

value type constraint
A constraint of the form typar : struct, which limits the type of typar to a .NET value type.

variable type
A type of the form 'ident, which represents the name of another type.

W
wildcard pattern
The underscore character _, which matches any input.

280

Index# flexible type symbol, 114#indent, 249#load directive, 174#nowarn directive, 174% operator, 86%% operator, 86& byref address-of operator, 73& conjunctive patterns, 102&& native pointer address-of operator, 73&& operator, 77.fs extension, 19, 173.fsi extension, 19.fsscript extension, 19, 173.fsx extension, 19, 173.ml extension, 248.mli extension, 248:: cons pattern, 102; token, 77_ wildcard pattern, 102__LINE__, 27__SOURCE_DIRECTORY__, 27__SOURCE_FILE__, 27|| operator, 77= function, 145
abstract members, 138
abstract types, 44
AbstractClass attribute, 138accessibilities
annotations for, 161
default annotation for modules, 159
location of modifiers, 162
active pattern functions, 100
active pattern results, 70
address-of expressions, 73
AddressOf expressions, 89, 94
agents, 244
AllowNullLiteral attribute, 47
anonymous variable type, 37
application expressions, 69, 190
arguments
CLI optional, 133
named, 131
optional, 132
required unnamed, 132
arity, 214
conformance in value signatures, 168
array expressions, 58, 91
array sequence expression, 67
array type, 38
assemblies
contents of, 171
referenced, 171
assert, 81
assertion expression, 81
assignment expression, 75
asynchronous computations, 244
attributes
AbstractClass, 138
AllowNullLiteral, 47
AttributeUsage, 179
AutoOpen, 171

AutoOpenAttribute, 237
CLIEvent, 136, 140
comparison, 143CompilationRepresentation, 137, 157
conditional compilation, 199
ContextStatic, 81, 159
custom, 43, 179, 227
DefaultValue, 122emitted by F# compiler, 234
EntryPoint, 177
equality, 143
GeneralizableValue, 160
grammar of, 179
in type definitions, 110
InternalsVisibleTo, 162
Literal, 159
mapping to CLI metadata, 180
Measure, 111, 150, 153
MeasureAnnotatedAbbreviation, 154
NoEquality, 42
OptionalArgument, 134
ReflectedDefinition, 85
RequireQualifiedAccess, 184
RequiresExplicitTypeArguments, 160
RequiresQualifiedAccess, 126
SealedAttribute, 44
ThreadStatic, 81, 159
unrecognized by F#, 234
VolatileField, 88AttributeUsage attribute, 179
automatic generalization, 13
AutoOpen attribute, 171
AutoOpenAttribute, 237
AutoSerializable attribute, 114, 116, 117base type, 45
basic types
abbreviations for, 237
block expressions, 76bprintf function, 68
byref arguments, 213
byref pointers, 54
byref-address-of expression, 73
case names, 115
characters, 23class types, 117
additional fields in, 122members in, 120class/end tokens, 117classes, 43
CLI methods, 215
CLI pointer types, 238
CLIEvent attribute, 136, 140
comments, 21, 217
compare function, 145
CompareTo, 145
comparison attributes, 143
comparison constraint, 42
ComparisonConditionalOn constraint dependency,
144
compatibility features, 247
compilation order, 172

281

CompilationRepresentation attribute, 137, 157
COMPILED compilation symbol, 19, 174
compiler directives, 174
computation expression, 62
condensation, 210
Conditional attribute, 199
conditional compilation, 21, 199
ML compatibility and, 247
conditional expressions, 77
constant expressions, 56
constants with measure annotations, 151
constrained types, 38
constraints, 38
comparison, 42
current inference, 36
default constructor, 40
delegate, 41
dependency of, 143, 144
enumeration, 41
equality, 42
equational, 200
explicit declaration of, 43
flexible type, 84
inflexible type, 84
member, 202
member, 40
nullness, 39, 47, 68, 201
reference type, 41
simple, 201
solving, 200
struct, 40, 201
subtype, 38, 39, 201
unmanaged, 42ContextStatic attribute, 81, 159
control flow expressions, 76
copy-and-update record expression, 59
curried form, 131
custom attributes
effect on signature checking, 180
in type definitions, 110
CustomComparison attribute, 143
CustomEquality attribute, 143
declarations
base type, 44interface, 44
default initialization, 48
DefaultValue attribute, 122
definition expressions, 81
definitions
recursive, 204delayed expression, 62delegate constraint, 41delegate implementation expression, 71
delegate type, 126delegates, 43
deterministic disposal expression, 83
directives#load, 174#nowarn, 174
compiler, 174
lexical, 174
line, 27
preprocessing, 21
dispatch slot checking, 61, 212
dispatch slot inference, 61, 211
dispatch slots, 212
do statements, 119

in modules, 160
static, 119
done token, 79
dynamic coercion expressions, 84, 93
dynamic type-test expressions, 84, 92
elif branch, 77
else branch, 77
entry points, 177
EntryPoint attribute, 177
enum types, 125
enumerable extraction, 79
enums, 44
equality attributes, 143
equality constraint, 42
EqualityConditionalOn constraint dependency, 143
evaluationof active pattern results, 70
of AddressOf expressions, 94
of array expressions, 91
of definition expressions, 92of dynamic coercion expressions, 93of dynamic type-test expressions, 92
of field lookups, 91
of for loops, 92
of function applications, 90of function expressions, 91
of method applications, 90
of object expressions, 91
of record expressions, 91
of sequential execution expressions, 93of try-finally expressions, 94of try-with expressions, 93, 94
of union cases, 91
of value references, 90
of while loops, 92
event types, 245
events, 136
exception definitions, 126
exceptions, 235
execution of F# code, 175
expression splices, 86
expressions
address-of, 73
application, 69
array. See array expression
array sequence, 67
assertion, 81
assignment, 75
block, 76
builder, 62
checking of, 53
computation, 53, 62
conditional, 77
constant, 56
delayed, 62delegate implementation, 71
deterministic disposal, 83
dynamic coercion, 84
dynamic type-test, 84
elaborated, 54
evaluation of, 87
for loop, 79
function, 60
function and value definitions, 81
list, 57
list sequence, 67
lookup, 73
282

member constraint invocation, 74
name resolution in, 185
null, 68
object. See object expressions
object construction, 70
operator, 71, 72
parenthesized, 76
pattern-matching, 78
precedence in, 33
quoted, 54, 85
range, 53, 66
record construction, 58
recursive, 83
reraise, 80
sequence, 66
sequence iteration, 78
sequential conditional, 77
sequential execution, 77
shortcut and, 77
shortcut or, 77
slice, 74
static coercion, 84
syntactical elements of, 51
try-finally, 80
try-with, 80
tuple, 56
type-annotated, 84
while-loop, 79
extension members, 127
defined by C#, 128
field lookups, 91
fieldsadditional, in classes, 122
name resolution for labels, 189
filename extensions, 19
ML compatibility and, 248
files
implementation, 172
signature. See signature files
flexibility, 199
flexible types, 114
floating type variable environment, 36
for loop, 79
for loops, 92
format strings, 68
fprintf function, 68
fresh type, 36
function applications, 90
function definition expressions, 81
function definitions, 119, 204, 205
ambiguous, 204
in modules, 158
static, 119
function expressions, 60, 91
function values, 13
functionsactive pattern, 100
undentation of, 224
GeneralizableValue attribute, 160
generalization, 45, 208
generic types, 208
GetHashCode, 145
GetSlice, 74
guarded rules, 105
hash function, 145
hashing, 142
hidden tokens, 218

identifiers, 22
local names for, 161long, 31
OCaml keywords as, 247
replacement of, 27
if statement, 77
if/then/else expression
undentation of body, 224
immutability, 12
immutable collection types, 245
implementation files
anonymous, 172
contents of, 172
implementation members, 138
import declarations, 161
in token, 81
indentation, 217
incremental, 224
indexer properties, 130
inferencearity, 214
dispatch slot, 211inference variables, 45
infix operators, 32
inherit declaration, 118, 123
initialization, 48
of objects, 117
static, 175
instance members, 129
compilation as static method, 137
integer literals, 56
INTERACTIVE compilation symbol, 19, 174interface type definitions, 123interface types, 46, 140interface/end tokens, 123interfaces, 43internal accessibility, 161
internal type abbreviations, 114
InternalsVisibleTo attribute, 162
intrinsic extensions, 127
keywords
OCaml, 247
symbolic, 24
kindanonymous, 112
kind of type definition, 43
lazy computations, 244
libraries
CLI base, 237
F# base, 237
lightweight syntax, 11, 217
balancing rules for, 221
disabling, 249
parsing, 220
rules for, 217
line directives, 27
list expression, 57
list sequence expression, 67
list type, 244
Literal attribute, 159
literals
integer, 56
numeric, 25
string, 23
lookupexpression-qualified, 192
item-qualified, 191283

unqualified, 190
lookup expressions, 73
mailbox processor, 244
measure annotated base types, 154
Measure attribute, 16, 150, 153
measure parameters, 111
defining, 153
erasing of, 153
MeasureAnnotatedAbbreviation attribute, 154
measures, 44
basic types and annotations for, 238
building blocks of, 150constraints on, 152
defined, 149
defining, 152
generalization of, 152
relations on, 151
type definitions with, 153
member constraint invocation expressions, 74
member definitions, 204member signatures, 166
members, 129
extension, 127
intrinsic, 127
name resolution for, 187
naming restrictions for, 136
processing of definitions, 204
signature conformance for, 169
method applications, 90
method calls
conditional compilation of, 199
method members, 129, 131
curried, 131
named arguments to, 131
optional arguments to, 132
methods
overloading of, 135
overriding, 61
Microsoft.FSharp.Collections.list, 244
Microsoft.FSharp.Core, 237
Microsoft.FSharp.Core.NativeIntrop, 245
Microsoft.FSharp.Core.Operators, 238
Microsoft.FSharp.Core.option, 244
mlcompatibility option, 247
module declaration, 172
modulesabbreviations for, 161
active pattern definitions in, 160
defining, 157
do statements in, 160
function definitions in, 158
name resolution in, 183
signature of, 157
undentation of, 225
value definitions in, 158
mscorlib.dll, 237
mutable, 114, 119mutable value definitions, 204
mutable values, 76
name environment, 183
adding items to, 184
name resolution, 36, 37, 183
namespace declaration, 172
namespace declaration groups, 155
namespaces, 155grammar of, 155
name resolution in, 183

opened for F# code, 237
native pointer operations, 245
nativeptr type, 238
nativeptr-address-of expression, 73
NoComparison attribute, 143
NoEquality attribute, 143
null, 47
null expression, 68
NullReferenceException, 90
numeric literals, 25
object construction expression, 70
object constructors, 117
additional, 120
primary, 117
object expressions, 60, 91
Object.Equals, 144
objects
initialization of, 117
physical identity of, 94
references to, 94
offside contexts, 220
offside limit, 222
offside lines, 219
offside rule, 219
exceptions to, 223
operations
underspecified results of, 95
operator expressions, 71
operators
address-of, 73
basic arithmetic, 238
bitwise, 239
checked arithmetic, 243
default definition of, 72
exception, 241
function pipelining and composition, 241
generic equality and comparison, 239
infix, 32
input and output handles, 242
math, 240
ML compatibility and, 248
names of, 29
object transformation, 241
overloaded conversion functions, 242
pair, 241
precedence of, 33
prefix, 32
splicing, 86
symbolic, 25, 32
option type, 244
OptionalArgument attribute, 134
overflow checking, 243
parallel execution, 88
ParamArray conversion, 196
parenthesized expressions, 76
pattern matching, 13
pattern-matching expression, 78
pattern-matching function, 78
patterns, 97
active, 100
array, 104
as, 101
conjunctive, 102cons, 102
dynamic type-test, 103
guarded rules for, 105
literal, 99284

name resolution for, 188
named, 98
null, 105
record, 104
simple constant, 98
type-annotated, 103
union case, 99
variable, 98
wildcard, 102
pointer, byref, 54
precedence
differences from OCaml, 248
of function applications, 225of type applications, 226
prefix operators, 32
preprocessing directives, 21
printf, 245
printf function, 68
private accessibility, 161
private type abbreviations, 114
property members, 129, 130, 138public accessibility, 159, 161
quotations, 245
quoted expression, 85
quoted expressions, 54
range expressions, 66
rec, 119record construction expression, 58
record expressions
evaluation of, 91
record expressionss
copy-and-update, 59
record types
automatically implemented interfaces in, 115
members in, 114
scope of field labels, 114
record types, 114
records, 44
recursive definitions, 204, 205
recursive function definition, 83
recursive safety analysis, 206
recursive value definition, 83
reference types
zero value of, 89
ReferenceEquality attribute, 143reflected forms, 181
ReflectedDefinition attribute, 85
reflection, 245
RequireQualifiedAccess attribute, 184
RequiresExplicitTypeArguments attribute, 160
RequiresQualifiedAccess attribute, 126
reraise expressions, 80
resolutionfunction application, 194
method application, 194
script files, 173
Sealed attribute, 114
SealedAttribute attribute, 44
sequence expression, 66
sequence iteration expression, 78
sequential conditional expressions, 77
sequential execution expressions, 77, 93
shortcut and expression, 77
shortcut or expression, 77
signature elements, 166
signature files, 165
anonymous, 173

compilation order of, 172
contents of, 173
signatures
conformance of, 167
declarations of, 166
member, 167
module, 157
of namespace declaration groups, 156
type definition, 167
value, 166
slice expressions, 74
source code files, 19
sprintf function, 68
stack allocation, 246
static coercion expressions, 84
static initializer
execution of, 175
static initializers, 120
static members, 129
static types, 36
strings, 23
format, 68
newlines in, 24
strongly typed quoted expressions, 86
struct types
default constructor in, 125
struct/end tokens, 123
structs, 44
structural equality, 142
structural types, 142
StructuralComparison attribute, 143
StructuralEquality attribute, 143
symbolic operators, 25, 32
syntactic types, 36
System.Object, 61
System.Reflection objects, 181
System.Tuple, 57
System.Type objects, 181
text formatting, 245
ThreadStatic attribute, 81, 159
tokenshidden, 27, 218
try-finally expressions, 80
evaluation of, 94
try-with expressions, 80, 93
tuple type, 38
type
fresh, 36
meanings of, 35
named, 37
statically resolved variable, 38
type abbreviations, 43, 113
type annotations
over-constrained, 203
type applications
lexical analysis of, 226
type definition group, 110
type definition signatures, 166, 167
type definitions, 35, 43
abstract members in, 138
checking of, 110delegate, 126
enum, 125
exception, 126
generic, 43
grammar of, 107interface, 123285

interfaces in, 140
kinds of, 109
location of, 109
reference, 44
sealed, 44
struct, 123
type extensions, 127
type functions, 158
signature conformance for, 169
type inference, 12, 36
type inference environment, 36
type kind inference, 112
type parameter definitions, 42
type variable
definition site, 45
type variables, 35
name resolution for, 189
rigid, 45
type-annotated expressions, 84
type-annotated patterns, 103
typedefof operator, 181
type-directed conversions, 134
typeof operator, 181
types
anonymous variable. See anonymous variable
type
array. See array typebase, 45class, 44, 117
coercion of, 46
comparison of, 142condensation of generalized function types, 210
constrained, 38
conversion of, 134delegate, 71, 126
dynamic conversion of, 48
enum, 125
equivalence of, 46
exn (exception), 126
flexible, 114
implicit static members of, 202
initial, 53interface types of. See interface types
logical properties of, 43
name resolution for, 188
nativeptr, 238
partial static, 45
record, 114

reference, 44
renaming, 113
runtime, 35
static, 35
structural, 142
syntactic, 35
tuple. See tuple typeunion, 115
unit, 79
unmanaged, 42
value, 44variable, 37
zero value of, 89
undentation, 222, 224
union cases, 91
union types, 115
automatically implemented interfaces for, 116
compiled, 116
members in, 116
unions, 44
unit type, 47, 79
units of measure. See measuresunmanaged constraint, 42
val specification, 122value definition expressions, 81
value definitions, 119, 204
in modules, 158
static, 119value references, 90
value signatures, 166
value types
zero value of, 89
valuesarity conformance for, 168
processing of definitions, 204
runtime, 87
signature conformance for, 167
verbatim strings, 24
virtual methods, 139
VolatileField attribute, 88
weakly typed quoted expression, 86
while loops, 92
while-loop expression, 79
whitespace, 21
significance in lightweight syntax, 217
with/end tokens, 115, 116
XML documentation tokens, 21
zero value, 89286