From ce99825ad89f590c1f197dd9c1e1bb8e0ea9ba87 Mon Sep 17 00:00:00 2001 From: Thomas Boby Date: Wed, 21 Sep 2022 17:10:31 +0100 Subject: [PATCH 1/4] Create template --- ...129-shorthand-anonymous-unary-functions.md | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 RFCs/FS-1129-shorthand-anonymous-unary-functions.md diff --git a/RFCs/FS-1129-shorthand-anonymous-unary-functions.md b/RFCs/FS-1129-shorthand-anonymous-unary-functions.md new file mode 100644 index 00000000..f386ce86 --- /dev/null +++ b/RFCs/FS-1129-shorthand-anonymous-unary-functions.md @@ -0,0 +1,154 @@ +# F# RFC FS-1129 - (`_.` shorthand for simple anonymous unary functions) + +The design suggestion [Allow _.Property shorthand for accessor functions](https://github.com/fsharp/fslang-suggestions/issues/506) has been marked "approved in principle". + +This RFC covers the detailed proposal for this suggestion. + +- [x] [Suggestion](https://github.com/fsharp/fslang-suggestions/issues/506) +- [x] Approved in principle +- [ ] [Implementation](https://github.com/dotnet/fsharp/pull/13907) +- [ ] Design Review Meeting(s) with @dsyme and others invitees +- [Discussion](https://github.com/fsharp/fslang-design/discussions/FILL-ME-IN) + +# Summary + +Add a syntactic sugar shorthand (`_.`) for simple functions consisting of accessors and atomic function application only. + +```fsharp +_.Foo => fun o -> o.Foo +``` + +# Motivation + +It is fairly common to need to create function expressions which consist of a single argument and only "dot" operations upon it. + +```fsharp +someList |> List.map (fun x -> x.ToString()) +someList |> List.exists (fun x -> x.Contains "foo") +someList |> List.sortBy (fun x -> x.Bar) +``` + +These functions: + +- Add a small amount of visual noise +- Add a small amount of cognitive overhead and maintenance burden on naming the function argument +- Have a small risk of leading to errors if the wrong ident is used on the rhs + +The introduction of a dedicated syntax for these functions could improve the general experience when piping values. + +Why are we doing this? What use cases does it support? What is the expected outcome? + +# Detailed design + +This is a syntactical transformation of the form: + +```fsharp +_.Foo => fun o -> o.Foo +``` + +And the following things should be valid: + +```fsharp +_.Foo.Bar => fun o -> o.Foo.Bar +_.Foo.[5] => fun o -> o.Foo.[5] +_.Foo() => fun o -> o.Foo() +_.Foo(5).X => fun o -> o.Foo(5).X +``` + +This transformation should take place after the SyntaxTree is created, during the checking of the expression. + +Given an application expression where the first part of the long ident in the leading expression is an underscore, the elaborated form of `_.expr` should be `fun x -> x.expr` where `x` is a synthetic variable. + +And allowing things like (_ + 5) is massively increasing the scope of this extension, without providing much benefit. + +Or in other words: "_. binds only to dot-lookups and atomic function application." + +We should consider the interaction with property extraction in pattern matching, e.g. + +match x with +| _.Length as 0 -> .... + + +This is the bulk of the RFC. Explain the design in enough detail for somebody familiar +with the language to understand, and for somebody familiar with the compiler to implement. +This should get into specifics and corner-cases, and include examples of how the feature is used. + +Example code: + +```fsharp +let add x y = x + y +``` + +# Drawbacks + +1. Additional complexity in the language and compiler. +2. It may lead to an expectation that similar shorthands are available in other contexts (such as in pattern matching). +3. It may lead to confusion as to the meaning of underscore (`_`). + +# Alternatives + +What other designs have been considered? What is the impact of not doing this? + +# Compatibility + +Please address all necessary compatibility questions: + +* Is this a breaking change? +* What happens when previous versions of the F# compiler encounter this design addition as source code? +* What happens when previous versions of the F# compiler encounter this design addition in compiled binaries? +* If this is a change or extension to FSharp.Core, what happens when previous versions of the F# compiler encounter this construct? + +# Pragmatics + +## Diagnostics + +the use of this feature should really be disallowed or give a warning in cases such as [this] +(Allow _.Property shorthand for accessor functions #506 (comment)) e.g. at least if_ is used as a fun _-> ... wildcard pattern somewhere in the enclosing expression . I'm not sure what the exact limitation should be though, since you might reasonably use_ as a nested pattern in a match statement wildcard. But some kind of limitation would seem sensible. + +```fsharp +let h : string array array -> (string -> int) array = Array.map (fun _->_.Length) // return tuple from array length and function that ignores argument and returns array length +``` + +Please list the reasonable expectations for diagnostics for misuse of this feature. + +## Tooling + +### Debugging + +Please list the reasonable expectations for tooling for this feature, including any of these: + +* Debugging + * Breakpoints/stepping + * Expression evaluator + * Data displays for locals and hover tips +* Auto-complete +* Tooltips +* Navigation and Go To Definition +* Colorization +* Brace/parenthesis matching + +## Performance + +Please list any notable concerns for impact on the performance of compilation and/or generated code + +* For existing code +* For the new features + +## Scaling + +Please list the dimensions that describe the inputs for this new feature, e.g. "number of widgets" etc. For each, estimate a reasonable upper bound for the expected size in human-written code and machine-generated code that the compiler will accept. + +For example + +* Expected maximum number of widgets in reasonable hand-written code: 100 +* Expected reasonable upper bound for number of widgets accepted: 500 + +Testing should particularly check that compilation is linear (or log-linear or similar) along these dimensions. If quadratic or worse this should ideally be noted in the RFC. + +## Culture-aware formatting/parsing + +Does the proposed RFC interact with culture-aware formatting and parsing of numbers, dates and currencies? For example, if the RFC includes plaintext outputs, are these outputs specified to be culture-invariant or current-culture. + +# Unresolved questions + +What parts of the design are still TBD? From 2156d68703632a55d5cc5c141d5c700ba0e93383 Mon Sep 17 00:00:00 2001 From: Thomas Boby Date: Wed, 21 Sep 2022 19:58:38 +0100 Subject: [PATCH 2/4] Fill out some of the pragmatics --- ...129-shorthand-anonymous-unary-functions.md | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/RFCs/FS-1129-shorthand-anonymous-unary-functions.md b/RFCs/FS-1129-shorthand-anonymous-unary-functions.md index f386ce86..a595b01d 100644 --- a/RFCs/FS-1129-shorthand-anonymous-unary-functions.md +++ b/RFCs/FS-1129-shorthand-anonymous-unary-functions.md @@ -94,8 +94,12 @@ What other designs have been considered? What is the impact of not doing this? 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? + * Parser errors + * TODO: Check this would always be the case * What happens when previous versions of the F# compiler encounter this design addition in compiled binaries? + * TODO: investigate * If this is a change or extension to FSharp.Core, what happens when previous versions of the F# compiler encounter this construct? # Pragmatics @@ -115,24 +119,39 @@ Please list the reasonable expectations for diagnostics for misuse of this featu ### Debugging -Please list the reasonable expectations for tooling for this feature, including any of these: +In summary, all language server features directed at the underscore should behave as if directed at the arg and/or reference of the equivalent elaborated form. All language features directed at anything after the dot should similarly behave as if directed at the body of the equivalent function. * Debugging * Breakpoints/stepping + * This should behave in the same way as the equivalent elaborated form: a breakpoint covering a line with the shorthand should break at least once each time the function is called. Similarly, stepping into the function should behave as if it had been written in the elaborated form. * Expression evaluator + * TODO * Data displays for locals and hover tips + * The debugger should show the value of the synthetic local variable storing the argument when stopped inside the function call + * When inside the scope of multiple nested versions of this shorthand the variables should have unique (and somehow clear) names + * The current synthetic variable value should be displayed when hovering over the underscore part of the shorthand. + * Not any underscore, and the correct value for the correct function when nested + * Other data displays and hover should be equivalent to the elaborated form * Auto-complete + * Completion after `_.` should be equivalent to completion after `(fun x -> x.` in the same context. + * TODO: Check there aren't any snippets or other oddities * Tooltips + * On hovering over the underscore a tooltip should be shown the the type of the anonymous function, and any other context that would normally be shown for a function arg. + * TODO: Does the rhs `x` ever show something different to the lhs `x`? * Navigation and Go To Definition + * None? No document symbols are created by this construct. * Colorization + * TODO * Brace/parenthesis matching + * TODO ## Performance -Please list any notable concerns for impact on the performance of compilation and/or generated code +TODO, but I can't see how this could cause any impact on the performance of generated code as the construct should be entirely erased. -* For existing code -* For the new features +This adds rules to the parser which could cause more backtracking. + +This adds extra work between parsing and checking which requires walking the syntax tree. ## Scaling @@ -147,8 +166,14 @@ Testing should particularly check that compilation is linear (or log-linear or s ## Culture-aware formatting/parsing +TODO, but no + Does the proposed RFC interact with culture-aware formatting and parsing of numbers, dates and currencies? For example, if the RFC includes plaintext outputs, are these outputs specified to be culture-invariant or current-culture. # Unresolved questions +1. Is underscore definitely the appropriate symbol? +2. Is there a good name for this feature? +3. What happens when you nest this? + What parts of the design are still TBD? From cea47521ace04664ce0021b2ea6e4ac66238276c Mon Sep 17 00:00:00 2001 From: Thomas Boby Date: Fri, 23 Sep 2022 15:44:02 +0100 Subject: [PATCH 3/4] Expand diagnostics and alternatives --- ...129-shorthand-anonymous-unary-functions.md | 45 +++++++++++++++---- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/RFCs/FS-1129-shorthand-anonymous-unary-functions.md b/RFCs/FS-1129-shorthand-anonymous-unary-functions.md index a595b01d..91d20516 100644 --- a/RFCs/FS-1129-shorthand-anonymous-unary-functions.md +++ b/RFCs/FS-1129-shorthand-anonymous-unary-functions.md @@ -59,13 +59,11 @@ This transformation should take place after the SyntaxTree is created, during th Given an application expression where the first part of the long ident in the leading expression is an underscore, the elaborated form of `_.expr` should be `fun x -> x.expr` where `x` is a synthetic variable. -And allowing things like (_ + 5) is massively increasing the scope of this extension, without providing much benefit. +Or in other words: "`_.` binds only to dot-lookups and atomic function application." -Or in other words: "_. binds only to dot-lookups and atomic function application." +> We should consider the interaction with property extraction in pattern matching, e.g. -We should consider the interaction with property extraction in pattern matching, e.g. - -match x with +> match x with | _.Length as 0 -> .... @@ -87,6 +85,14 @@ let add x y = x + y # Alternatives +An alternative would be to extend the feature to allow anonymous functions with "captures" such as in [Clojure](https://clojure.org/guides/learn/functions#_anonymous_function_syntax) or [Elixir](https://hexdocs.pm/elixir/Kernel.SpecialForms.html#&/1-anonymous-functions), with something like: + +```fsharp +&(& * 2) => (fun x -> x * 2) +``` + +TODO: why is this not worth it + What other designs have been considered? What is the impact of not doing this? # Compatibility @@ -106,13 +112,34 @@ Please address all necessary compatibility questions: ## Diagnostics -the use of this feature should really be disallowed or give a warning in cases such as [this] -(Allow _.Property shorthand for accessor functions #506 (comment)) e.g. at least if_ is used as a fun _-> ... wildcard pattern somewhere in the enclosing expression . I'm not sure what the exact limitation should be though, since you might reasonably use_ as a nested pattern in a match statement wildcard. But some kind of limitation would seem sensible. +### Discard/wildcard confusion + +It may be confusing to allow the use of this shorthand when a discard has already been used in the same context. + +#### Wildcard `fun` binding ```fsharp -let h : string array array -> (string -> int) array = Array.map (fun _->_.Length) // return tuple from array length and function that ignores argument and returns array length +let a : string -> string = (fun _ -> 5 |> _.ToString()) ``` +The use of this shorthand inside a `fun` with a wildcard pattern should emit a warning. + +#### Discard pattern + +```fsharp +let b : int -> int -> string = function |5 -> (fun _ -> "Five") |_ -> _.ToString() +``` + +The use of this shorthand inside a pattern match with a discard should not warn. + +#### Discarded local variable + +```fsharp +let c : string = let _ = "test" in "asd" |> _.ToString() +``` + +TODO: Should this be OK? + Please list the reasonable expectations for diagnostics for misuse of this feature. ## Tooling @@ -175,5 +202,7 @@ Does the proposed RFC interact with culture-aware formatting and parsing of numb 1. Is underscore definitely the appropriate symbol? 2. Is there a good name for this feature? 3. What happens when you nest this? +4. Are you allowed inline comments between `_` and `.` or between `.` and `expr`? +5. In what contexts should uses of discards/wildcards conflict and raise a warning? What parts of the design are still TBD? From 71d2cdcb15e974d279762c96a8482da6ed6a89c4 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 6 Nov 2023 14:49:50 +0100 Subject: [PATCH 4/4] Update FS-1129-shorthand-anonymous-unary-functions.md --- RFCs/FS-1129-shorthand-anonymous-unary-functions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RFCs/FS-1129-shorthand-anonymous-unary-functions.md b/RFCs/FS-1129-shorthand-anonymous-unary-functions.md index 91d20516..b64d710c 100644 --- a/RFCs/FS-1129-shorthand-anonymous-unary-functions.md +++ b/RFCs/FS-1129-shorthand-anonymous-unary-functions.md @@ -6,7 +6,7 @@ This RFC covers the detailed proposal for this suggestion. - [x] [Suggestion](https://github.com/fsharp/fslang-suggestions/issues/506) - [x] Approved in principle -- [ ] [Implementation](https://github.com/dotnet/fsharp/pull/13907) +- [x] [Implementation](https://github.com/dotnet/fsharp/pull/13907) - [ ] Design Review Meeting(s) with @dsyme and others invitees - [Discussion](https://github.com/fsharp/fslang-design/discussions/FILL-ME-IN) @@ -50,7 +50,7 @@ And the following things should be valid: ```fsharp _.Foo.Bar => fun o -> o.Foo.Bar -_.Foo.[5] => fun o -> o.Foo.[5] +_.Foo.[5] => fun o -> o.Foo[5] _.Foo() => fun o -> o.Foo() _.Foo(5).X => fun o -> o.Foo(5).X ```