Saturday, February 18, 2012

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    -> ()
posted on Saturday, February 18, 2012 3:44:30 PM (Pacific Standard Time, UTC-08:00)  #    Comments [9]

kick it on