LLS
LLS

Reputation: 2228

Module values in F# don't get initialized. Why?

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.

Upvotes: 15

Views: 1981

Answers (2)

kvb
kvb

Reputation: 55184

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.

Upvotes: 20

svick
svick

Reputation: 244777

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").

Upvotes: 6

Related Questions