Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recognising [<System.Runtime.CompilerServices.ModuleInitializer>] #1024

Closed
5 tasks done
Happypig375 opened this issue Jun 7, 2021 · 7 comments
Closed
5 tasks done

Comments

@Happypig375
Copy link
Contributor

Happypig375 commented Jun 7, 2021

I propose we recognize the attribute for the C# 9.0 feature: Module Initializers.

open System
type C() =
    [<System.Runtime.CompilerServices.ModuleInitializer>]
    static member M() = printfn "Called once only per run, when any type in this project is referenced"

This is a rather niche scenario, but once you run into it the solutions appear to be pretty painful. There are reports of a number of customers (inside and outside Microsoft) struggling with the problem, and there are no doubt more undocumented cases.

The existing way of approaching this problem in F# is to use tools for injecting IL post-build.

Pros and Cons

The advantages of making this adjustment to F# are

  1. Consistency with C#
  2. Convenience by enabling a mechanism for assembly (IL module)-level initialization
  3. Conciseness in that we don't have to use IL injectors

The disadvantage of making this adjustment to F# is that the name of (IL) module initializers may be conflated with (F#) modules. However, this name was chosen by the C# team and will be confusing if this attribute does not work similarly in F#.

Extra information

Estimated cost (XS, S, M, L, XL, XXL):
S if we limit the use of this attribute to one static method only,
M if we implement the full C# feature where multiple static methods can have this attribute, and a deterministic order is produced.

Related suggestions: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/module-initializers

Affidavit (please submit!)

Please tick this by placing a cross in the box:

  • This is not a question (e.g. like one you might ask on stackoverflow) and I have searched stackoverflow for discussions of this issue
  • I have searched both open and closed suggestions on this site and believe this is not a duplicate
  • This is not something which has obviously "already been decided" in previous versions of F#. If you're questioning a fundamental design decision that has obviously already been taken (e.g. "Make F# untyped") then please don't submit it.

Please tick all that apply:

  • This is not a breaking change to the F# language design
  • I or my company would be willing to help implement and/or test this

For Readers

If you would like to see this issue implemented, please click the 👍 emoji on this issue. These counts are used to generally order the suggestions by engagement.

@kerams
Copy link

kerams commented Jun 7, 2021

S if we limit the use of this attribute to one static method only,

That's per class, right?

@Happypig375
Copy link
Contributor Author

@kerams per project.

@kerams
Copy link

kerams commented Jun 7, 2021

This is what C# spits out.

namespace Preview
{
    class Test
    {
        [System.Runtime.CompilerServices.ModuleInitializer]
        public static void Init()
        {

        }
        
        [System.Runtime.CompilerServices.ModuleInitializer]
        public static void Init2()
        {

        }
    }
}
.class private auto ansi '<Module>'
{
    // Methods
    .method private hidebysig specialname rtspecialname static 
        void .cctor () cil managed 
    {
        // Method begins at RVA 0x2050
        // Code size 11 (0xb)
        .maxstack 8

        IL_0000: call void Preview.Test::Init()
        IL_0005: call void Preview.Test::Init2()
        IL_000a: ret
    } // end of method '<Module>'::.cctor

} // end of class <Module>

// Standard IL for class Test below

Basically just a static ctor for <Module> that calls the initializers one by one.

@Happypig375
Copy link
Contributor Author

Yes, this is what module initializers do.

@dsyme
Copy link
Collaborator

dsyme commented Jun 7, 2021

F# has on-demand file-level initialization, which is necessary for coherent, safe initialization of 'let' bindings. It is a subtle mechanism built out of existing .NET initialization mechanisms that is very, very carefully implemented and tested. I suspect adding DLL/whole-assembly initialization will interact exceptionally badly with this mechanism, exposing the programmer to all sorts of situations where static 'let' bindings are not yet established.

Note F# doesn't really let you write class initializers explicitly (the class initializers do exist but they get chained into file-level initialization code, with the exception of generic types, which are called out as a special case in the F# spec). That said, if a .NET DLL/whole-assembly initialization mechanism had existed in .NET 2.0 we probably would have made use of it for F# initialization.

In short, F# takes its own view on initialization because we want static let to be sound - i.e. to make it very difficult to encounter a situation where static let bindings are not yet initialized (you need to use reflection of multi-threading during initialization). Any work in this area should really be done from this F# perspective.

@Happypig375
Copy link
Contributor Author

@dsyme

That said, if a .NET DLL/whole-assembly initialization mechanism had existed in .NET 2.0 we probably would have made use of it for F# initialization.

But module initializers in IL has existed since .NET Framework 1.0 (see ECMA-335, 1st edition, December 2001, § II.9.8).

@dsyme
Copy link
Collaborator

dsyme commented Jun 14, 2022

Closing this as we don't plan to support this form of initialization.

@dsyme dsyme closed this as completed Jun 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants