fsharp / fslang-suggestions

The place to make suggestions, discuss and vote on F# language and core library features
345 stars 21 forks source link

#langversion #814

Open Happypig375 opened 4 years ago

Happypig375 commented 4 years ago

langversion

I propose we add #langversion to F# scripts.

#langversion "preview"
let x = () in nameof x;; // :)

The existing way of approaching this problem in F# is creating an entire project just to make scripts with nameof usable.


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

For help type #help;;

> let x = () in nameof x;; // :(

  let x = () in nameof x;; // :(
  --------------^^^^^^

stdin(1,15): error FS0039: The value or constructor 'nameof' is not defined.

> 

Pros and Cons

The advantages of making this adjustment to F# are

  1. Ability to use new F# features in script files
  2. More consistency with actual F#

The disadvantages of making this adjustment to F# are none that I can think of.

Extra information

Estimated cost (XS, S, M, L, XL, XXL): S

Related suggestions: None

Affidavit (please submit!)

Please tick this by placing a cross in the box:

Please tick all that apply:

baronfel commented 4 years ago

You can also run a script with preview features in an instance of FSI that's preview enabled: dotnet FSI --langversion:preview

Which is not to say that this shouldn't be done, just giving you a workaround.

cartermp commented 4 years ago

It's very easy to implement this as a new #r directive, but rationalizing this with an F# scripting environment is pretty wild. Consider this script, where you execute it line-by line:

#langversion "preview"
let x = 12
let name() = nameof x
#langversion "4.7"
printfn "%s" (name ())

What should happen? The name function is already defined, compiled, and evaluated. So we can't emit a compile error without some additional machinery in the compiler that traces arbitrary function calls to see if something is being used but it's not defined or unavailable.

This is also even more difficult in a Jupyter scripting environment, where you might have tens of cells, and you can reference things defined in any other cells (not just those defined above where you're working!). You can imagine the above script split into two arbitrary cells in a very large notebook - what happens to other cells if you set the LangVersion to a lower language version somewhere in the notebook?

Questions like this didn't yield any satisfying answers, hence it was not implemented.

matthid commented 4 years ago

@cartermp Why is it even that complicated? Hash directives already indicate that it is something "static". Therefore in a regular script file the "last" one would win. In an interactive environment, you would just switch the compiler settings for new code as there is really nothing else you can do.

Isn't this already similar to when you use the current directory and change it via #cd or #silentcd? Consider code where you interactively use the FSI current directory or store it in a runtime variable. Yes we cannot change that variable later.

Alternatively, we can just make version downgrades fail or ignore them silently.

cartermp commented 4 years ago

It's complicated mostly because the notion of switching one's effectively language version interactively isn't what it was originally designed for. While you can certainly do this in a project file in tools like VS and have the editor react accordingly, this isn't interactive - it's akin to rebuilding again, just at design-time. So it's more like restarting an FSI session and reloading everything than an interactive switch in the current tooling.

The majority case is easy here; like almost all other #r directives it would only be top-level and functionally equivalent to have started the FSI session with --langversion:<VER>. But we didn't want to implement this half-cocked for the minority case. This is less of a problem in small scripts, but is much more difficult to track down in a large notebook, which is now a supported scenario for interactive F# programming.

Additionally, there's no good way to handle script IntelliSense for this. Currently, nameof gives a diagnostic in the editor if you're not using the preview language version. In the above code sample, would it still do that? Probably not, since it's valid for the first half of that script. But it would be invalid in the latter half if copy/pasted, so we'd probably want to make sure that works. An analogue here is #if defs in source, but we don't handle those nearly as well as the C#/VB editor today so I suspect there would be holes or simple lots of bugs today. Since we're planning on bringing a rich editing experience to Jupyter, it's also critical that getting this sort of thing right also work there, where the code that sets the language version may not be in the same cell you're working on.

matthid commented 4 years ago

I see why intellisense might be a bit hard to do correctly. I don't get why Jupyter is so special - I guess I'm not familiar enough with their workflow - but in any case thanks for the details! So I assume in the end this feature is just not an 'S' but a lot of work and includes some risks and at this point it is debatable if it is even worth the effort.

cartermp commented 4 years ago

Yeah, to handle the majority case it's probably an S. So I wouldn't necessarily rule it out, but I'd expect a handful of bugs immediately from curious people who want to take it further.

KevinRansom commented 4 years ago

As a rule, we want to provide tools that encourage developers to adopt the latest builds eagerly. This feature was designed to be in support of that goal.

The primary intent of the languageversion switch is to enable a developer who is working on a project using a stable version of F# to peg that project to that specific version of F#, and so not introducing dependencies on newer features of F# until the project team has declared an interest in completing the transition to the latest language features.

The secondary intent of the feature was to enable developers to kick the tyres on preview language features by explicitly selecting preview.

Scripting developers can use the command line switch to constrain their scripts to a down-level language version if they so choose. Although the global.json file is the preferable choice for that. Scripts that self-adapt to the ambient fsi language version is not a scenario that we currently target.

Kevin

Happypig375 commented 4 years ago

So preview features like nameof cannot be used in the Visual Studio F# Interactive? It does not support --langversion, any suggestions will have to be done inside F# code.

cartermp commented 4 years ago

Currently we don't plan on enabling opt-in previews with the Visual Studio FSI, but we'd certainly accept a PR that implemented the option in tooling.

abelbraaksma commented 4 years ago

@Happypig375, do you mean that the workaround suggestion by @baronfel doesn't work? https://github.com/fsharp/fslang-suggestions/issues/814#issuecomment-560998987

Happypig375 commented 4 years ago

It only works for independent FSI instances.

KevinRansom commented 4 years ago

@Happypig375 we or someone else can probably add UI to enable preview in VS Fsi. That is a good proposal. I'm kind of sorry I hadn't thought of it already, however, we have only recently decided that we will preview F# 5.0 language features in the 4.7 compiler the previous plan was to restrict them to dotnet 5.0 previews.

I hope this alleviates your concerns

Kevin

Happypig375 commented 4 years ago

Unneeded as of https://github.com/dotnet/fsharp/pull/8443

gusty commented 9 months ago

@Happypig375 I don't think that #8443 render this unneeded.

I think this is a good suggestion and it's very helpful to quickly test regressions.

@cartermp

It's complicated mostly because the notion of switching one's effectively language version interactively isn't what it was originally designed for.

The same applies to #r you can in theory use it twice to link the same library at different version numbers, but in fact what will happen is that the second time it will be ignored. Same could happen here.

smoothdeveloper commented 9 months ago

The same applies to #r you can in theory use it twice to link the same library at different version numbers, but in fact what will happen is that the second time it will be ignored. Same could happen here.

I think it would be confusing with #load unless it is agreed and doable to have independent files with different langversion still work, and a warning if a single file has more than one such attribute evaluated, in which case this is great for testing interaction of features without complex setup.

smoothdeveloper commented 9 months ago

in fact, FSI will need to accept it as input and not care if it is from same file or not, so it is a bit tricky, and I don't think the behaviour of #r "someassembly" not refreshing the assembly is good one, we shouldn't use this limitation as guiding how #langversion could work IMO.

Happypig375 commented 9 months ago

Reopened at the request of @gusty