-
Notifications
You must be signed in to change notification settings - Fork 144
add FS-1331 - Allow opens in type and expression scopes #810
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
base: main
Are you sure you want to change the base?
Changes from 5 commits
0622190
9316a67
2b39deb
ee8ec8e
6935353
ae64021
a3e48b6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| # F# RFC FS-1331 - Allow opens in type and expression scopes | ||
|
|
||
| The design suggestion [Allow the use of open at type and expression scopes](https://github.com/fsharp/fslang-suggestions/issues/96) has been marked "approved in principle". | ||
|
|
||
| This RFC covers the detailed proposal for this suggestion. | ||
|
|
||
| - [x] [Suggestion](https://github.com/fsharp/fslang-suggestions/issues/96) | ||
| - [x] Approved in principle | ||
| - [x] [Implementation](https://github.com/dotnet/fsharp/pull/18814) | ||
| - [x] [Discussion](https://github.com/fsharp/fslang-design/discussions/812) | ||
|
|
||
| # Summary | ||
|
|
||
| This RFC proposes to allow the use of `open` at type and expression scopes. Type/expression-scoped `open`s will work as same as module-scoped `open`, that is, modules or types with `[<RequireQualifiedAccess>]` cannot be opened. | ||
|
|
||
| # Motivation | ||
|
|
||
| By allowing this, we can | ||
| 1. Picking a certain set of operator overloads in some tight context like `Checked`. | ||
| 2. Making the `open`s span less than the whole module/namespace. | ||
|
|
||
| # Detailed design | ||
|
|
||
| 1. Expression-scoped `open` is an expression that opens a module in the body expression scope, and its type is the body's type. | ||
|
|
||
| ```fsharp | ||
| ((open System | ||
| Int32.MaxValue + 1 // The body expression | ||
| ): int) | ||
| let test () = | ||
| open global.System | ||
| printfn "%d" (Int32.MaxValue + 1) | ||
| open type System.Int32 | ||
| open Checked | ||
| printfn "%d" (MaxValue + 1) | ||
| // In computation expressions | ||
| let res = async { | ||
| open System | ||
| Console.WriteLine("Hello, World!") | ||
| let! x = Async.Sleep 1000 | ||
| return x | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In Lambdas ? [ 1..10 ]
|> List.iter (fun x ->
open System
Console.WriteLine($"Value: {x}"))
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lambda is ok |
||
| // In type and member's definitions | ||
| type C() = | ||
| do | ||
| open System | ||
| printfn "%d" Int32.MaxValue | ||
| member _.M() = open type Int32; MaxValue | ||
| ``` | ||
|
|
||
| 1. Type-scoped `open` is a statement that opens a module in the type's following scope. It can be used any type definitions, type expressions and the `with` section of a record/union/exception type. | ||
|
|
||
|
|
||
| ```fsharp | ||
| type C() = | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we please add some samples about ? type IFace =
abstract F : int -> int
open System
member _.F _ = 3
{ new IFace with
open System
member _.F _ = 3
}
type C () =
member _.F () = 3
open System
member _.F _ = 3
open System
interface IFace with
open System
member _.F _ = 3
type A =
| A
open System
member _.F _ = 3
and B =
| B
open System
member _.F _ = 3
type U =
| A
| B
open System
member _.F _ = 3
type R =
open System
{ A : int }
open System
member _.F _ = 3
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd be fine with the feature shipping, with none of the places @edgarfgp showcased, being supported; and giving more room for consideration about the samples and in-type-definition semantics. Ideally, it could be used in a |
||
| do printfn "%d" Int32.MaxValue // <- Cannot find the `Int32` here | ||
| open System | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this be considered an expressions-scoped
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The original suggestion doesn't mentioned this, but I think the contents
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that makes sense. I was somehow thinking of an |
||
| do printfn "%d" Int32.MaxValue | ||
| member _.M() = open type Int32; MaxValue | ||
| member _.M2() = List<int>() // <- Cannot find the `List` here | ||
| open System.Collections.Generic | ||
| member _.M3() = List<int>() | ||
| type System.Int32 with | ||
| open type System.Math | ||
| member this.Abs111 = Abs(this) | ||
| type A = A of int | ||
| with | ||
| open System | ||
| // .... | ||
| ``` | ||
|
|
||
| # Drawbacks | ||
|
|
||
| TODO | ||
|
||
|
|
||
| # Alternatives | ||
|
|
||
| The original suggestion mentioned 3 ways to handle expression/type-scoped `open` + `[<RequireQualifiedAccess>]` attribute: | ||
|
|
||
| 1. Do nothing, RQA is still respected, and this specific use case is something we just accept as not accomplishable | ||
| 2. Have type- and function-scoped open declarations bypass RQA | ||
| 3. Introduce a "mostly RQA" attribute that allows you to open them only in the type and function scope | ||
|
|
||
| This RFC is based on the first option. | ||
|
|
||
| # Compatibility | ||
|
|
||
| Please address all necessary compatibility questions: | ||
|
|
||
| * Is this a breaking change? | ||
| > No. | ||
| * What happens when previous versions of the F# compiler encounter this design addition as source code? | ||
| > It will fail to compile, as `open` is not allowed in type and expression scopes in previous versions. | ||
| * What happens when previous versions of the F# compiler encounter this design addition in compiled binaries? | ||
| > It should work fine, as `open`s are code-level constructs. | ||
| # Pragmatics | ||
|
|
||
| ## Tooling | ||
|
|
||
| Please list the reasonable expectations for tooling for this feature, including any of these: | ||
T-Gro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| * Debugging | ||
| * Breakpoints/stepping | ||
| Make sure that cannot set breakpoints in the `open` statement | ||
| * Error recovery (wrong, incomplete code) | ||
| * Auto-complete | ||
| Check if the completion list after the expression/type-scoped `open` is same as the module-scoped `open`. | ||
| * Code fixes | ||
| * `AddOpenCodeFixProvider` | ||
| It should works like before, that is, append the missing `open` to the nearest module/namespace-scoped `open`s. | ||
|
|
||
| * `ConvertCSharpUsingToFSharpOpen` | ||
| It should works like before. | ||
|
|
||
| * `RemoveUnusedOpens` | ||
| It should be able to recognize `open`s on expression/type level and remove unused items. | ||
Uh oh!
There was an error while loading. Please reload this page.