I need to do some setup in a module that I wanted to accomplish by leveraging a do block. Strangely though, my do block never seems to get hit.
Even stranger, If I load the module code into fsi, it does get hit. Here is my example:
Main.fs
[<EntryPoint>]
let main args =
printfn "%b" TestNamespace.M.x
0
TestModule.fs
namespace TestNamespace
module M =
do
printfn "In do"
failwith "Error" // this is line 6
let x = true
When I run the compiled executable I get
>test.exe
true
Why didn't the exception get thrown? If I run the module in FSI by itself I get
In do
System.Exception: Error
at <StartupCode$FSI_0006>.$FSI_0006.main@() in C:\Projects\Personal2\Playground\fsscripts\fsscripts\TestModule.fs:line 6
Stopped due to error
So it got the exception.
I see that in the decompliation that the do initializers get rolled into a seperate class
namespace \u003CStartupCode\u0024fsscripts\u003E
{
internal static class \u0024Library1
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
[CompilerGenerated]
[DebuggerNonUserCode]
internal static int init\u0040;
static \u0024Library1()
{
ExtraTopLevelOperators.PrintFormatLine<Unit>((PrintfFormat<Unit, TextWriter, Unit, Unit>) new PrintfFormat<Unit, TextWriter, Unit, Unit, Unit>("In do"));
Operators.FailWith<Unit>("Error");
bool x = M.x;
}
}
}
VS the actual module code:
namespace TestNamespace
{
[CompilationMapping(SourceConstructFlags.Module)]
public static class M
{
public static bool x
{
[DebuggerNonUserCode] get
{
return true;
}
}
}
}
So how do I make sure the do block actually gets executed?
--
Edit, given the above example counts as a simple constant expression so won't produce an observable initialization, why does the following also not work?
[<EntryPoint>]
let main args =
printfn "%d" (TestNamespace.M.x id 1)
0
namespace TestNamespace
module M =
do
printfn "In do"
failwith "Error"
let x f a = f a
This prints out 1 no problem.
Edit, after having re-read Tomas's comments its because a function is considered a constant expression.
For a good explanation of the problem, see the answer to this previous SO question. The important bit says:
the static initializer for the file is executed on first access of a value that has observable initialization
Now, "observable initialization" is somewhat tricky idea, but simple constant initialization definitely does not have observable initialization - that's why the do block is not executed. You can trick the compiler into thinking that there is some imperative action, for example by adding do ():
module M =
do
printfn "In do"
failwith "Error" // this is line 6
let x = (do ()); true
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