Asbestos Supply

2011-07-28 F#, System.Linq extensions and the case of the missing null

I posted a question to StackOverflow yesterday and will describe the issue and resolution in this post.

F# is a funny language in that it is a functional (I suppose it's multi-paradigm but very strong emphasis on functional) language built on the .NET platform which has a very different style as a whole. I had an interesting issue the other day where I noticed a null value at runtime being returned from a .NET component in a place that F# was certain null values could not exist.

In my case this was Entity Framework.  I followed this walkthrough to create F# types that mapped to my database using Entity Framework Code First.  I wrote a query that ended in .FirstOrDefault() and here is where the problem started:

let result = context
    .SearchResults
    .Where((fun (r:SearchResult) -> r.Program = request.Program))
    .OrderByDescending((fun r -> r.AcquisitionDate))
    .FirstOrDefault()

Now .FirstOrDefault() is implemented such that it returns either the first element if one exists, or default(T) if not.  Well my SearchResult type is defined in F# and is just a regular class so the default(typeof(SearchResult)) is equal to null.  However, F# doesn't allow nulls be default - due to its functional nature it doesn't by default support null values (which I've come to really appreciate when I'm writing 'pure' F#).  So when the database has no data, what value do you think is bound to result?

If you guessed null you were right. But I just said that F# doesn't support null.  So the following code does not compile:

match result with
    | null -> () // do nothing since this doesn't compile anyway
    | price -> () // do nothing since this doesn't compile anyway

the compilation error states

the type 'SearchResult' does not have 'null' as a proper value

Interesting.  So SearchResult doesn't support null.  Even more interesting is if you comment out the bad code and run it you'll see that result which is of type SearchResult is indeed bound to null.

It seems to me that in a world of uncertainty – a world of mixed paradigms - F# assumes a convention.  F# assumes that every component is as functionally pure as it is - though it is made to interoperate with components that are known not to be.

It turns out that the fix to this is pretty simple.  As stated in the first paragraph in the MSDN article on nulls in F#, F# has an AllowNullLiteral attribute. I decorated my EF types with this and then the compiler allowed me to compare my results value to null.  How imperative!