fsharp icon indicating copy to clipboard operation
fsharp copied to clipboard

Change to `string` function behaviour with enums in FSharp.Core 10.0.100

Open rob-earwaker opened this issue 1 month ago • 5 comments

When using .NET 9 SDK or earlier, the string function returns the enum case name for FSharp.Core versions < 10. When upgrading to FSharp.Core 10.0.100, the string function returns the enum's integer value as a string.

Repro steps

  1. New .NET 9 F# console app with the following lines:
    let enumValue = System.ConsoleColor.DarkBlue
    printfn $"string:     '{string enumValue}'"
    printfn $"ToString(): '{enumValue.ToString()}'"
    
  2. Pin FSharp.Core version to 9.0.303.
  3. Add global.json with SDK version set to 9.0.308.
  4. dotnet run produces:
    string:     'DarkBlue'
    ToString(): 'DarkBlue'
    
  5. Update FSharp.Core version to 10.0.100.
  6. dotnet run produces:
    string:     '1'
    ToString(): 'DarkBlue'
    

Project used to reproduce behaviour above: FSharp.String.TestApp.zip

Expected behavior

string function returns the enum case name, i.e. the equivalent of calling the ToString() method.

Actual behavior

string function returns the enum case's integer value as a string.

Known workarounds

One of the following:

  • Use the ToString()method directly
  • Use an earlier version of FSharp.Core
  • Use the .NET 10 SDK.

rob-earwaker avatar Dec 09 '25 15:12 rob-earwaker

This must be from https://github.com/dotnet/fsharp/pull/18546, although I don't think I quite understand how.

brianrourkeboll avatar Dec 09 '25 19:12 brianrourkeboll

Well, I thought that I tested this combination, but I must have messed up my testing somehow, since I am indeed able to reproduce this now.

Unfortunately, I think the only thing that could be done at this point would be to undo the change from https://github.com/dotnet/fsharp/pull/18546 in FSharp.Core so that subsequent releases of FSharp.Core (after 10.0.101) would behave correctly again when consumed by older compilers.[^1] It's up to @T-Gro as to whether we want to do that.

[^1]: It's possible that there'd be a way to get the change to work correctly with older compilers while still working with newer ones, but since there is apparently more involved than I originally believed, it's probably not worth it.

brianrourkeboll avatar Dec 11 '25 00:12 brianrourkeboll

This only comes up for the combination of not using NET10 SDK and consuming FSharp.Core 10 (or higher).

With going trough the servicing process, we could in theory port this feature (assuming it gets accepted as a reason for NET SDK 9 servicing) to SDK 9, but that would still assume people will update (i.e. the same people who did not update to NET10 SDK yet).

Is there something we can annotate in FSharp.Core so that older compilers would get a compile-time diagnostics? (telling people to update SDK is something we have done in the past)

T-Gro avatar Dec 11 '25 07:12 T-Gro

I am thinking along the semantics of [WarnIfCompilerBelow(10.0)] (but it would need to be something which the old compiler already understands)

T-Gro avatar Dec 11 '25 08:12 T-Gro

@brianrourkeboll Thanks for confirming that you can reproduce.

We went for the option of updating to target .NET 10 and are therefore now using the new compiler. This was seen as a safer option than continuing to target .NET 9 and building using the .NET 10 SDK since it forces us to use the new compiler and removes the risk of seeing different behaviour depending on which version of the SDK is installed in the build environment.

We've never had any problems using newer major versions of FSharp.Core and older versions of the compiler before, but in raising this issue it did make me wonder how common this scenario is and that maybe we should be keeping the major versions in sync. I couldn't find any documentation advising that they should be kept in sync or anything explaining the expected compatibility, but perhaps it's not unexpected that there are breaking changes like this, given it's a major version increment.

rob-earwaker avatar Dec 11 '25 08:12 rob-earwaker