Handling Null Values in F#
Solution 1
If you don't want to do anything in the null case, then you can use the unit value ()
:
match value with
| null -> ()
| _ -> // your code here
Of course, you could also do the null check just like in C#, which is probably clearer in this case:
if value <> null then
// your code here
Solution 2
For some reason (I haven't yet investigated why) not (obj.ReferenceEquals(value, null))
performs much better than value <> null
. I write a lot of F# code that is used from C#, so I keep an "interop" module around to ease dealing with null
. Also, if you'd rather have your "normal" case first when pattern matching, you can use an active pattern:
let (|NotNull|_|) value =
if obj.ReferenceEquals(value, null) then None
else Some()
match value with
| NotNull ->
//do something with value
| _ -> nullArg "value"
If you want a simple if
statement, this works too:
let inline notNull value = not (obj.ReferenceEquals(value, null))
if notNull value then
//do something with value
UPDATE
Here are some benchmarks and additional information on the performance discrepancy:
let inline isNull value = (value = null)
let inline isNullFast value = obj.ReferenceEquals(value, null)
let items = List.init 10000000 (fun _ -> null:obj)
let test f = items |> Seq.forall f |> printfn "%b"
#time "on"
test isNull //Real: 00:00:01.512, CPU: 00:00:01.513, GC gen0: 0, gen1: 0, gen2: 0
test isNullFast //Real: 00:00:00.195, CPU: 00:00:00.202, GC gen0: 0, gen1: 0, gen2: 0
A speed-up of 775% -- not too bad. After looking at the code in .NET Reflector: ReferenceEquals
is a native/unmanaged function. The =
operator calls HashCompare.GenericEqualityIntrinsic<'T>
, ultimately ending up at the internal function GenericEqualityObj
. In Reflector, this beauty decompiles to 122 lines of C#. Obviously, equality is a complicated issue. For null
-checking a simple reference comparison is enough, so you can avoid the cost of subtler equality semantics.
UPDATE 2
Pattern matching also avoids the overhead of the equality operator. The following function performs similarly to ReferenceEquals
, but only works with types defined outside F# or decorated with [<AllowNullLiteral>]
.
let inline isNullMatch value = match value with null -> true | _ -> false
test isNullMatch //Real: 00:00:00.205, CPU: 00:00:00.202, GC gen0: 0, gen1: 0, gen2: 0
UPDATE 3
As noted in Maslow's comment, an isNull
operator was added in F# 4.0. It's defined the same as isNullMatch
above, and therefore performs optimally.
Solution 3
If you have a type that has been declared in C# or a .NET library in general (not in F#) then null
is a proper value of that type and you can easily compare the value against null
as posted by kvb. For example, assume that C# caller gives you an instance of Random
:
let foo (arg:System.Random) =
if arg <> null then
// do something
Things become more tricky if the C# caller gives you a type that was declared in F#. Types declared in F# do not have null
as a value and F# compiler will not allow you to assign them null
or to check them against null
. The problem is that C# doesn't do this check and a C# caller could still give you null
. For example:
type MyType(n:int) =
member x.Number = n
In that case, you need either boxing or Unchecked.defaultOf<_>
:
let foo (m:MyType) =
if (box m) <> null then
// do something
let foo (m:MyType) =
if m <> Unchecked.defaultOf<_> then
// do something
Jonathan Sternberg
Updated on July 09, 2022Comments
-
Jonathan Sternberg almost 2 years
I need to interop with some C# code with F#. Null is a possible value that it is given so I need to check if the value was null. The docs suggest using pattern matching as such:
match value with | null -> ... | _ -> ...
The problem I'm having is the original code is structured in C# as:
if ( value != null ) { ... }
How do I do the equivalent in F#? Is there a no-op for pattern matching? Is there a way to check for null with an if statement?
-
Jonathan Sternberg over 12 yearsThis just seemed to be me misunderstanding the operators. Finally found this: msdn.microsoft.com/en-us/library/dd233228.aspx
-
Nicolas Fall over 8 yearswhat changes does this answer need based on the new
isNull
in F# 4?Operators.isNull : value:'T -> bool when 'T : null
blogs.msdn.com/b/dotnet/archive/2015/04/29/… -
James Moore over 7 yearsThe problem is that real life often hands you nasty chains of C# classes, so you have to do ugly things like "if (vc <> null && vc.NavigationController <> null && vc.NavigationController.NavigationBar <> null)". It's not really a single null by itself that's painful.