15

I got a strange behavior when I used F#. When I use let binding in a module, and if the value is created from a constructor, then it's uninitialized when used outside. (I used it from C# using ModuleName.s2 or ModuleName.f())

//in a module
let s1 = "1" //normal
let s2 = new String('i', 5) //null

let f () =
    s2.Equals("something") //Exception

Is this a normal behavior? Thanks in advance.

EDIT: For the purpose of debugging, I choose to compile it as an executable. This may be the problem as other people pointed out.

LLS
  • 2,128
  • 2
  • 23
  • 36

2 Answers2

20

In an F# library, modules are initialized via static constructors which ensure that initialization occurs prior to any of the module's values being used. By contrast, in an F# executable, this initialization is performed in the application's entry point. This means that if another assembly references the F# application (regardless of the language the other application is written in), the initialization code won't be run.

UPDATE

Brian pointed me to this part of the spec, which indicates that this is the expected behavior.

It looks like one workaround would be to provide an explicit entry point, like this:

[<EntryPoint>]
let main _ =
    0

You can then call this main method from your C# app to ensure that the module's contents are properly initialized.

UPDATE 2

I was misreading the spec - you do not need to actually call the explicit entry point from the referencing assembly. Its mere presence will cause the initialization to occur correctly.

kvb
  • 54,864
  • 2
  • 91
  • 133
  • 1
    @LLS - Happy to help. I've made a small correction - you don't need to call the entry point from the other assembly. – kvb Jul 09 '11 at 01:47
  • This is why I love Stack Overflow, +1 for this very useful answer! Was wondering why everything was null. Figured it had to something to do with the parts of the module not being initialized correctly. – Joshua Rodgers Oct 27 '11 at 18:19
  • F# 3.1 - I have an explicit entry point but still run into this problem. (I have a unit test project that references the .exe.. unit tests fail because things are null that shouldn't be null.) – stmax Oct 13 '14 at 12:12
  • @stmax - You'll probably get better advice if you open a new question with the relevant details. – kvb Oct 13 '14 at 12:44
  • 1
    @kvb - after some debugging i found out that my problem was caused by something completely different / only the symptoms were exactly the same. so F#'s doing everything right and your suggestion with the entry point works! (PS.: i verified that it still works with .net 4.0, 4.5, 4.5.1 and F# 3.0 and 3.1) – stmax Oct 13 '14 at 16:02
6

For some reason, SomeModule.s2 is implemented as a (read-only) property that returns the value of the unspeakable static field <StartupCode$FS>.$Program.s2@9. If you compile as an application, that field is initialized in the main method. When used from your C# code, this method is not called, so the field is not initialized.

If you compile as a library, the code is the same, except the field is initialized in the static constructor of the $Program class, so it should work when used from C#.

The reason s1 always works is optimization: the F# compiler understands it's a constant and implements f() as "1".Equals("something").

svick
  • 236,525
  • 50
  • 385
  • 514
  • Thank you very much. I didn't realize that there is a difference between library and executable. – LLS Jul 08 '11 at 21:15