F# for Fun and Profit
  • Introduction
  • Getting started
    • Contents of the book
    • "Why use F#?" in one page
    • Installing and using F#
    • F# syntax in 60 seconds
    • Learning F#
    • Troubleshooting F#
    • Low-risk ways to use F# at work
      • Twenty six low-risk ways to use F# at work
      • Using F# for development and devops scripts
      • Using F# for testing
      • Using F# for database related tasks
      • Other interesting ways of using F# at work
  • Why use F#?
    • The "Why use F#?" Series
      • Introduction to the 'Why use F#' series
      • F# syntax in 60 seconds
      • Comparing F# with C#: A simple sum
      • Comparing F# with C#: Sorting
      • Comparing F# with C#: Downloading a web page
      • Four Key Concepts
      • Conciseness
      • Type inference
      • Low overhead type definitions
      • Using functions to extract boilerplate code
      • Using functions as building blocks
      • Pattern matching for conciseness
      • Convenience
      • Out-of-the-box behavior for types
      • Functions as interfaces
      • Partial Application
      • Active patterns
      • Correctness
      • Immutability
      • Exhaustive pattern matching
      • Using the type system to ensure correct code
      • Worked example: Designing for correctness
      • Concurrency
      • Asynchronous programming
      • Messages and Agents
      • Functional Reactive Programming
      • Completeness
      • Seamless interoperation with .NET libraries
      • Anything C# can do...
      • Why use F#: Conclusion
  • Thinking Functionally
    • The "Thinking Functionally" Series
      • Thinking Functionally: Introduction
      • Mathematical functions
      • Function Values and Simple Values
      • How types work with functions
      • Currying
      • Partial application
      • Function associativity and composition
      • Defining functions
      • Function signatures
      • Organizing functions
      • Attaching functions to types
      • Worked example: A stack based calculator
  • Understanding F# ###
    • The "Expressions and syntax" Series
      • Expressions and syntax: Introduction
      • Expressions vs. statements
      • Overview of F# expressions
      • Binding with let, use, and do
      • F# syntax: indentation and verbosity
      • Parameter and value naming conventions
      • Control flow expressions
      • Exceptions
      • Match expressions
      • Formatted text using printf
      • Worked example: Parsing command line arguments
      • Worked example: Roman numerals
    • The "Understanding F# types" Series
      • Understanding F# types: Introduction
      • Overview of types in F#
      • Type abbreviations
      • Tuples
      • Records
      • Discriminated Unions
      • The Option type
      • Enum types
      • Built-in .NET types
      • Units of measure
      • Understanding type inference
    • Choosing between collection functions
    • The "Object-oriented programming in F#" Series
      • Object-oriented programming in F#: Introduction
      • Classes
      • Inheritance and abstract classes
      • Interfaces
      • Object expressions
    • The "Computation Expressions" Series
      • Computation expressions: Introduction
      • Understanding continuations
      • Introducing 'bind'
      • Computation expressions and wrapper types
      • More on wrapper types
      • Implementing a builder: Zero and Yield
      • Implementing a builder: Combine
      • Implementing a builder: Delay and Run
      • Implementing a builder: Overloading
      • Implementing a builder: Adding laziness
      • Implementing a builder: The rest of the standard methods
    • Organizing modules in a project
    • The "Dependency cycles" Series
      • Cyclic dependencies are evil
      • Refactoring to remove cyclic dependencies
      • Cycles and modularity in the wild
    • The "Porting from C#" Series
      • Porting from C# to F#: Introduction
      • Getting started with direct porting
  • Functional Design ###
    • The "Designing with types" Series
      • Designing with types: Introduction
      • Single case union types
      • Making illegal states unrepresentable
      • Discovering new concepts
      • Making state explicit
      • Constrained strings
      • Non-string types
      • Designing with types: Conclusion
    • Algebraic type sizes and domain modelling
    • Thirteen ways of looking at a turtle
      • Thirteen ways of looking at a turtle (part 2)
      • Thirteen ways of looking at a turtle - addendum
  • Functional Patterns ###
    • How to design and code a complete program
    • A functional approach to error handling (Railway oriented programming)
      • Railway oriented programming: Carbonated edition
    • The "Understanding monoids" Series
      • Monoids without tears
      • Monoids in practice
      • Working with non-monoids
    • The "Understanding Parser Combinators" Series
      • Understanding Parser Combinators
      • Building a useful set of parser combinators
      • Improving the parser library
      • Writing a JSON parser from scratch
    • The "Handling State" Series
      • Dr Frankenfunctor and the Monadster
      • Completing the body of the Monadster
      • Refactoring the Monadster
    • The "Map and Bind and Apply, Oh my!" Series
      • Understanding map and apply
      • Understanding bind
      • Using the core functions in practice
      • Understanding traverse and sequence
      • Using map, apply, bind and sequence in practice
      • Reinventing the Reader monad
      • Map and Bind and Apply, a summary
    • The "Recursive types and folds" Series
      • Introduction to recursive types
      • Catamorphism examples
      • Introducing Folds
      • Understanding Folds
      • Generic recursive types
      • Trees in the real world
    • The "A functional approach to authorization" Series
      • A functional approach to authorization
      • Constraining capabilities based on identity and role
      • Using types as access tokens
  • Testing
    • An introduction to property-based testing
    • Choosing properties for property-based testing
  • Examples and Walkthroughs
    • Worked example: Designing for correctness
    • Worked example: A stack based calculator
    • Worked example: Parsing command line arguments
    • Worked example: Roman numerals
    • Commentary on 'Roman Numerals Kata with Commentary'
    • Calculator Walkthrough: Part 1
      • Calculator Walkthrough: Part 2
      • Calculator Walkthrough: Part 3
      • Calculator Walkthrough: Part 4
    • Enterprise Tic-Tac-Toe
      • Enterprise Tic-Tac-Toe, part 2
    • Writing a JSON parser from scratch
  • Other
    • Ten reasons not to use a statically typed functional programming language
    • Why I won't be writing a monad tutorial
    • Is your programming language unreasonable?
    • We don't need no stinking UML diagrams
    • Introvert and extrovert programming languages
    • Swapping type-safety for high performance using compiler directives
Powered by GitBook
On this page
  • General guidelines for troubleshooting ##
  • F# compiler errors
  • FS0001: The type 'X' does not match the type 'Y' ##
  • FS0003: This value is not a function and cannot be applied ##
  • FS0008: This runtime coercion or type test involves an indeterminate type ##
  • FS0010: Unexpected identifier in binding ##
  • FS0010: Incomplete structured construct ##
  • FS0013: The static coercion from type X to Y involves an indeterminate type ##
  • FS0020: This expression should have type 'unit' ##
  • FS0030: Value restriction ##
  • FS0035: This construct is deprecated ##
  • FS0039: The field, constructor or member X is not defined ##
  • FS0041: A unique overload for could not be determined ##
  • FS0049: Uppercase variable identifiers should not generally be used in patterns ##
  • FS0072: Lookup on object of indeterminate type ##
  • FS0588: Block following this 'let' is unfinished ##

Was this helpful?

  1. Getting started

Troubleshooting F#

Why won't my code compile?

PreviousLearning F#NextLow-risk ways to use F# at work

Last updated 5 years ago

Was this helpful?

As the saying goes, "if it compiles, it's correct", but it can be extremely frustrating just trying to get the code to compile at all! So this page is devoted to helping you troubleshoot your F# code.

I will first present some general advice on troubleshooting and some of the most common errors that beginners make. After that, I will describe each of the common error messages in detail, and give examples of how they can occur and how to correct them.

General guidelines for troubleshooting ##

By far the most important thing you can do is to take the time and effort to understand exactly how F# works, especially the core concepts involving functions and the type system. So please read and reread the series and , play with the examples, and get comfortable with the ideas before you try to start doing serious coding. If you don't understand how functions and types work, then the compiler errors will not make any sense.

If you are coming from an imperative language such as C#, you may have developed some bad habits by relying on the debugger to find and fix incorrect code. In F#, you will probably not get that far, because the compiler is so much stricter in many ways. And of course, there is no tool to "debug" the compiler and step through its processing. The best tool for debugging compiler errors is your brain, and F# forces you to use it!

Nevertheless, there are a number of extremely common errors that beginners make, and I will quickly go through them.

Don't use parentheses when calling a function ###

In F#, whitespace is the standard separator for function parameters. You will rarely need to use parentheses, and in particular, do not use parentheses when calling a function.

let add x y = x + y
let result = add (1 2)  //wrong
    // error FS0003: This value is not a function and cannot be applied
let result = add 1 2    //correct

Don't mix up tuples with multiple parameters ###

If it has a comma, it is a tuple. And a tuple is one object not two. So you will get errors about passing the wrong type of parameter, or too few parameters.

addTwoParams (1,2)  // trying to pass a single tuple rather than two args
   // error FS0001: This expression was expected to have type
   //               int but here has type 'a * 'b

The compiler treats (1,2) as a generic tuple, which it attempts to pass to "addTwoParams". Then it complains that the first parameter of addTwoParams is an int, and we're trying to pass a tuple.

If you attempt to pass two arguments to a function expecting one tuple, you will get another obscure error.

addTuple 1 2   // trying to pass two args rather than one tuple
  // error FS0003: This value is not a function and cannot be applied

Watch out for too few or too many arguments ###

The F# compiler will not complain if you pass too few arguments to a function (in fact "partial application" is an important feature), but if you don't understand what is going on, you will often get strange "type mismatch" errors later.

Similarly the error for having too many arguments is typically "This value is not a function" rather than a more straightforward error.

The "printf" family of functions is very strict in this respect. The argument count must be exact.

Use semicolons for list separators ###

In the few places where F# needs an explicit separator character, such as lists and records, the semicolon is used. Commas are never used. (Like a broken record, I will remind you that commas are for tuples).

let list1 = [1,2,3]    // wrong! This is a ONE-element list containing 
                       // a three-element tuple
let list1 = [1;2;3]    // correct

type Customer = {Name:string, Address: string}  // wrong
type Customer = {Name:string; Address: string}  // correct

Don't use ! for not or != for not-equal ###

The exclamation point symbol is not the "NOT" operator. It is the deferencing operator for mutable references. If you use it by mistake, you will get the following error:

let y = true
let z = !y
// => error FS0001: This expression was expected to have 
//    type 'a ref but here has type bool

The correct construction is to use the "not" keyword. Think SQL or VB syntax rather than C syntax.

let y = true
let z = not y       //correct

And for "not equal", use "<>", again like SQL or VB.

let z = 1 <> 2      //correct

Don't use = for assignment ###

If you are using mutable values, the assignment operation is written "<-". If you use the equals symbol you might not even get an error, just an unexpected result.

let mutable x = 1
x = x + 1          // returns false. x is not equal to x+1 
x <- x + 1         // assigns x+1 to x

Watch out for hidden tab characters ###

The indenting rules are very straightforward, and it is easy to get the hang of them. But you are not allowed to use tabs, only spaces.

let add x y =     
{tab}x + y   
// => error FS1161: TABs are not allowed in F# code

Be sure to set your editor to convert tabs to spaces. And watch out if you are pasting code in from elsewhere. If you do run into persistent problems with a bit of code, try removing the whitespace and re-adding it.

Don't mistake simple values for function values ###

If you are trying to create a function pointer or delegate, watch out that you don't accidentally create a simple value that has already been evaluated.

If you want a parameterless function that you can reuse, you will need to explicitly pass a unit parameter, or define it as a lambda.

let reader = new System.IO.StringReader("hello")
let nextLineFn   =  reader.ReadLine()  //wrong
let nextLineFn() =  reader.ReadLine()  //correct
let nextLineFn   =  fun() -> reader.ReadLine()  //correct

let r = new System.Random()
let randomFn   =  r.Next()  //wrong
let randomFn() =  r.Next()  //correct
let randomFn   =  fun () -> r.Next()  //correct

Tips for troubleshooting "not enough information" errors ###

The F# compiler is currently a one-pass left-to-right compiler, and so type information later in the program is unavailable to the compiler if it hasn't been parsed yet.

  • Define things before they are used (this includes making sure the files are compiled in the right order)

  • Put the things that have "known types" earlier than things that have "unknown types". In particular, you might be able reorder pipes and similar chained functions so that the typed objects come first.

  • Annotate as needed. One common trick is to add annotations until everything works, and then take them away one by one until you have the minimum needed.

Do try to avoid annotating if possible. Not only is it not aesthetically pleasing, but it makes the code more brittle. It is a lot easier to change types if there are no explicit dependencies on them.

F# compiler errors

A listing of common errors, ordered by error number

Here is a list of the major errors that seem to me worth documenting. I have not documented any errors that are self explanatory, only those that seem obscure to beginners.

I will continue to add to the list in the future, and I welcome any suggestions for additions.

FS0001: The type 'X' does not match the type 'Y' ##

This is probably the most common error you will run into. It can manifest itself in a wide variety of contexts, so I have grouped the most common problems together with examples and fixes. Do pay attention to the error message, as it is normally quite explicit about what the problem is.

Error message

Possible causes

The type 'float' does not match the type 'int'

The type 'int' does not support any operators named 'DivideByInt'

The type 'X' is not compatible with any of the types

This type (function type) does not match the type (simple type). Note: function types have a arrow in them, like 'a -> 'b.

This expression was expected to have (function type) but here has (simple type)

This expression was expected to have (N part function) but here has (N-1 part function)

This expression was expected to have (simple type) but here has (function type)

This expression was expected to have (type) but here has (other type)

Type mismatch. Expecting a (simple type) but given a (tuple type). Note: tuple types have a star in them, like 'a * 'b.

Type mismatch. Expecting a (tuple type) but given a (different tuple type).

This expression was expected to have type 'a ref but here has type X

The type (type) does not match the type (other type)

This expression was expected to have type (monadic type) but here has type 'b * 'c

A. Can't mix ints and floats ###

Unlike C# and most imperative languages, ints and floats cannot be mixed in expressions. You will get a type error if you attempt this:

1 + 2.0  //wrong
   // => error FS0001: The type 'float' does not match the type 'int'

The fix is to cast the int into a float first:

float 1 + 2.0  //correct

This issue can also manifest itself in library functions and other places. For example, you cannot do "average" on a list of ints.

[1..10] |> List.average   // wrong
   // => error FS0001: The type 'int' does not support any 
   //    operators named 'DivideByInt'

You must cast each int to a float first, as shown below:

[1..10] |> List.map float |> List.average  //correct 
[1..10] |> List.averageBy float  //correct (uses averageBy)

B. Using the wrong numeric type ###

You will get a "not compatible" error when a numeric cast failed.

printfn "hello %i" 1.0  // should be a int not a float
  // error FS0001: The type 'float' is not compatible 
  //               with any of the types byte,int16,int32...

One possible fix is to cast it if appropriate.

printfn "hello %i" (int 1.0)

C. Passing too many arguments to a function ###

let add x y = x + y
let result = add 1 2 3
// ==> error FS0001: The type ''a -> 'b' does not match the type 'int'

The clue is in the error.

The fix is to remove one of the arguments!

Similar errors are caused by passing too many arguments to printf.

printfn "hello" 42
// ==> error FS0001: This expression was expected to have type 'a -> 'b    
//                   but here has type unit    

printfn "hello %i" 42 43
// ==> Error FS0001: Type mismatch. Expecting a 'a -> 'b -> 'c    
//                   but given a 'a -> unit    

printfn "hello %i %i" 42 43 44
// ==> Error FS0001: Type mismatch. Expecting a  'a -> 'b -> 'c -> 'd    
//                   but given a 'a -> 'b -> unit

D. Passing too few arguments to a function ###

If you do not pass enough arguments to a function, you will get a partial application. When you later use it, you get an error because it is not a simple type.

let reader = new System.IO.StringReader("hello");

let line = reader.ReadLine        //wrong but compiler doesn't complain
printfn "The line is %s" line     //compiler error here!
// ==> error FS0001: This expression was expected to have type string    
//                   but here has type unit -> string

This is particularly common for some .NET library functions that expect a unit parameter, such as ReadLine above.

The fix is to pass the correct number of parameters. Check the type of the result value to make sure that it is indeed a simple type. In the ReadLine case, the fix is to pass a () argument.

let line = reader.ReadLine()      //correct
printfn "The line is %s" line     //no compiler error

E. Straightforward type mismatch ###

The simplest case is that you have the wrong type, or you are using the wrong type in a print format string.

printfn "hello %s" 1.0
// => error FS0001: This expression was expected to have type string    
//                  but here has type float

F. Inconsistent return types in branches or matches ###

A common mistake is that if you have a branch or match expression, then every branch MUST return the same type. If not, you will get a type error.

let f x = 
  if x > 1 then "hello"
  else 42
// => error FS0001: This expression was expected to have type string    
//                  but here has type int
let g x = 
  match x with
  | 1 -> "hello"
  | _ -> 42
// error FS0001: This expression was expected to have type
//               string but here has type int

Obviously, the straightforward fix is to make each branch return the same type.

let f x = 
  if x > 1 then "hello"
  else "42"

let g x = 
  match x with
  | 1 -> "hello"
  | _ -> "42"

Remember that if an "else" branch is missing, it is assumed to return unit, so the "true" branch must also return unit.

let f x = 
  if x > 1 then "hello"
// error FS0001: This expression was expected to have type
//               unit but here has type string

If both branches cannot return the same type, you may need to create a new union type that can contain both types.

type StringOrInt = | S of string | I of int  // new union type
let f x = 
  if x > 1 then S "hello"
  else I 42

G. Watch out for type inference effects buried in a function ###

A function may cause an unexpected type inference that ripples around your code. For example, in the following, the innocent print format string accidentally causes doSomething to expect a string.

let doSomething x = 
   // do something
   printfn "x is %s" x
   // do something more

doSomething 1
// => error FS0001: This expression was expected to have type string    
//    but here has type int

The fix is to check the function signatures and drill down until you find the guilty party. Also, use the most generic types possible, and avoid type annotations if possible.

H. Have you used a comma instead of space or semicolon? ###

If you are new to F#, you might accidentally use a comma instead of spaces to separate function arguments:

// define a two parameter function
let add x y = x + 1

add(x,y)   // FS0001: This expression was expected to have 
           // type int but here has type  'a * 'b

The fix is: don't use a comma!

add x y    // OK

One area where commas are used is when calling .NET library functions. These all take tuples as arguments, so the comma form is correct. In fact, these calls look just the same as they would from C#:

// correct
System.String.Compare("a","b")

// incorrect
System.String.Compare "a" "b"

I. Tuples must be the same type to be compared or pattern matched ###

Tuples with different types cannot be compared. Trying to compare a tuple of type int * int, with a tuple of type int * string results in an error:

let  t1 = (0, 1)
let  t2 = (0, "hello")
t1 = t2
// => error FS0001: Type mismatch. Expecting a int * int    
//    but given a int * string    
//    The type 'int' does not match the type 'string'

And the length must be the same:

let  t1 = (0, 1)
let  t2 = (0, 1, "hello")
t1 = t2
// => error FS0001: Type mismatch. Expecting a int * int    
//    but given a int * int * string    
//    The tuples have differing lengths of 2 and 3

You can get the same issue when pattern matching tuples during binding:

let x,y = 1,2,3
// => error FS0001: Type mismatch. Expecting a 'a * 'b    
//                  but given a 'a * 'b * 'c    
//                  The tuples have differing lengths of 2 and 3

let f (x,y) = x + y
let z = (1,"hello")
let result = f z
// => error FS0001: Type mismatch. Expecting a int * int    
//                  but given a int * string    
//                  The type 'int' does not match the type 'string'

J. Don't use ! as the "not" operator ###

If you use ! as a "not" operator, you will get a type error mentioning the word "ref".

let y = true
let z = !y     //wrong
// => error FS0001: This expression was expected to have 
//    type 'a ref but here has type bool

The fix is to use the "not" keyword instead.

let y = true
let z = not y   //correct

K. Operator precedence (especially functions and pipes) ###

If you mix up operator precedence, you may get type errors. Generally, function application is highest precedence compared to other operators, so you get an error in the case below:

String.length "hello" + "world"
   // => error FS0001:  The type 'string' does not match the type 'int'

// what is really happening
(String.length "hello") + "world"

The fix is to use parentheses.

String.length ("hello" + "world")  // corrected

Conversely, the pipe operator is low precedence compared to other operators.

let result = 42 + [1..10] |> List.sum
 // => => error FS0001:  The type ''a list' does not match the type 'int'

// what is really happening
let result = (42 + [1..10]) |> List.sum

Again, the fix is to use parentheses.

let result = 42 + ([1..10] |> List.sum)

L. let! error in computation expressions (monads) ###

Here is a simple computation expression:

type Wrapper<'a> = Wrapped of 'a

type wrapBuilder() = 
    member this.Bind (wrapper:Wrapper<'a>) (func:'a->Wrapper<'b>) = 
        match wrapper with
        | Wrapped(innerThing) -> func innerThing

    member this.Return innerThing = 
        Wrapped(innerThing) 

let wrap = new wrapBuilder()

However, if you try to use it, you get an error.

wrap {
    let! x1 = Wrapped(1)   // <== error here
    let! y1 = Wrapped(2)
    let z1 = x + y
    return z
    }
// error FS0001: This expression was expected to have type Wrapper<'a>
//               but here has type 'b * 'c

The reason is that "Bind" expects a tuple (wrapper,func), not two parameters. (Check the signature for bind in the F# documentation).

The fix is to change the bind function to accept a tuple as its (single) parameter.

type wrapBuilder() = 
    member this.Bind (wrapper:Wrapper<'a>, func:'a->Wrapper<'b>) = 
        match wrapper with
        | Wrapped(innerThing) -> func innerThing

FS0003: This value is not a function and cannot be applied ##

This error typically occurs when passing too many arguments to a function.

let add1 x = x + 1
let x = add1 2 3
// ==>   error FS0003: This value is not a function and cannot be applied

It can also occur when you do operator overloading, but the operators cannot be used as prefix or infix.

let (!!) x y = x + y
(!!) 1 2              // ok
1 !! 2                // failed !! cannot be used as an infix operator
// error FS0003: This value is not a function and cannot be applied

FS0008: This runtime coercion or type test involves an indeterminate type ##

You will often see this when attempting to use ":?" operator to match on a type.

let detectType v =
    match v with
        | :? int -> printfn "this is an int"
        | _ -> printfn "something else"
// error FS0008: This runtime coercion or type test from type 'a to int    
// involves an indeterminate type based on information prior to this program point. 
// Runtime type tests are not allowed on some types. Further type annotations are needed.

The message tells you the problem: "runtime type tests are not allowed on some types".

The answer is to "box" the value which forces it into a reference type, and then you can type check it:

let detectTypeBoxed v =
    match box v with      // used "box v" 
        | :? int -> printfn "this is an int"
        | _ -> printfn "something else"

//test
detectTypeBoxed 1
detectTypeBoxed 3.14

FS0010: Unexpected identifier in binding ##

Typically caused by breaking the "offside" rule for aligning expressions in a block.

//3456789
let f = 
  let x=1     // offside line is at column 3 
   x+1        // oops! don't start at column 4
              // error FS0010: Unexpected identifier in binding

The fix is to align the code correctly!

FS0010: Incomplete structured construct ##

Often occurs if you are missing parentheses from a class constructor:

type Something() =
   let field = ()

let x1 = new Something     // Error FS0010 
let x2 = new Something()   // OK!

Can also occur if you forgot to put parentheses around an operator:

// define new operator
let (|+) a = -a

|+ 1    // error FS0010: 
        // Unexpected infix operator

(|+) 1  // with parentheses -- OK!

Can also occur if you are missing one side of an infix operator:

|| true  // error FS0010: Unexpected symbol '||'
false || true  // OK

Can also occur if you attempt to send a namespace definition to F# interactive. The interactive console does not allow namespaces.

namespace Customer  // FS0010: Incomplete structured construct 

// declare a type
type Person= {First:string; Last:string}

FS0013: The static coercion from type X to Y involves an indeterminate type ##

This is generally caused by implic

FS0020: This expression should have type 'unit' ##

This error is commonly found in two situations:

  • Expressions that are not the last expression in the block

  • Using wrong assignment operator

FS0020 with expressions that are not the last expression in the block ###

Only the last expression in a block can return a value. All others must return unit. So this typically occurs when you have a function in a place that is not the last function.

let something = 
  2+2               // => FS0020: This expression should have type 'unit'
  "hello"

The easy fix is use ignore. But ask yourself why you are using a function and then throwing away the answer ? it might be a bug.

let something = 
  2+2 |> ignore     // ok
  "hello"

This also occurs if you think you writing C# and you accidentally use semicolons to separate expressions:

// wrong
let result = 2+2; "hello";

// fixed
let result = 2+2 |> ignore; "hello";

FS0020 with assignment ###

Another variant of this error occurs when assigning to a property.

This expression should have type 'unit', but has type 'Y'. 

With this error, chances are you have confused the assignment operator "<-" for mutable values, with the equality comparison operator "=".

// '=' versus '<-'
let add() =
    let mutable x = 1
    x = x + 1          // warning FS0020
    printfn "%d" x

The fix is to use the proper assignment operator.

// fixed
let add() =
    let mutable x = 1
    x <- x + 1
    printfn "%d" x

FS0030: Value restriction ##

This is related to F#'s automatic generalization to generic types whenever possible.

For example, given :

let id x = x
let compose f g x = g (f x)
let opt = None

F#'s type inference will cleverly figure out the generic types.

val id : 'a -> 'a
val compose : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c
val opt : 'a option

However in some cases, the F# compiler feels that the code is ambiguous, and, even though it looks like it is guessing the type correctly, it needs you to be more specific:

let idMap = List.map id             // error FS0030
let blankConcat = String.concat ""  // error FS0030

Almost always this will be caused by trying to define a partially applied function, and almost always, the easiest fix is to explicitly add the missing parameter:

let idMap list = List.map id list             // OK
let blankConcat list = String.concat "" list  // OK

FS0035: This construct is deprecated ##

F# syntax has been cleaned up over the last few years, so if you are using examples from an older F# book or webpage, you may run into this. See the MSDN documentation for the correct syntax.

let x = 10
let rnd1 = System.Random x         // Good
let rnd2 = new System.Random(x)    // Good
let rnd3 = new System.Random x     // error FS0035

FS0039: The field, constructor or member X is not defined ##

This error is commonly found in four situations:

  • The obvious case where something really isn't defined! And make sure that you don't have a typo or case mismatch either.

  • Interfaces

  • Recursion

  • Extension methods

FS0039 with interfaces ###

The key point is that when a interface member is explicitly implemented, it cannot be accessed through a normal class instance, but only through an instance of the interface, so you have to cast to the interface type by using the :> operator.

Here's an example of a class that implements an interface:

type MyResource() = 
   interface System.IDisposable with
       member this.Dispose() = printfn "disposed"

This doesn't work:

let x = new MyResource()
x.Dispose()  // error FS0039: The field, constructor 
             // or member 'Dispose' is not defined

The fix is to cast the object to the interface, as below:

// fixed by casting to System.IDisposable 
(x :> System.IDisposable).Dispose()   // OK

let y =  new MyResource() :> System.IDisposable 
y.Dispose()   // OK

FS0039 with recursion ###

Here's a standard Fibonacci implementation:

let fib i = 
   match i with
   | 1 -> 1
   | 2 -> 1
   | n -> fib(n-1) + fib(n-2)

Unfortunately, this will not compile:

Error FS0039: The value or constructor 'fib' is not defined

The reason is that when the compiler sees 'fib' in the body, it doesn't know about the function because it hasn't finished compiling it yet!

The fix is to use the "rec" keyword.

let rec fib i = 
   match i with
   | 1 -> 1
   | 2 -> 1
   | n -> fib(n-1) + fib(n-2)

Note that this only applies to "let" functions. Member functions do not need this, because the scope rules are slightly different.

type FibHelper() =
    member this.fib i = 
       match i with
       | 1 -> 1
       | 2 -> 1
       | n -> fib(n-1) + fib(n-2)

FS0039 with extension methods ###

If you have defined an extension method, you won't be able to use it unless the module is in scope.

Here's a simple extension to demonstrate:

module IntExtensions = 
    type System.Int32 with
        member this.IsEven = this % 2 = 0

If you try to use it the extension, you get the FS0039 error:

let i = 2
let result = i.IsEven  
    // FS0039: The field, constructor or 
    // member 'IsEven' is not defined

The fix is just to open the IntExtensions module.

open IntExtensions // bring module into scope
let i = 2
let result = i.IsEven  // fixed!

FS0041: A unique overload for could not be determined ##

This can be caused when calling a .NET library function that has multiple overloads:

let streamReader filename = new System.IO.StreamReader(filename) // FS0041

There a number of ways to fix this. One way is to use an explicit type annotation:

let streamReader filename = new System.IO.StreamReader(filename:string) // OK

You can sometimes use a named parameter to avoid the type annotation:

let streamReader filename = new System.IO.StreamReader(path=filename) // OK

Or you can try to create intermediate objects that help the type inference, again without needing type annotations:

let streamReader filename = 
    let fileInfo = System.IO.FileInfo(filename)
    new System.IO.StreamReader(fileInfo.FullName) // OK

FS0049: Uppercase variable identifiers should not generally be used in patterns ##

When pattern matching, be aware of a subtle difference between the pure F# union types which consist of a tag only, and a .NET Enum type.

Pure F# union type:

type ColorUnion = Red | Yellow 
let redUnion = Red  

match redUnion with
| Red -> printfn "red"     // no problem
| _ -> printfn "something else"

But with .NET enums you must fully qualify them:

type ColorEnum = Green=0 | Blue=1      // enum 
let blueEnum = ColorEnum.Blue  

match blueEnum with
| Blue -> printfn "blue"     // warning FS0049
| _ -> printfn "something else"

The fixed version:

match blueEnum with
| ColorEnum.Blue -> printfn "blue" 
| _ -> printfn "something else"

FS0072: Lookup on object of indeterminate type ##

This occurs when "dotting into" an object whose type is unknown.

Consider the following example:

let stringLength x = x.Length // Error FS0072

The compiler does not know what type "x" is, and therefore does not know if "Length" is a valid method.

There a number of ways to fix this. The crudest way is to provide an explicit type annotation:

let stringLength (x:string) = x.Length  // OK

In some cases though, judicious rearrangement of the code can help. For example, the example below looks like it should work. It's obvious to a human that the List.map function is being applied to a list of strings, so why does x.Length cause an error?

List.map (fun x -> x.Length) ["hello"; "world"] // Error FS0072

The reason is that the F# compiler is currently a one-pass compiler, and so type information present later in the program cannot be used if it hasn't been parsed yet.

Yes, you can always explicitly annotate:

List.map (fun x:string -> x.Length) ["hello"; "world"] // OK

But another, more elegant way that will often fix the problem is to rearrange things so the known types come first, and the compiler can digest them before it moves to the next clause.

["hello"; "world"] |> List.map (fun x -> x.Length)   // OK

It's good practice to avoid explicit type annotations, so this approach is best, if it is feasible.

FS0588: Block following this 'let' is unfinished ##

Caused by outdenting an expression in a block, and thus breaking the "offside rule".

//3456789
let f = 
  let x=1    // offside line is at column 3 
 x+1         // offside! You are ahead of the ball!
             // error FS0588: Block following this 
             // 'let' is unfinished

The fix is to align the code correctly.

This is a very important topic ? it is critical that you understand how partial application works. See the series for a more detailed discussion.

See the series for more discussion of parameterless functions.

A number of errors can be caused by this, such as and . The suggested fixes for each of these specific cases are described below, but there are some general principles that can help if the compiler is complaining about missing types or not enough information. These guidelines are:

See also for another issue caused by alignment.

For more details see the MSDN article on .

In F# all interfaces are "explicit" implementations rather than "implicit". (Read the C# documentation on for an explanation of the difference).

See also for another issue caused by alignment.

"thinking functionally"
"thinking functionally"
"automatic generalization"
"explicit interface implementation"
"thinking functionally"
"understanding F# types"
(Jump to the error numbers)
"FS0072: Lookup on object of indeterminate type"
"FS0041: A unique overload for could not be determined"
FS0001: The type 'X' does not match the type 'Y'
FS0003: This value is not a function and cannot be applied
FS0008: This runtime coercion or type test involves an indeterminate type
FS0010: Unexpected identifier in binding
FS0010: Incomplete structured construct
FS0013: The static coercion from type X to Y involves an indeterminate type
FS0020: This expression should have type 'unit'
FS0030: Value restriction
FS0035: This construct is deprecated
FS0039: The field, constructor or member X is not defined
FS0041: A unique overload for could not be determined
FS0049: Uppercase variable identifiers should not generally be used in patterns
FS0072: Lookup on object of indeterminate type
FS0588: Block following this 'let' is unfinished
FS0588: Block following this 'let' is unfinished
FS0010: Unexpected identifier in binding
A. Can't mix floats and ints
A. Can't mix floats and ints.
B. Using the wrong numeric type.
C. Passing too many arguments to a function.
C. Passing too many arguments to a function.
C. Passing too many arguments to a function.
D. Passing too few arguments to a function.
E. Straightforward type mismatch.
F. Inconsistent returns in branches or matches.
G. Watch out for type inference effects buried in a function.
H. Have you used a comma instead of space or semicolon?
I. Tuples must be the same type to be compared.
J. Don't use ! as the "not" operator.
K. Operator precedence (especially functions and pipes).
L. let! error in computation expressions.