dotnet / fsharp

The F# compiler, F# core library, F# language service, and F# tooling integration for Visual Studio
https://dotnet.microsoft.com/languages/fsharp
MIT License
3.93k stars 788 forks source link

F# interactive fails to load proper project "version" when using multiple scripts load #13866

Closed jkone27 closed 2 years ago

jkone27 commented 2 years ago
image

I get this behaviour when referencing multiple scripts from different files

image

i have a single Domain.fsx which i load in multiple files and then open as open Domain

// Domain.fsx
type Account = {
    Email: string
    Name: string
    Password : string
}

I am not sure why is not possible to load Domain, ionide in vscode shows no errors.

//Script.fsx

//load other scripts
#load "Domain.fsx"
#load "Dto.fsx"
#load "Infrastructure.fsx"

//..

open Domain
open Dto
open Infrastructure

note: restarting ionide - closing/reopening vscode doesnt help

abonie commented 2 years ago

I could not repro this issue with information provided.

@jkone27: Would it be possible for you to narrow it down to a minimal sequence of steps that can reproduce this bug?

jkone27 commented 2 years ago

reproduce-load-issue.zip

abonie commented 2 years ago

So far I got it down to a fairly small repro, four script files still needed:

A.fsx:
#load "Domain.fsx"

let a = Domain.T

B.fsx:
#load "Domain.fsx"

let b = Domain.T

Domain.fsx:
type T = T

Script.fsx:
#load "A.fsx"
#load "B.fsx"

A.a = B.b

~same error:

> dotnet fsi Script.fsx
Script.fsx(4,7): error FS0001: This expression was expected to have type
    'FSI_0001.Domain.T'
but here has type
    'FSI_0002.Domain.T'
abonie commented 2 years ago

This unfortunately seems to currently be by design for fsi - each time a script is loaded it gets wrapped in a new outer namespace. It seems to have been this way since dotnet 5 at least, maybe forever.

Fixing this would significantly improve fsi scripting story IMO, but it is more of a feature request that would have to be submitted as a proposal at fslang-suggestions. And it would have to account for both interactive sessions with fsi and running a script with fsi ScriptName.fsx as well as for a case when script file has changed in between loads. @jkone27 I encourage you to submit a suggestion there if you have time to write it up.


Sidenote: F# language specification describes how scripts should behave when compiled and that includes loading same script file multiple times:

Script files may add other signature, implementation, and script files to the list of sources by using the #load directive. Files are compiled in the same order that was passed to the compiler, except that each script is searched for #load directives and the loaded files are placed before the script, in the order they appear in the script. If a filename appears in more than one #load directive, the file is placed in the list only once, at the position it first appeared.

and in fact, when compiled with fsc.exe Script.fsx, the code I pasted above runs without this type error. However, that's not even a suitable work around as far as I am concerned. And there's nothing about semantics of running scripts with fsi in the spec.

abonie commented 2 years ago

I believe I've found a viable workaround: In Script.fsx replace multiple #loads:

#load "Domain.fsx"
#load "Dto.fsx"
#load "Infrastructure.fsx"

with one:

#load "Domain.fsx" "Dto.fsx" "Infrastructure.fsx"

I am not yet convinced if this is always a feasible solution to these kinds of issues, but @jkone27, let me know if it works for you

jkone27 commented 2 years ago

Yes this workaround works! thanks a LOT 👍 💚

dsyme commented 2 years ago

Please note the above is the correct approach

Closing as by-design.

jkone27 commented 2 years ago

Thanks, @abonie just suggesting also for new users, maybe the compiler error should make this design decision more explicit to the user, or suggest this option if there is a multiline load

#load
#load 

--> compiler fail with

please replace multiline #load directives with a single load# directive in your script file ,  
#load supports multiple scripts in the same line, example: `#load "first.fsx" "second.fsx"`
smoothdeveloper commented 2 years ago

@jkone27 I believe there are subtleties about how those directives impact the evaluation, that would make such adjustment problematic, on top of backward compatibility.

Adjusting https://learn.microsoft.com/en-us/dotnet/fsharp/tools/fsharp-interactive/#f-interactive-directive-reference so it shows that several files can be loaded seems to be a good solution to make it explicit, what can be done with this directive.