F# null trick

fsharp_null_250I have faced with an interesting F# behaviour on the null check. I tried to make a MongoDB query using C# Driver LINQ but F# compiler said that I could not compare the result with null, because result could not be null, but I am sure that query can return nothing =).

I am going to show you the same behaviour with classic LINQ. Please, look at the source code:

open System
open System.Linq
open System.Collections.Generic

type typeA = {Variable:int}

let x = List<typeA>().FirstOrDefault()

if (x = null) then None else Some(x)

If you evaluate first 7 lines of code, you will see that x is equal to null, List is empty, so the value of our LINQ query will be default(null). But, if you try to execute the line number 9, F# compiler will say that it cannot be compiled, because the typeA does not have a null as a proper value, but x is null! Hmm… real magic…

null_trick.fsx(9,9): error FS0043: The type ‘typeA’ does not have ‘null’ as a proper value

Actually, there is an excellent MSDN article “Null Values (F#)“, which should be read carefully. At the first look you may think that AllowNullLiteralAttribute is an answer and try to modify the typeA in the following way:

[<AllowNullLiteral>]
type typeA = {Variable:int}

But you can not have this code compiled, because

null_trick.fsx(6,6): error FS0934: Records, union, abbreviations and struct types cannot have the ‘AllowNullLiteral’ attribute

CLIMutableAttrubute is not an option too, because it does not affect null-behaviour. At the end of the “Null Values (F#)” article you will see an interesting example with a null check:

match box value with
| null -> printf "The value is null."
| _ -> printf "The value is not null."

Boxing is an answer! We need it to perform a null check for an arbitrary value. So, the working example will look like:

open System
open System.Linq
open System.Collections.Generic

type typeA = {Variable:int}

let x = List<typeA>().FirstOrDefault()

if (box x = null) then None else Some(x)

F# world is full of magic. Wonders await us at every turn.

P.S. Read more tales of null in Steffen Forkmann’s blog.

10 thoughts on “F# null trick

  1. Hi
    Did you mean “if (box x = null) then None else Some(x)”?
    Or maybe I am missing the point?
    Best regards Robert

  2. I just made an isNull function that’s really just a concise wrapper for Object.ReferenceEquals(foo, null)

  3. Sergey,

    For sum types, you can’t use [<AllowNullLiteral>], but you can use [<CompilationRepresentation>] (carefully!) to interop with some functions that return null without actually making the type use the null constraint (as far as the F# compiler is concerned).

    I wrote a little example below — I don’t know if it’ll work for what you want, but it might just be interesting anyway.

    Cheers,
    Jack

    [<CompilationRepresentation(CompilationRepresentationFlags.UseNullAsTrueValue)>]
    type typeB = Empty | Result of int

    let x = List().FirstOrDefault()
    let y = match x with Empty -> None | Result v -> Some v

    1. Grrr, WordPress ate the formatting on the attribute. Trying again (remove the spaces):

      [<CompilationRepresentation(CompilationRepresentationFlags.UseNullAsTrueValue)>]
      type typeB = Empty | Result of int

      let x = List().FirstOrDefault()
      let y = match x with Empty -> None | Result v -> Some v

      1. Sorry — looks like WordPress doesn’t like code in the comments. Anyway, if you decorate the type with CompilationRepresentationAttribute and pass it CompilationRepresentationFlags.UseNullAsTrueValue that should do the trick.

  4. If I evaluate the first code snippet, I get the following:

    let x = List().FirstOrDefault()
    —-^

    stdin(27,5): error FS0030: Value restriction. The value ‘x’ has been inferred to have generic type
    val x : ‘_a when ‘_a : equality and ‘_a : null
    Either define ‘x’ as a simple data term, make it a function with explicit arguments or, if you do not intend for it to be generic, add a type annotation.

    If I add a type annotation like this:

    let x: obj = List().FirstOrDefault()

    the code compiles just fine.

    So, what’s the problem? Am I missing anything?

    1. Sorry, it is WordPress formatting once again! WordPress does not like < > brackets =(
      In the first sample List should be typed! (already fixed)
      Thanks.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s