42

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
  • 6,421
  • 7
  • 39
  • 58

6 Answers6

63

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.

Community
  • 1
  • 1
Daniel
  • 47,404
  • 11
  • 101
  • 179
  • 4
    what changes does this answer need based on the new `isNull` in F# 4? `Operators.isNull : value:'T -> bool when 'T : null` http://blogs.msdn.com/b/dotnet/archive/2015/04/29/rounding-out-visual-f-4-0-in-vs-2015-rc.aspx – Maslow Nov 11 '15 at 15:58
  • @Maslow obj.ReferenceEquals takes `obj` parameters, so you don't have to first upcast it – symbiont Jan 29 '23 at 22:56
42

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
kvb
  • 54,864
  • 2
  • 91
  • 133
16

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
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
0

I've recently faced a similar dilemma. My issue was that I exposed an F#-developed API that could be consumed from C# code. C# has no issue passing null to a method that accepts interfaces or classes, but if the method and the types of its arguments are F# types, you are not allowed to properly do the null checks.

The reason is that F# types initially do not accept the null literal, while technically nothing in the CLR protects your API from being improperly invoked, especially from a more null-tolerant language such as C#. You would initially end up being unable to properly null-protect yourself unless you use the [<AllowNullLiteral>] attribute, which is quite an ugly approach.

So, I've come to this solution in the related topic, which allows me to keep my F# code clean and F#-friendly. In general I create a function that will accept any object and will convert it to an Option, then instead of null I will validate against None. This is similar to using the pre-defined in F# Option.ofObj function, but the latter does require the object passed to be explicitly nullable (thus annotated with the AllowNullLiteral uglyness).

Ivaylo Slavov
  • 8,839
  • 12
  • 65
  • 108
0

I found a simple way to do this

open System

Object.ReferenceEquals(value, null)
Nguyen Duc Dung
  • 591
  • 1
  • 4
  • 5
0

Of course nulls are generally discouraged in F#, but...

Without going into examples etc there's an article with some examples @ MSDN here. It reveals specifically how to detect a null -- and you can then parse it out of the input or handle as desired.

Hardryv
  • 755
  • 7
  • 12
  • 3
    The 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. – James Moore Jul 19 '16 at 23:47