7

I am reading the book "Professional F# 2.0" The author shows the following code

let a string : option = None
if a.IsNone then
    System.Console.WriteLine("a is none")
else
    System.Console.WriteLine("a is some");;

then says

"this makes the use of Option vastly superior to the use of null and goes a long way towards removing a significant source of exceptions thrown at runtime"

ok. so I write

System.Console.WriteLine(a.GetType());;

And I get

System.NullReferenceException: Object reference not set to an instance of an object. at System.Object.GetType() at .$FSI_0008.main@() Stopped due to error

And I am like 'un!!!"

How is really doing a

if a.isSome then
    do bla bla

any different from

if a != null then
   do bla bla

So I don't see how is the programmer being saved from NullPointers

PS: NullPointerException has caused me lot of grief in the past.

Knows Not Much
  • 30,395
  • 60
  • 197
  • 373
  • 4
    The code you're quoting from that book is bad style. Don't do that. The null reference exception from `None` is unfortunate. The benefit of no-nulls applies to the ML subset of F# where it really does help a lot. – J D Jul 04 '12 at 19:02

2 Answers2

9

The F# compiler does not prevent you from NullReferenceException completely. When you work with types defined in .NET, then you can still get null value, because there is no way F# could prevent that.

However, when you work with types declared in F#, then the compiler does not allow creating null values of that type, and so it avoids NullReferenceException in that case. For example, the following code does not compile:

type Person(name:string) = 
  member x.Name = name

// 'Person' is a type declared in F#, so the argument cannot be 'null' in safe code
let printName (person:Person) = 
  printfn "%s" person.Name

// Compiler error here - 'null' is not a valid value of 'Pereson' type
printName null

When you use option<Person> as an argument, then you have to explicitly check for None and Some cases. This is best done using match, which checks that you're not missing any of the cases. For example:

let printName (person:option<Person>) = 
  match person with
  // You get a warning here, saying that you're not handling the 'None' case!
  | Some person -> printfn "%s" person.Name

The warning tells you that you should add case handling None. You can still compile the code, but you will not get NullReferenceException when working with F# types if you do not ignore warnings.

See also this great, related StackOverflow post.

Community
  • 1
  • 1
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • "when you work with types declared in F#, then the compiler does not allow creating null values of that type". The question contains the counterexample `None` which is a value of an F# type that is represented as `null`. – J D Jul 04 '12 at 18:22
  • @JonHarrop - that's kind of a corner case, though, since the representation is normally only observed by calling .NET methods defined on `object`. For instance, calling `None.IsNone` doesn't throw a `NullReferenceException`. – kvb Jul 04 '12 at 18:41
  • @JonHarrop That's not true. From the language perspective, `option<'T>` does not allow `null` as a value (try `let (a:option) = null`). Compiled representation is a different thing, but it only matters for interop. – Tomas Petricek Jul 04 '12 at 23:01
  • @JonHarrop **EDIT** Oh yes, I see your point - the example in the question is a place where the compiled representation "leaks out". – Tomas Petricek Jul 04 '12 at 23:49
  • @kvb Calling `printf "%A" [None]` prints `[null]` instead of `[None]`. – J D Jul 05 '12 at 08:16
  • @JonHarrop - you're right, the null representation leaks through in a few other places too, which is unfortunate. – kvb Jul 05 '12 at 14:31
5

To add to the answer by Tomas, a major benefit of Option type lies in the higher order functions that it supports, that give you more brevity and safety. You might find my blog post on the topic useful.

missingfaktor
  • 90,905
  • 62
  • 285
  • 365