I've declared a tuple like this:
module MyModule =
let private INVALID_TUPLE = ("0", DateTime.MinValue)
When I reference it lower in the module, it's always null:
let private invalidForNone someOtherTuple =
match someOtherTuple with
| None -> INVALID_TUPLE // it's null
| Some(t) -> t
Further, when I place a breakpoint on the tuple declaration, it never hits.
If I do the exact same thing in a script (fsx) file, start debugging, execute, the breakpoint on the tuple declaration hits and the reference to the tuple is good.
ILSpy for my module shows that there is some startup code generated that has a Main method that creates INVALID_TUPLE. Apparently, that's not running for some reason?
Here is a sample that reproduces the behavior (now that I realize it has something to do with the way MSTest executes the code). Call this from a C# unit test; result will be null. In fact, no breakpoint in the F# code will execute at all.
module NullTupleTest
open System
let private INVALID_TUPLE = ("invalid", DateTime.MinValue)
let private TupleTest someTuple =
match someTuple with
| None -> INVALID_TUPLE
| Some(dt) -> dt
let Main = TupleTest None
The error can happen when you run code compiled as an executable in a way that does not run the Main
method of the compiled executable - for example by referencing it from a library or by using unit test runner. The solution is to compile the F# project as a library and perhaps have another executable as the entry point. (Alternatively, you could also modify the code to avoid let
bound global values, but I'd prefer the first approach.)
This is caused by the fact that the F# compiler handles initialization differently for code compiled as an executable and for code compiled as a library.
Main
method and it runs when the application starts (but only when it starts as a normal executable).I think the reason for this is that the F# compiler tries to keep the order in which initialization happens (from top to bottom). For executables, this can be done by running initializers in the Main
method. For libraries, there is no reliable way of doing that (because libraries have no "initialization") and so using static constructors is the next best opion.
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