A type extension is F#’s syntax for augmenting an existing type with new members – similar in spirit to C# and VB’s extensions methods. I employ type extensions to make life a bit easier when working with .NET libraries from F#. For example, I might make System.IO.Stream.ReadByte() a bit more natural to use from F# by writing a type extension like so:
type Stream with
match x.ReadByte() with
| -1 -> None
| b -> Some(byte b)
The function above captures the semantic of the int returned by Stream.ReadByte(), which might be either an unsigned byte or -1 when at the end of the stream. In F#, this semantic is easily represented as an Option and then more easily consumed by other F# code.
So yes, type extensions are dead useful. However, if you’re writing a type extension for a generic type, you’ll need to include all of the type parameters and there constraints. Normally this isn’t a big deal, but I just spent several minutes tearing (more) of my hair out trying to determine the correct incantation to write a type extension for Nullable.
In the documentation, Nullable is declared with two generic constraints: a struct constraint and a default constructor constraint. However, that’s not quite enough to make the F# compiler happy. F# expects a type constraint to System.ValueType as well. So, three constraints are needed to declare a type extension for Nullable.
type Nullable<'a when 'a : struct
and 'a : (new : unit -> 'a)
and 'a :> System.ValueType> with
member x.AsOption() =
match x.HasValue with
| true -> Some(x.Value)
| false -> None
Of course, once you’ve written that, you shouldn’t need to write it again and you can use the extension to handle Nullable a bit more naturally in F#.
match someNullable.AsOption() with
| Some(n) -> printfn "%d" n
| None -> ()