Built-in .NET types
Ints, strings, bools, etc
In this post we'll take a quick look at how F# handles the standard types that are built into .NET.
Literals
F# uses the same syntax for literals that C# does, with a few exceptions.
I'll divide the built-in types into the following groups:
miscellaneous types (
bool
,char
, etc. )string types
integer types (
int
,uint
andbyte
, etc)float types (
float
,decimal
, etc)pointer types (
IntPtr
, etc)
The following tables list the primitive types, with their F# keywords, their suffixes if any, an example, and the corresponding .NET CLR type.
Miscellaneous types
Object
Unit
Bool
Char (Unicode)
Char (Ascii)
Keyword
obj
unit
bool
char
byte
Suffix
B
Example
let o = obj()
let u = ()
true false
'a'
'a'B
.NET Type
Object
(no equivalent)
Boolean
Char
Byte
Object and unit are not really .NET primitive types, but I have included them for the sake of completeness.
String types
String (Unicode)
Verbatim string (Unicode)
Triple quoted string (Unicode)
String (Ascii)
Keyword
string
string
string
byte[]
Suffix
Example
"first\nsecond line"
@"C:\name"
"""can "contain"" special chars"""
"aaa"B
.NET Type
String
String
String
Byte[]
The usual special characters can be used inside normal strings, such as \n
, \t
, \\
, etc. Quotes must be escaped with a backslash: \'
and \"
.
In verbatim strings, backslashes are ignored (good for Windows filenames and regex patterns). But quotes need to be doubled.
Triple-quoted strings are new in VS2012. They are useful because special characters do not need to be escaped at all, and so they can handle embedded quotes nicely (great for XML).
Integer types
8 bit (Signed)
8 bit (Unsigned)
16 bit (Signed)
16 bit (Unsigned)
32 bit (Signed)
32 bit (Unsigned)
64 bit (Signed)
64 bit (Unsigned)
Unlimited precision
Keyword
sbyte
byte
int16
uint16
int
uint32
int64
uint64
bigint
Suffix
y
uy
s
us
u
L
UL
I
Example
99y
99uy
99s
99us
99
99u
99L
99UL
99I
.NET Type
SByte
Byte
Int16
UInt16
Int32
UInt32
Int64
UInt64
BigInteger
BigInteger
is available in all versions of F#. From .NET 4 it is included as part of the .NET base library.
Integer types can also be written in hex and octal.
The hex prefix is
0x
. So0xFF
is hex for 255.The octal prefix is
0o
. So0o377
is octal for 255.
Floating point types
32 bit floating point
64 bit (default) floating point
High precision floating point
Keyword
float32, single
float, double
decimal
Suffix
f
m
Example
123.456f
123.456
123.456m
.NET Type
Single
Double
Decimal
Note that F# natively uses float
instead of double
, but both can be used.
Pointer types
Pointer/handle (signed)
Pointer/handle (unsigned)
Keyword
nativeint
unativeint
Suffix
n
un
Example
0xFFFFFFFFn
0xFFFFFFFFun
.NET Type
IntPtr
UIntPtr
Casting between built-in primitive types
Note: this section only covers casting of primitive types. For casting between classes see the series on object-oriented programming.
There is no direct "cast" syntax in F#, but there are helper functions to cast between types. These helper functions have the same name as the type (you can see them in the Microsoft.FSharp.Core
namespace).
So for example, in C# you might write:
var x = (int)1.23
var y = (double)1
In F# the equivalent would be:
let x = int 1.23
let y = float 1
In F# there are only casting functions for numeric types. In particular, there is no cast for bool, and you must use Convert
or similar.
let x = bool 1 //error
let y = System.Convert.ToBoolean(1) // ok
Boxing and unboxing
Just as in C# and other .NET languages, the primitive int and float types are value objects, not classes. Although this is normally transparent, there are certain occasions where it can be an issue.
First, lets look at the transparent case. In the example below, we define a function that takes a parameter of type Object
, and simply returns it. If we pass in an int
, it is silently boxed into an object, as can be seen from the test code, which returns an object
not an int
.
// create a function with parameter of type Object
let objFunction (o:obj) = o
// test: call with an integer
let result = objFunction 1
// result is
// val result : obj = 1
The fact that result
is an object, not an int, can cause type errors if you are not careful. For example, the result cannot be directly compared with the original value:
let resultIsOne = (result = 1)
// error FS0001: This expression was expected to have type obj
// but here has type int
To work with this situation, and other similar ones, you can convert a primitive type to an object directly, by using the box
keyword:
let o = box 1
// retest the comparison example above, but with boxing
let result = objFunction 1
let resultIsOne = (result = box 1) // OK
To convert an object back to an primitive type, use the unbox
keyword, but unlike box
, you must either supply a specific type to unbox to, or be sure that the compiler has enough information to make an accurate type inference.
// box an int
let o = box 1
// type known for target value
let i:int = unbox o // OK
// explicit type given in unbox
let j = unbox<int> o // OK
// type inference, so no type annotation needed
let k = 1 + unbox o // OK
So the comparison example above could also be done with unbox
. No explicit type annotation is needed because it is being compared with an int.
let result = objFunction 1
let resultIsOne = (unbox result = 1) // OK
A common problem occurs if you do not specify enough type information -- you will encounter the infamous "Value restriction" error, as shown below:
let o = box 1
// no type specified
let i = unbox o // FS0030: Value restriction error
The solution is to reorder the code to help the type inference, or when all else fails, add an explicit type annotation. See the post on type inference for more tips.
Boxing in combination with type detection
Let's say that you want to have a function that matches based on the type of the parameter, using the :?
operator:
let detectType v =
match v with
| :? int -> printfn "this is an int"
| _ -> printfn "something else"
Unfortunately, this code will fail to compile, with the following error:
// 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
Last updated
Was this helpful?