Skip to content

Commit c0d0ae0

Browse files
cristianoccknitt
andauthored
Add surface syntax for partial application of uncurried functions (#6166)
* Add surface syntax for partial application of uncurried functions Fixes #6165 This PR introduces a new surface syntax for partial application of uncurried functions in ReScript. The syntax allows developers to partially apply uncurried functions more easily and concisely using the ... token in argument lists. The implementation includes changes to the parser, grammar, and printer to handle the new syntax properly. Example: res Copy code let add = (a, b) => a + b let ptl1 = add(1, ...) let result = ptl1(2) // result will be 3 This example demonstrates the new syntax for partial application of the uncurried add function. * Update CHANGELOG.md Co-authored-by: Christoph Knittel <[email protected]> * Update CHANGELOG.md * Update CHANGELOG.md --------- Co-authored-by: Christoph Knittel <[email protected]>
1 parent 2db6f9b commit c0d0ae0

8 files changed

+61
-20
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
1313
# 11.0.0-alpha.4 (Unreleased)
1414

15+
#### :rocket: Main New Feature
16+
- Add surface syntax for partial application of uncurried functions: `foo(1, ...)`. This corresponds to curried application in the old mode. https://github.com/rescript-lang/rescript-compiler/pull/6166
17+
1518
#### :bug: Bug Fix
1619

1720
- Fix broken formatting in uncurried mode for functions with _ placeholder args. https://github.com/rescript-lang/rescript-compiler/pull/6148

res_syntax/src/res_core.ml

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3598,6 +3598,17 @@ and parseCallExpr p funExpr =
35983598
parseCommaDelimitedRegion ~grammar:Grammar.ArgumentList ~closing:Rparen
35993599
~f:parseArgument p
36003600
in
3601+
let resPartialAttr =
3602+
let loc = mkLoc startPos p.prevEndPos in
3603+
(Location.mkloc "res.partial" loc, Parsetree.PStr [])
3604+
in
3605+
let isPartial =
3606+
match p.token with
3607+
| DotDotDot when args <> [] ->
3608+
Parser.next p;
3609+
true
3610+
| _ -> false
3611+
in
36013612
Parser.expect Rparen p;
36023613
let args =
36033614
match args with
@@ -3626,7 +3637,8 @@ and parseCallExpr p funExpr =
36263637
} as expr;
36273638
};
36283639
]
3629-
when (not loc.loc_ghost) && p.mode = ParseForTypeChecker ->
3640+
when (not loc.loc_ghost) && p.mode = ParseForTypeChecker && not isPartial
3641+
->
36303642
(* Since there is no syntax space for arity zero vs arity one,
36313643
* we expand
36323644
* `fn(. ())` into
@@ -3670,22 +3682,20 @@ and parseCallExpr p funExpr =
36703682
| [] -> []
36713683
in
36723684
let apply =
3673-
List.fold_left
3674-
(fun callBody group ->
3685+
Ext_list.fold_left args funExpr (fun callBody group ->
36753686
let dotted, args = group in
36763687
let args, wrap = processUnderscoreApplication p args in
36773688
let exp =
36783689
let uncurried =
36793690
p.uncurried_config |> Res_uncurried.fromDotted ~dotted
36803691
in
3681-
if uncurried then
3682-
let attrs = [uncurriedAppAttr] in
3683-
Ast_helper.Exp.apply ~loc ~attrs callBody args
3684-
else Ast_helper.Exp.apply ~loc callBody args
3692+
let attrs = if uncurried then [uncurriedAppAttr] else [] in
3693+
let attrs = if isPartial then resPartialAttr :: attrs else attrs in
3694+
Ast_helper.Exp.apply ~loc ~attrs callBody args
36853695
in
36863696
wrap exp)
3687-
funExpr args
36883697
in
3698+
36893699
Parser.eatBreadcrumb p;
36903700
apply
36913701

res_syntax/src/res_grammar.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ let isListTerminator grammar token =
299299
| _, Token.Eof
300300
| ExprList, (Rparen | Forwardslash | Rbracket)
301301
| ListExpr, Rparen
302-
| ArgumentList, Rparen
302+
| ArgumentList, (Rparen | DotDotDot)
303303
| TypExprList, (Rparen | Forwardslash | GreaterThan | Equal)
304304
| ModExprList, Rparen
305305
| ( (PatternList | PatternOcamlList | PatternRecord),

res_syntax/src/res_parsetree_viewer.ml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,15 @@ let processUncurriedAppAttribute attrs =
7272
in
7373
process false [] attrs
7474

75+
let processPartialAppAttribute attrs =
76+
let rec process partialApp acc attrs =
77+
match attrs with
78+
| [] -> (partialApp, List.rev acc)
79+
| ({Location.txt = "res.partial"}, _) :: rest -> process true acc rest
80+
| attr :: rest -> process partialApp (attr :: acc) rest
81+
in
82+
process false [] attrs
83+
7584
type functionAttributesInfo = {
7685
async: bool;
7786
bs: bool;

res_syntax/src/res_parsetree_viewer.mli

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ val processBsAttribute : Parsetree.attributes -> bool * Parsetree.attributes
2020
val processUncurriedAppAttribute :
2121
Parsetree.attributes -> bool * Parsetree.attributes
2222

23+
val processPartialAppAttribute :
24+
Parsetree.attributes -> bool * Parsetree.attributes
25+
2326
type functionAttributesInfo = {
2427
async: bool;
2528
bs: bool;

res_syntax/src/res_printer.ml

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3970,6 +3970,13 @@ and printPexpApply ~state expr cmtTbl =
39703970
let uncurried, attrs =
39713971
ParsetreeViewer.processUncurriedAppAttribute expr.pexp_attributes
39723972
in
3973+
let partial, attrs = ParsetreeViewer.processPartialAppAttribute attrs in
3974+
let args =
3975+
if partial then
3976+
let dummy = Ast_helper.Exp.constant (Ast_helper.Const.int 0) in
3977+
args @ [(Asttypes.Labelled "...", dummy)]
3978+
else args
3979+
in
39733980
let dotted = state.uncurried_config |> Res_uncurried.getDotted ~uncurried in
39743981
let callExprDoc =
39753982
let doc = printExpressionWithComments ~state callExpr cmtTbl in
@@ -4580,7 +4587,7 @@ and printArguments ~state ~dotted
45804587
and printArgument ~state (argLbl, arg) cmtTbl =
45814588
match (argLbl, arg) with
45824589
(* ~a (punned)*)
4583-
| ( Asttypes.Labelled lbl,
4590+
| ( Labelled lbl,
45844591
({
45854592
pexp_desc = Pexp_ident {txt = Longident.Lident name};
45864593
pexp_attributes = [] | [({Location.txt = "res.namedArgLoc"}, _)];
@@ -4594,7 +4601,7 @@ and printArgument ~state (argLbl, arg) cmtTbl =
45944601
let doc = Doc.concat [Doc.tilde; printIdentLike lbl] in
45954602
printComments doc cmtTbl loc
45964603
(* ~a: int (punned)*)
4597-
| ( Asttypes.Labelled lbl,
4604+
| ( Labelled lbl,
45984605
{
45994606
pexp_desc =
46004607
Pexp_constraint
@@ -4622,7 +4629,7 @@ and printArgument ~state (argLbl, arg) cmtTbl =
46224629
in
46234630
printComments doc cmtTbl loc
46244631
(* ~a? (optional lbl punned)*)
4625-
| ( Asttypes.Optional lbl,
4632+
| ( Optional lbl,
46264633
{
46274634
pexp_desc = Pexp_ident {txt = Longident.Lident name};
46284635
pexp_attributes = [] | [({Location.txt = "res.namedArgLoc"}, _)];
@@ -4642,27 +4649,32 @@ and printArgument ~state (argLbl, arg) cmtTbl =
46424649
(loc, {expr with pexp_attributes = attrs})
46434650
| _ -> (expr.pexp_loc, expr)
46444651
in
4645-
let printedLbl =
4652+
let printedLbl, dotdotdot =
46464653
match argLbl with
4647-
| Asttypes.Nolabel -> Doc.nil
4648-
| Asttypes.Labelled lbl ->
4654+
| Nolabel -> (Doc.nil, false)
4655+
| Labelled "..." ->
4656+
let doc = Doc.text "..." in
4657+
(printComments doc cmtTbl argLoc, true)
4658+
| Labelled lbl ->
46494659
let doc = Doc.concat [Doc.tilde; printIdentLike lbl; Doc.equal] in
4650-
printComments doc cmtTbl argLoc
4651-
| Asttypes.Optional lbl ->
4660+
(printComments doc cmtTbl argLoc, false)
4661+
| Optional lbl ->
46524662
let doc =
46534663
Doc.concat [Doc.tilde; printIdentLike lbl; Doc.equal; Doc.question]
46544664
in
4655-
printComments doc cmtTbl argLoc
4665+
(printComments doc cmtTbl argLoc, false)
46564666
in
46574667
let printedExpr =
46584668
let doc = printExpressionWithComments ~state expr cmtTbl in
46594669
match Parens.expr expr with
4660-
| Parens.Parenthesized -> addParens doc
4670+
| Parenthesized -> addParens doc
46614671
| Braced braces -> printBraces doc expr braces
46624672
| Nothing -> doc
46634673
in
46644674
let loc = {argLoc with loc_end = expr.pexp_loc.loc_end} in
4665-
let doc = Doc.concat [printedLbl; printedExpr] in
4675+
let doc =
4676+
if dotdotdot then printedLbl else Doc.concat [printedLbl; printedExpr]
4677+
in
46664678
printComments doc cmtTbl loc
46674679

46684680
and printCases ~state (cases : Parsetree.case list) cmtTbl =

res_syntax/tests/printer/expr/UncurriedByDefault.res

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,5 @@ let fnU = (_x): ((unit) => unit) => fooC
157157
let aU = (() => "foo")->Ok
158158

159159
Ok("_")->Belt.Result.map(concatStrings(_, "foo"))
160+
161+
let ptl1 = add(1, ...)

res_syntax/tests/printer/expr/expected/UncurriedByDefault.res.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,5 @@ let fnU = (_x): (unit => unit) => fooC
157157
let aU = (() => "foo")->Ok
158158

159159
Ok("_")->Belt.Result.map(concatStrings(_, "foo"))
160+
161+
let ptl1 = add(1, ...)

0 commit comments

Comments
 (0)