Open pkese opened 3 years ago
This makes it impractical to experiment with interactive workspaces
You can always do #if INTERACTIVE
as a workaround. "Impractical" is an overstatement here.
"Impractical" is an overstatement here.
It might be impractical for beginners who are learning F# by using FSI. Learning a language is hard enough, and being confronted with misalignments (here: works in projects, not in FSI) is then even harder (I remember that I stumbled across this, too).
One thing to keep in mind: Working in FSI means often: The developer has to know which code was evaluated when and how, or in other words: One has to mind-map the code displayed on screen vs. the program currently available in FSI. Having a
module X
[code]
on top of an fsx file would hopefully not be understood as some kind of directive for tooling in a way that all code in that file will be always related to that module, no matter how it is evaluated. Instead, it would behave ideitically to:
module X =
[code]
This is important to understand when adding / changing code incrementally that is not in global scope.
I don't have a strong opinion, I also faced a bit of head scratching on this matter when I picked up FSI originally, but it made sense once I understood how to use #load
and the various implications about partial evaluations in context of FSI and the implicit "put in a module" if you #load
an .fsx which doesn't even bear a module name.
This makes it impractical to experiment with interactive workspaces, because one always has to remove the module definition before testing a single file with fsi and then put it back if one wishes to reference the file from somewhere else.
I'd say impractical here is a bit strong, despite this limitation, FSI is practicable, was, even when I was learning.
Maybe a stop gap suggestion is to improve the error message with specific guidance if this occurs in FSI?
If there is an easy way to fix this issue, which doesn't have negative impact on how things would be evaluated on subsequent partial evaluations (which I'm not clear there is so far), I agree removing this hump could be helpful, but enhancing the error message would also achieve this IMO.
@Happypig375
Imagine I have a library that I typically use from somewhere else with #load
.
But I also like to keep a little test for it, which I run by executing just this particular file.
module Sum
let add x y = x + y
if fsi.CommandLineArgs = [| __SOURCE_FILE__ |] then
assert(Sum.add 1 2 = 3)
printfn "my code tested fine"
I don't know, how I can fix this issue using #if INTERACTIVE
, because it is always interactive:
If I reference it in another intractive script using #load
, then it's interactive,
and if I run it directly using dotnet fsi
it is also interactive.
There are lots of cases with similar use, e.g. code with database access functions (queries and inserts) which optionally parses some command line flags and renders create table statements when run on its own.
It is a common pattern in non-compiled languages.
Generally I'm in favour of this. However one question, for this:
module M
let x = 1
module M =
let x = 2
let y = M.x
What would be the result when executing the first fource lines then the last line as separate intereactions in F# Interactive? Is y
1 or 2?
That is, does executing
module M
let x = 1
actually establish a module M
? Or is module M
ignored in script fragment executions in favour of the implicit module for each script fragment?
@pkese I think @Happypig375 idea was this:
file "sum.fsx"
#if COMPILED
module Sum
#endif
let add x y = x + y
if fsi.CommandLineArgs = [| __SOURCE_FILE__ |] then
assert(add 1 2 = 3)
printfn "my code tested fine"
Now, using "add" by #load-ing that file from another file:
#load "sum.fsx"
open Sum
printfn $"sum of 10 and 15 is {add 10 15}"
This works as long as the module name ("Sum") equals the file name without extension ("sum.fsx" - first char case insensitive), because when #load is used, FSI creates a module (fst letter capitalized) according to the loaded .fsx file name. According to this pattern, I propose this:
module M [NewLine]
will create a module "M" that can be accessed via M
. The name of the .fsx file doesn't matter in that case (current behaviour).module M [NewLine]
: A module is created according to it's file name (current behaviour).module M [NewLine]
get ignored + emit a warning that can also be displayed in tooling. (current behaviour: error FS0010 is raised).Another consideration is private constructors. Suppose in one file I have
namespace My.Namespace
type MyType = private MyLeft | MyRight
and in another file (MyOtherModule.fs) I have
#if COMPILED
module My.Namespace.MyOtherModule
#endif
let lft = MyLeft
Then it will compile just fine, but you won't be able to use it in FSI at all, because it will complain about MyLeft
being inaccessible from this location. Also, making FSI just ignore the module My.Namespace.MyModule
line won't resolve this at all. I actually don't know of any way of getting MyLeft
in scope in FSI since I don't believe any sort of namespacing is allowed.
@pkese I've marked this as approved-in-principle
Remove restriction that main .fsx file mustn't define a module
Dotnet fsi fails if the main .fsx file defines a module:
What is allowed is
or just
A full-fledged F# project however does not have this restriction.
This makes it impractical to experiment with interactive workspaces, because one always has to remove the module definition before testing a single file with
fsi
and then put it back if one wishes to reference the file from somewhere else.I would therefore propose to remove this 'no module' restriction.
This is based on report: https://github.com/dotnet/fsharp/issues/11699
Pros and Cons
The advantages of making this adjustment to F# are
The disadvantages of making this adjustment to F# are
Extra information
Estimated cost (XS, S, M, L, XL, XXL): S
Related suggestions: none that I know
Affidavit (please submit!)
Please tick this by placing a cross in the box:
Please tick all that apply:
For Readers
If you would like to see this issue implemented, please click the :+1: emoji on this issue. These counts are used to generally order the suggestions by engagement.