I 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.
Hi
Did you mean “if (box x = null) then None else Some(x)”?
Or maybe I am missing the point?
Best regards Robert
Oh, yes, sure. Thanks! =)
I just made an isNull function that’s really just a concise wrapper for Object.ReferenceEquals(foo, null)
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
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
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.
Thanks for your comment and sorry for such WordPress behaviour.
I hope that I have restored it correctly.
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?
Sorry, it is WordPress formatting once again! WordPress does not like < > brackets =(
In the first sample List should be typed! (already fixed)
Thanks.