Troubleshooting F#
Why won't my code compile?
Last updated
Was this helpful?
Why won't my code compile?
Last updated
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.
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.
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.
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.
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.
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.
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).
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:
The correct construction is to use the "not" keyword. Think SQL or VB syntax rather than C syntax.
And for "not equal", use "<>", again like SQL or VB.
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.
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.
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.
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.
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.
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.
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
Unlike C# and most imperative languages, ints and floats cannot be mixed in expressions. You will get a type error if you attempt this:
The fix is to cast the int into a float
first:
This issue can also manifest itself in library functions and other places. For example, you cannot do "average
" on a list of ints.
You must cast each int to a float first, as shown below:
You will get a "not compatible" error when a numeric cast failed.
One possible fix is to cast it if appropriate.
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
.
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.
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.
The simplest case is that you have the wrong type, or you are using the wrong type in a print format string.
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.
Obviously, the straightforward fix is to make each branch return the same type.
Remember that if an "else" branch is missing, it is assumed to return unit, so the "true" branch must also return unit.
If both branches cannot return the same type, you may need to create a new union type that can contain both types.
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.
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.
If you are new to F#, you might accidentally use a comma instead of spaces to separate function arguments:
The fix is: don't use a comma!
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#:
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:
And the length must be the same:
You can get the same issue when pattern matching tuples during binding:
If you use !
as a "not" operator, you will get a type error mentioning the word "ref".
The fix is to use the "not" keyword instead.
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:
The fix is to use parentheses.
Conversely, the pipe operator is low precedence compared to other operators.
Again, the fix is to use parentheses.
Here is a simple computation expression:
However, if you try to use it, you get an error.
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.
This error typically occurs when passing too many arguments to a function.
It can also occur when you do operator overloading, but the operators cannot be used as prefix or infix.
You will often see this when attempting to use ":?
" operator to match on a type.
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:
Typically caused by breaking the "offside" rule for aligning expressions in a block.
The fix is to align the code correctly!
Often occurs if you are missing parentheses from a class constructor:
Can also occur if you forgot to put parentheses around an operator:
Can also occur if you are missing one side of an infix operator:
Can also occur if you attempt to send a namespace definition to F# interactive. The interactive console does not allow namespaces.
This is generally caused by implic
This error is commonly found in two situations:
Expressions that are not the last expression in the block
Using wrong assignment operator
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.
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.
This also occurs if you think you writing C# and you accidentally use semicolons to separate expressions:
Another variant of this error occurs when assigning to a property.
With this error, chances are you have confused the assignment operator "<-
" for mutable values, with the equality comparison operator "=
".
The fix is to use the proper assignment operator.
This is related to F#'s automatic generalization to generic types whenever possible.
For example, given :
F#'s type inference will cleverly figure out the generic types.
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:
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:
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.
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
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:
This doesn't work:
The fix is to cast the object to the interface, as below:
Here's a standard Fibonacci implementation:
Unfortunately, this will not compile:
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.
Note that this only applies to "let
" functions. Member functions do not need this, because the scope rules are slightly different.
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:
If you try to use it the extension, you get the FS0039 error:
The fix is just to open the IntExtensions
module.
This can be caused when calling a .NET library function that has multiple overloads:
There a number of ways to fix this. One way is to use an explicit type annotation:
You can sometimes use a named parameter to avoid the type annotation:
Or you can try to create intermediate objects that help the type inference, again without needing type annotations:
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:
But with .NET enums you must fully qualify them:
The fixed version:
This occurs when "dotting into" an object whose type is unknown.
Consider the following example:
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:
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?
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:
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.
It's good practice to avoid explicit type annotations, so this approach is best, if it is feasible.
Caused by outdenting an expression in a block, and thus breaking the "offside rule".
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.