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

'do' statements in modules do not get evaluated ? #13905

Closed
goswinr opened this issue Sep 15, 2022 · 7 comments
Closed

'do' statements in modules do not get evaluated ? #13905

goswinr opened this issue Sep 15, 2022 · 7 comments
Labels

Comments

@goswinr
Copy link
Contributor

goswinr commented Sep 15, 2022

It seems that do statements in modules do not get evaluated when the module is accessed from another assembly.

Creating an empty project

D:\>md Test
D:\>cd Test
D:\Test>dotnet new classlib -lang F#
D:\Test>code Library.fs

then changing the code to

namespace Test

module SayDll =
    let hello name =
        printfn "Hello %s" name
    
    do printfn "loaded-SayDll"

and calling it from fsi

D:\Test>dotnet build
D:\Test>cd bin/debug/net7.0
D:\Test\bin\Debug\net7.0>dotnet fsi

Microsoft (R) F# Interactive version 12.0.2.0 for F# 6.0
Copyright (c) Microsoft Corporation. All Rights Reserved.

For help type #help;;

> #r "Test.dll"
- Test.SayDll.hello "Goswin";;

--> Referenced 'D:\Test\bin\Debug\net7.0\Test.dll' (file may be locked by F# Interactive process)

Binding session to 'D:\Test\bin\Debug\net7.0\Test.dll'...
Hello Goswin
val it: unit = ()

>

The do statement was not evaluated.
Whereas putting it all in fsi

D:\>dotnet fsi

Microsoft (R) F# Interactive version 12.0.2.0 for F# 6.0
Copyright (c) Microsoft Corporation. All Rights Reserved.

For help type #help;;

> module SayFsx =
-     open System.Net
-     let hello name =
-         printfn "Hello %s" name
-
-     do printfn "loaded-SayFsx"
-
- SayFsx.hello "Goswin";;
loaded-SayFsx
Hello Goswin
module SayFsx =
  val hello: name: string -> unit
val it: unit = ()

the do statement evaluates as expected.

Is this behavior intentional? Did I miss some documentation on this?
There is also a not really answered question related to this on Stackoverflow.
What would make a do statement in another assembly's module evaluate?

@goswinr goswinr added the Bug label Sep 15, 2022
@kerams
Copy link
Contributor

kerams commented Sep 15, 2022

This would explain some strange initialization behavior I saw a couple of weeks ago. Thought I was going crazy.

@vzarytovskii
Copy link
Member

I think it's by design due to how static members are processed in compiler and fsi.

@goswinr
Copy link
Contributor Author

goswinr commented Sep 15, 2022

If it is by design, will do statements in other assemblies never evaluate ?

@dotnet dotnet deleted a comment Sep 15, 2022
@charlesroddie
Copy link
Contributor

charlesroddie commented Sep 17, 2022

This is according to spec.

FSharp language specification 4.1, 12.5.1 execution of static initializers.

The thing causing the difference between FSI and a dll reference is that static initializers are run on the basis of files, and in the first case there is something needing initialization in the same file (because everything is in one file), and in the second it is a "different file".

@goswinr goswinr closed this as completed Sep 17, 2022
@kerams
Copy link
Contributor

kerams commented Sep 17, 2022

If it's according to spec, I would, at the very least, expect to see a warning when do module initializers are used in class libraries.

The workaround I have come up with is

module X

let _initializer: unit =
    oneTimeSideEffect ()

let funcRequiringOneTimeInitialization x y =
    _initializer // this is required, otherwise _initializer is never evaluated
    doStuff x y

It feels very brittle and unintuitive though. Yes, I can use a class with a static ctor for this, but "class bad, module good" :)))).

@charlesroddie
Copy link
Contributor

expect to see a warning when do module initializers are used in class libraries.

It would seem reasonable to warn when unit-valued ("do") initializers are used inside modules with no "observable initializers", implying that the unit-valued initializers will never be executed.

It feels very brittle and unintuitive though. Yes, I can use a class with a static ctor for this, but "class bad, module good" :)))).

The module behaviour, while brittle, is well-defined, whereas class static dos apparently only run sometime before the class is used. In your case that is fine, so in your case class would be better and give more easily understandable behaviour.

@abelbraaksma
Copy link
Contributor

The module behaviour, while brittle, is well-defined

Yes, but it is well defined by compiling into a hidden class with a static constructor. So, whatever applies to the static constructor (first access kicks it), applies to the module do as well. It is true, that the compiler sometimes issues code to enforce this "first access", but that only applies to fsx and "files that contain main" afaik. The spec itself says, for instance:

These initializers are executed immediately before the first dereference of the static fields for the generic type

@abelbraaksma abelbraaksma closed this as not planned Won't fix, can't repro, duplicate, stale May 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants