Monday, June 9, 2008

Using F# option types in C#

This post shows a small example of how to use F# option type instances in C#.

Option types

The F# option type provides a very nice way for defining variables that can may or may not hold a value. For example, in the following class definition, the description field is optional .


type Product(name : string, description : string option) =
class

member this.Name
with get() = name

member this.Description
with get() = description

end


F# Optional types are constructed using the Some and None constructors. For example:


let p1 = Product("Foo",Some "A description")
let p2 = Product("Goo",None)


Optional types are available in many functional languages, for example: Haskell's Maybe, Scala's Option, OCaml's option, among others.

Using option types in C#

Manipulating a option instance from C# requires some considerations since it is defined in F# as a discriminated union. Some useful documentation on how to manipulate discriminated unions from C# includes: Creating and Consuming F# discriminated unions from F#'s manual and this entry from the hubFS.

An instance of the class defined above can be created in C# like this:


Product p = new Product("Foo", Option<string>.Some("A description"));
Product p2 = new Product("Goo", Option<string>.None);


As shown here the Option<A> class (defined in Microsoft.FSharp.Core) provide static methods to construct both cases of the discriminated union.

Several techniques can be used to verify if an instance of Option<A> was created with Some or None. For example, there are static methods called get_IsNone or get_Some which can be used as follows:


if (Option<string>.get_IsNone(p.Description))
{
...
}
...
if (Option<string>.get_IsSome(p.Description))
{
...
}


Another alternative is to use the GetTag method as follows:


if(Option<string>.GetTag(p.Description) == Option<string>.tag_Some) {
...
}


Extension methods

C# extension methods can be used to avoid calling static methods on the Option<A> class. For example:


static class Utility
{
public static bool IsSome(this Option<string> option)
{
return Option<string>.get_IsSome(option);
}

public static bool IsNone(this Option<string> option)
{
return Option<string>.get_IsNone(option);
}
}


Having these extension methods the code could be changed to:



if (p.Description.IsSome())
{
...
}

if (p2.Description.IsSome())
{
...
}


C# extension methods could also be implemented directly in F# . This could be accomplished by applying the System.Runtime.CompilerServices.ExtensionAttribute attribute. For example:


[<System.Runtime.CompilerServices.Extension>]
module Methods =

[<System.Runtime.CompilerServices.Extension>]
let Exists(opt : string option) =
match opt with
| Some _ -> true
| None -> false


This method could be used in C# only by adding the namespace (using using) to the file where it will be used.


if (p2.Description.Exists())
{
...
}