CompilationRepresentationFlags.UseNullAsTrueValue
can be used to
Permit the use of null as a representation for nullary discriminators in a discriminated union
Option.None
is the most prominent example of this.
Why is this useful? How is a null check better than the traditional mechanism for checking union cases (the generated Tag
property)?
It leads to perhaps unexpected behavior:
Some(1).ToString() //"Some(1)"
None.ToString() //NullReferenceException
I tested Jack's assertion that comparing to null instead of a static readonly field is faster.
[<CompilationRepresentation(CompilationRepresentationFlags.UseNullAsTrueValue)>]
type T<'T> =
| Z
| X of 'T
let t = Z
Using ILSpy, I can see t
compiles to null (as expected):
public static Test.T<a> t<a>()
{
return null;
}
The test:
let mutable i = 0
for _ in 1 .. 10000000 do
match t with
| Z -> i <- i + 1
| _ -> ()
The results:
Real: 00:00:00.036, CPU: 00:00:00.046, GC gen0: 0, gen1: 0, gen2: 0
If the CompilationRepresentation
attribute is removed, t
becomes a static readonly field:
public static Test.T<a> t<a>()
{
return Test.T<a>.Z;
}
public static Test.T<T> Z
{
[CompilationMapping(SourceConstructFlags.UnionCase, 0)]
get
{
return Test.T<T>._unique_Z;
}
}
internal static readonly Test.T<T> _unique_Z = new Test.T<T>._Z();
And the results are the same:
Real: 00:00:00.036, CPU: 00:00:00.031, GC gen0: 0, gen1: 0, gen2: 0
The pattern match is compiled as t == null
in the former case and t is Z
in the latter.
Python uses the keyword None to define null objects and variables. While None does serve some of the same purposes as null in other languages, it's another beast entirely. As the null in Python, None is not defined to be 0 or any other value. In Python, None is an object and a first-class citizen!
Instead of null, there's None keyword used in Python. so there is no comparison between Python None vs null. In other programming languages null is often defined to be 0, but in Python used None to define null objects and variables. But None is not defined to be 0 or any other value.
NaN : means 0/0 -- Stands for Not a Number NA : is generally interpreted as a missing, does not exist NULL : is for empty object.
NULL is not a value. It is literally the absence of a value. You can't "equal" NULL! The operator != does not mean "is not"; it means "is not equal to".
The F# compiler sometimes uses null
as a representation for None because it's more efficient than actually creating an instance of FSharpOption<'T> and checking the Tag
property.
Think about it -- if you have a normal F# type (like a record) that's not allowed to be null, then any pointer to an instance of that type (the pointer used internally by the CLR) will never be NULL
. At the same time, if T
is a type which can represent n
states, then T option
can represent n+1
states. So, using null
as a representation for None simply takes advantage of that one extra state value which is available by the fact that F# types aren't allow to be null.
If you want to try turning this behavior off (for normal F# types), you can apply [<AllowNullLiteral(true)>]
to them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With