Testing for null reference in F#
To add some details to the comment by @ildjarn, you are getting the error message, because F# does not allow using null
as a value of types that are declared in F#. The motivation for this is that F# tries to eliminate null
values (and NullReferenceException
) from pure F# programs.
However, if you're using types that are not defined in F#, you are still allowed to use null
(e.g. when calling a function that takes System.Random
as an argument, you can give it null
). This is needed for interoperability, because you may need to pass null
to a .NET library or accept it as a result.
In your example, TweetUser
is a (record) type declared in F#, so the language does not allow treating null
as a value of type TweetUser
. However, you can still get null
value through i.e. Reflection or from C# code, so F# provides an "unsafe" function that creates a null
value of any type - including F# records, which should not normally have null
value. This is the Unchecked.defaultOf<_>
function and you can use it to implement a helper like this:
let inline isNull x = x = Unchecked.defaultof<_>
Alternatively, if you mark a type with the AllowNullLiteral
attribute, then you're saying to the F# compiler that it should allow null
as a value for that specific type, even if it is a type declared in F# (and it would not normally allow null
).
To cyclically expand on @Tomas' answer ;-]
let name = if not <| obj.ReferenceEquals (tweet.User, null)
then tweet.User.Name
else tweet.Sender.Name
or
let inline isNull (x:^T when ^T : not struct) = obj.ReferenceEquals (x, null)
Unchecked.defaultof<_>
is doing the right thing and producing nulls for your record types; the issue is that the default equality operator uses generic structural comparison, which expects you to always play by F#'s rules when using F# types. In any case, a null-check really only warrants referential comparison in the first place.