Click here to Skip to main content
Click here to Skip to main content

Tagged as

Go to top

F#9 : Option types

, 13 Mar 2014
Rate this:
Please Sign up or sign in to vote.
If C# there is a concept of null for reference types and Nullable class for value types (structs). For value type this can take one of the following 2 forms (for demo purposes I am using a int type here, but the type could be any value type) Nullable int?   These are both equivalent. […]

If C# there is a concept of null for reference types and Nullable<T> class for value types (structs). For value type this can take one of the following 2 forms (for demo purposes I am using a int type here, but the type could be any value type)

  • Nullable<int>
  • int?

These are both equivalent.

The Nullable<T> class exposes a few helper properties and methods that make it easier to work with null and value types. These are as following

Property/Method Description
HasValue Gets a value indicating whether the current Nullable<T> object has a valid value of its underlying type.
Value Gets the value of the current Nullable<T> object if it has been assigned a valid underlying value.
GetValueOrDefault() Retrieves the value of the current Nullable<T> object, or the object’s default value.
GetValueOrDefault(T) Retrieves the value of the current Nullable<T> object, or the specified default value.

In F# there is something slightly different to this, in the form of an Option type, which is more F# friendly type, that prefers to not deal in terms of null and not null, but rather prefers to deal with things in terms of “Can hold a value of a type” or “May not have a value”. This does sound like Nullable<T> I give you that, but it is a F# type after all, so you can expect it to ok to use in F# things, such as pattern matching etc etc.

Another thing to note with F’’#s Option type is that is may be used for any type, not just value types. This is different from the .NET Nullable<T> which may only be used with value types.

The value None is used when an option does not have an actual value. Otherwise, the expression Some( … ) gives the option a value. The values Some and None can obviously be used in pattern matching, which we will see an example of in this post.

Like Nullable<T>, the F# Option type has several helper properties / methods, which are shown in the table below.

Property/Method Type Description
None ‘T option A static property that enables you to create an option value that has the None value.
IsNone bool Returns true if the option has the None value.
IsSome bool Returns true if the option has a value that is not None.
Some ‘T option A static member that creates an option that has a value that is not None.
Value ‘T Returns the underlying value, or throws a NullReferenceException if the value is None.

Additional Helpers

There is an Option module that contains a few more helpers for dealing with Option types in F#. You can read more about this here: http://msdn.microsoft.com/en-us/library/ee370544.aspx

Creating Options

So now that we know what Option types are, how do we go about creating them. Lets see some examples shall we. Note in this example I has used the helper methods IsSome / IsNone. Personally I think pattern matching is a better way to go, as it will make you match against all cases including the None case.

In fact I will show you just how easy it is to get something wrong, when dealing with Option types, should you choose to use the helper methods, but first lets see an example of the ok case (though pattern matching is still preferred).

So lets say we had this code:

let someInt = Some(43)
let someString = Some("cat")

let printTheOption (opt :Option) =
    printfn "Actual Option=%A" opt
    printfn "IsNone=%b, IsSome=%b Value=%A\r\n\r\n" opt.IsNone opt.IsSome opt.Value
        
printfn "let someInt = Some(43)"
printfn "let someString = Some(\"cat\")"
printfn "printTheOption (someInt)"
printTheOption someInt
printfn "printTheOption (someString)"
printTheOption someString

This would give us the following results (as expected)

image

But what about we try that again using this code, where we have a None for the value of the Option we will pass to the “printTheOption” function:

let demoNone = None

let printTheOption (opt :Option) =
    printfn "Actual Option=%A" opt
    printfn "IsNone=%b, IsSome=%b Value=%A\r\n\r\n" opt.IsNone opt.IsSome opt.Value
        
printfn "let demoNone = None"
printfn "printTheOption demoNone"
printTheOption demoNone

This time if we attempt to run this code we get this result:

image

As you can see we have an issue here. The issues is that we tried to get the passed in Option value using the Option.Value helper property, which is this case was None, so we got a NullReferenceException. This is shown in the table above,when you use the helper properties and methods you may get Exceptions thrown such as this one. Ok you could use the IsNone method, but that would then ripple through your code, and you would be forever checking the value using this, when you could just use a nice clean pattern match, job done.

If you can’t relate to this, ask yourself how many times you have had to check for null when using C#, for me that is a fair amount. This has even given rise to people bringing functional constructs like the Maybe Null Monad into regular.NET code, but this certainly isn’t part of the BCL.

Luckily we can avoid these issues by using pattern matching which we look at next.

Pattern Matching Options

So now that we have seen the dangers of using the helper methods/properties, and how easy it is to forget about the None case. So lets now turn our attention to how we could avoid these Exceptions by simply using some simple pattern matching such as that shown here:

let printTheOption (opt :Option) = 
    match opt with
    | Some a -> printfn "opt is Some, and has value %A" a
    | None -> printfn "opt is None"
        

let demoNone = None
let someInt = Some 1
let someString = Some "crazy dude in the monkey house"

printTheOption demoNone
printTheOption someInt
printTheOption someString

My personal feeling is that the pattern matching is actually way more readable, than code that would be littered with IsSome / IsNone all over the place. That is of course a personal feeling, but the fact that we have covered all our bases here in this one simple pattern matched function, can not be ignored.

Here is the result of running this:

image

Option vs Null

So we have talked about F#s Option compared to Nullabe<T> and we know that an Option type can be used with any type whilst Nullable<T> can only be used with value types (structs). But what about Option types versus regular reference types in .NET. Well one huge win for F# Option is that when you use a reference type in .NET you are really dealing with a pointer reference, which as such can be set to null. The object type however is still the same as it was declared as, which may hold a valid reference to an object on the heap, or may be null.

So it is total ok to write something like this

string s1 = "cats";
int s1Length = s1.Length;

string s2 = null;
int s2Length = s2.Length;

This will compile just fine, for the reasons I stated above. However when we run this we will get a NullReferenceException, for which we would apply defensive programming to protect all code from the possible presence of null. This does become tedious pretty quickly even if you have a nice little guard class that will test a value and handle it / throw some more meaningful Exception.

This small screen shot uses LinqPad so it may look a bit strange if you have not seen LinqPad before, but trust me you would still get the same result in a different IDE.

image

Now lets what the equivalent code would look like in F#, which would be this

let s1 = "Cats"
let s1Length = s1.Length;
      
let s2 = None
let s2Length = s2.Length;

//excplicily string typed None
let s3 = Option.None
let s3Length = s3.Length;

Here is what this looks like in Visual Studio. It can be seen that this is an immediate compile time error, where sis considered a completely different type, and as such has no concept of a “Length” property.

Equality With Options

Option types are considered equal they hold the same type, and that the type they hold are equal, which is subject to the equality rules of the held type.

So this sort of thing would cause an immediate compile time error in F#

let o1 = Some 1
let o2 = Some "Cats"
     
printfn "o1=o2 : %b" (o1 = o2)

This will give this result

image

Whilst this would work as expected since the types are the same

let o1 = Some "Cats"
let o2 = Some "Cats"
let o3 = Some "cats"
     
printfn "o1=o2 : %b" (o1 = o2)    
printfn "o2=o3 : %b" (o2 = o3)   

Which yields this result

image

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Sacha Barber
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)
 
- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence
 
Both of these at Sussex University UK.
 
Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web03 | 2.8.140916.1 | Last Updated 14 Mar 2014
Article Copyright 2014 by Sacha Barber
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid