InfoQ

InfoQ

News

My Bookmarks

Login or Register to enable bookmarks for unlimited time.

The content has been bookmarked!

There was an error bookmarking this content! Please retry.

The Many Types of Null in F#

Posted by Jonathan Allen on Jun 04, 2009

Sections
Development,
Architecture & Design
Topics
Language Design ,
.NET
Tags
F# ,
Functional Programming

F# was supposed to free us of the tyranny of the unchecked null. Alas not only does it not do that, it introduces several more kinds of null. First consider this all too common problem in C# code.

int GetLength(string value) { return value.Length; }

Unless you have code analysis turned on and the function is publically available, you don’t get so much as a warning that this function may throw a NullReferenceException. Now let’s consider the F# equivalent.

let GetLength (value : string) = a.Length

Just like the C# version, this will throw a NullReferenceException if you accidentally pass a null to it. But unlike C#, you don’t even get so much as a warning.

Next up are nullable structures. Our test code in C# is followed by the F# equivalent.

static public bool IsPositive(int? value) { return value.Value > 0; }

let IsPositive( value : Nullable<int>) = value.Value > 0

Again, both versions are susceptible to throwing exceptions. In this case an InvalidOperationException.

Now that we established that using traditional types are just as dangerous as every, we turn to the new option types. First we will recode GetLength using an “option” instead of a normal string.

let GetLength2 (value : option<int>) = value.Value.Length

Now we have the possibility for two different exceptions. If you pass “None” to the function you will get an InvalidOperationException. If you pass a “Some(null)” to the function, you will get a NullReferenceException. And again, there are no compiler warnings telling you that your code can fail.

F# also adds the concept of triple nulls. Since you can nest options inside options, you can write the downright silly functions such as:

let GetLength3 (value : option<string>) = value.Value.Value.Length

let IsPositive( value : option<int>) = value.Value.Value > 0

When using F# types instead of normal CLS types, things are somewhat better. Classes defined in F# cannot be assigned the value null. However, they can still be wrapped in an option type, throwing away the null-safety and bringing us back to the problem of no decent compiler warnings.

Nothing wrong with the functions by Chris Pellett Posted
Re: Nothing wrong with the functions by Jonathan Allen Posted
Re: Nothing wrong with the functions by Chris Pellett Posted
Re: Nothing wrong with the functions by Jonathan Allen Posted
Re: Nothing wrong with the functions by Michael Robin Posted
I guess I don't agree by Sadek Drobi Posted
  1. Back to top

    Nothing wrong with the functions

    by Chris Pellett

    I don't see any problem with a function like:
    let getLength (s: string) = s.length

    I'm betting your problem is coming from where your value is coming from (that is then being passed to this function). Somewhere along the lines you are retrieving the value for "s" - and that is where you should be doing your option checking.

    For example, say I have this:
    let getString input = ...

    Function "getString" should actually return an Option<String>. Then, when I use it, I should do the proper pattern matching checks, like:
    let length =
    match getString foo with
    | Some(s) -> getLength s
    | None -> getLength "" // or some other action goes here

    So, to sum up - your example "getLength" function is actually correct and shouldn't do the null checking (since it is expecting a non-null object)</string>

  2. Back to top

    Re: Nothing wrong with the functions

    by Jonathan Allen

    I see I wasn't completely clear.

    Options and nulls are not the same thing. The Some(s) pattern will match on a Some(null), meaning you are passing a null to getLength(string) function.

    If you are using an "string option" you need to make two checks. First pattern match to see if it is Some(s) or None. Then make a second check to determine if it is Some(null) or Some("actual string").

  3. Back to top

    Re: Nothing wrong with the functions

    by Chris Pellett

    But again, you are defaulting to NULLs when you should in fact be using the options. Why would you return a NULL value instead of an Option? The whole point of Options is to encourage you not to.

    If you choose to go the NULL route, that is equivalent to an argument stating that F# fails functional principles because of the imperative code you write. F# doesn't fail at NULL support - it exceeds if you follow the Functional approach (of Options)

  4. Back to top

    Re: Nothing wrong with the functions

    by Jonathan Allen

    Why would you return a NULL value instead of an Option?


    1. Because someone screwed up.

    I'm willing to bet that 99% of the time, a function that returns a null is doing so by mistake.

    2. Because you have to deal with the BCL.

    F# users cannot ignore the Base Class Library. Even if you manage to ignore the rest of the .NET libraries, you still have to work with things like System.String. This in turn means you have to deal with wild nulls poping up in your otherwise clean code.

  5. Back to top

    Re: Nothing wrong with the functions

    by Michael Robin

    Try Option.Map or a variation on the Maybe Modad.

  6. Back to top

    I guess I don't agree

    by Sadek Drobi

    I would say that it is unusual in F# to use null and null is left only for compatibility with the rest of .net. So normally you'll not have to check for null when calling api's written in F#. Yes discipline is needed but this is always a tough call since you don't want to break compatibility with exiting libraries that form a part of F#'s power. Same problem you'd find in Scala but personally I can live with it. I wouldn't imagine OTH converting Strings in and out between F# and other languages so I guess I am saying I am quite satisfied with the trade off.

    About nesting Option types it can be very meaningful and desired in a lot of situations (exactly like lists of lists) and it is easy (I doubt it is already implemented) to have a join function to flatten the type (monadic join like the one of List monad).

    Sadek

Educational Content

10 tips on how to prevent business value risk

One category of risk that project teams need to ensure they address is business value failure – delivering a product that fails to provide value for the business investor.

Interview: Software Systems Architecture: Working With Stakeholders Using Viewpoints and Perspectives

InfoQ spoke to the authors of Software Systems Architecture on a couple of new topics, the System Context viewpoint and Agile, which have been added to the second edition.

Beauty Is in the Eye of the Beholder

Alex Papadimoulis discusses ugly code, where it comes from, how to avoid it, and how to get rid of it.

Architecting Visa for Massive Scale and Continuous Innovation

John Davies examines Visa’s architecture and shows how enterprises have architected complex integrations incorporating Hadoop, memcached, Ruby on Rails, and others to deliver innovative solutions.

Max Protect: Scalability and Caching at ESPN.com

Sean Comerford unveils ESPN.com’s architecture, what components are used and why, and the current changes the website goes through.

The Seven Deadly Sins of Enterprise Agile Adoption

Are there repeated patterns of failure on Enterprise Agile Enablement efforts? Sanjiv and Arlen discuss Seven Deadly Sins to avoid when adopting Agile in an enterprise.

Questions for an Enterprise Architect

Erik Dörnenburg answers: What is Enterprise and Evolutionary Architecture?, discussing 4 issues: Turning strategy into execution, Ensuring conformance, Where do the architects sit? Buying or building?

Wrap Your SQL Head Around Riak MapReduce

Sean Cribbs explains what Map-Reduce and Riak are, why and how to use Map-Reduce with Riak, and how to convert SQL queries into their Map-Reduce equivalents.