Skip to content

Commit 84e8f41

Browse files
authored
Add support for partial application of uncurried functions (#5805)
* Test uncurried cast idea. When curried application is used for an uncurried function, instead of a type error one gets a curried partial application. This allows e.g. to define curried library functions such as `raise` and use them both with curried and uncurried application. * Partial application of an uncurried type returns an uncurried type. * Support type expansion in partial application. * Clean tests * snap
1 parent 35e35c5 commit 84e8f41

12 files changed

+338
-60
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
- Introduce experimental uncurried by default mode. Can be turned on mid-file by adding standalone annotation `@@uncurried`. For experimentation only. https://github.com/rescript-lang/rescript-compiler/pull/5796
1818
- Adding `@@toUncurried` to the file and reformat will convert to uncurried syntax https://github.com/rescript-lang/rescript-compiler/pull/5800
1919
- Add support for unary uncurried pipe in uncurried mode https://github.com/rescript-lang/rescript-compiler/pull/5804
20+
- Add support for partial application of uncurried functions: with uncurried application one can provide a
21+
subset of the arguments, and return a curried type with the remaining ones https://github.com/rescript-lang/rescript-compiler/pull/5805
2022

2123
#### :boom: Breaking Change
2224

jscomp/build_tests/super_errors/expected/bucklescript.res.expected

Lines changed: 0 additions & 16 deletions
This file was deleted.

jscomp/build_tests/super_errors/expected/uncurry_in_curry.res.expected

Lines changed: 0 additions & 16 deletions
This file was deleted.

jscomp/build_tests/super_errors/fixtures/bucklescript.res

Lines changed: 0 additions & 4 deletions
This file was deleted.

jscomp/build_tests/super_errors/fixtures/uncurry_in_curry.res

Lines changed: 0 additions & 3 deletions
This file was deleted.

jscomp/ml/typecore.ml

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2949,6 +2949,26 @@ and type_application env funct (sargs : sargs) : targs * Types.type_expr =
29492949
tvar || List.mem l ls
29502950
in
29512951
let ignored = ref [] in
2952+
let extract_uncurried_type t =
2953+
match (expand_head env t).desc with
2954+
| Tconstr (Pdot (Pdot(Pident {name = "Js"},"Fn",_),_,_),[t],_) -> t
2955+
| _ -> t in
2956+
let lower_uncurried_arity ~nargs t newT =
2957+
match (expand_head env t).desc with
2958+
| Tconstr (Pdot (Pdot(Pident {name = "Js"},"Fn",_),a,_),[_],_) ->
2959+
let arity =
2960+
if String.sub a 0 5 = "arity"
2961+
then int_of_string (String.sub a 5 (String.length a - 5))
2962+
else 0 in
2963+
let newarity = arity - nargs in
2964+
if newarity > 0 then
2965+
let a = "arity" ^ string_of_int newarity in
2966+
let lid:Longident.t = Ldot (Ldot (Lident "Js", "Fn"), a) in
2967+
let path = Env.lookup_type lid env in
2968+
newconstr path [newT]
2969+
else newT
2970+
| _ -> newT
2971+
in
29522972
let rec type_unknown_args (args : lazy_args) omitted ty_fun (syntax_args : sargs)
29532973
: targs * _ =
29542974
match syntax_args with
@@ -3058,8 +3078,12 @@ and type_application env funct (sargs : sargs) : targs * Types.type_expr =
30583078
end;
30593079
([Nolabel, Some exp], ty_res)
30603080
| _ ->
3061-
let ty = funct.exp_type in
3062-
type_args [] [] ~ty_fun:ty (instance env ty) ~sargs
3081+
let ty = extract_uncurried_type funct.exp_type in
3082+
let targs, ret_t = type_args [] [] ~ty_fun:ty (instance env ty) ~sargs in
3083+
let ret_t =
3084+
if funct.exp_type == ty then ret_t
3085+
else lower_uncurried_arity funct.exp_type ~nargs:(List.length !ignored + List.length sargs) ret_t in
3086+
targs, ret_t
30633087

30643088
and type_construct env loc lid sarg ty_expected attrs =
30653089
let opath =

jscomp/test/build.ninja

Lines changed: 2 additions & 1 deletion
Large diffs are not rendered by default.

jscomp/test/uncurried_cast.js

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
'use strict';
2+
3+
var Curry = require("../../lib/js/curry.js");
4+
var Belt_List = require("../../lib/js/belt_List.js");
5+
var Caml_exceptions = require("../../lib/js/caml_exceptions.js");
6+
7+
function raise(e) {
8+
throw e;
9+
}
10+
11+
var map = Belt_List.mapU;
12+
13+
var List = {
14+
map: map
15+
};
16+
17+
var Uncurried = {
18+
raise: raise,
19+
List: List
20+
};
21+
22+
var E = /* @__PURE__ */Caml_exceptions.create("Uncurried_cast.E");
23+
24+
function testRaise(param) {
25+
throw {
26+
RE_EXN_ID: E,
27+
Error: new Error()
28+
};
29+
}
30+
31+
var l = map({
32+
hd: 1,
33+
tl: {
34+
hd: 2,
35+
tl: /* [] */0
36+
}
37+
}, (function (x) {
38+
return x + 1 | 0;
39+
}));
40+
41+
var partial_arg = {
42+
hd: 1,
43+
tl: {
44+
hd: 2,
45+
tl: /* [] */0
46+
}
47+
};
48+
49+
function partial(param) {
50+
return map(partial_arg, param);
51+
}
52+
53+
var ll = Curry._1(partial, (function (x) {
54+
return x + 1 | 0;
55+
}));
56+
57+
function withOpts(xOpt, y, zOpt, w) {
58+
var x = xOpt !== undefined ? xOpt : 3;
59+
var z = zOpt !== undefined ? zOpt : 4;
60+
return ((x + y | 0) + z | 0) + w | 0;
61+
}
62+
63+
function still2Args(param, param$1) {
64+
return withOpts(undefined, 4, param, param$1);
65+
}
66+
67+
var anInt = Curry._1(still2Args, 3)(5);
68+
69+
var StandardNotation = {
70+
testRaise: testRaise,
71+
l: l,
72+
partial: partial,
73+
ll: ll,
74+
withOpts: withOpts,
75+
still2Args: still2Args,
76+
anInt: anInt
77+
};
78+
79+
function testRaise$1() {
80+
return raise({
81+
RE_EXN_ID: E
82+
});
83+
}
84+
85+
var l$1 = map({
86+
hd: 1,
87+
tl: {
88+
hd: 2,
89+
tl: /* [] */0
90+
}
91+
}, (function (x) {
92+
return x + 1 | 0;
93+
}));
94+
95+
var partial_arg$1 = {
96+
hd: 1,
97+
tl: {
98+
hd: 2,
99+
tl: /* [] */0
100+
}
101+
};
102+
103+
function partial$1(param) {
104+
return map(partial_arg$1, param);
105+
}
106+
107+
var ll$1 = partial$1(function (x) {
108+
return x + 1 | 0;
109+
});
110+
111+
function withOpts$1(xOpt, y, zOpt, w) {
112+
var x = xOpt !== undefined ? xOpt : 3;
113+
var z = zOpt !== undefined ? zOpt : 4;
114+
return ((x + y | 0) + z | 0) + w | 0;
115+
}
116+
117+
function still2Args$1(param, param$1) {
118+
return withOpts$1(undefined, 4, param, param$1);
119+
}
120+
121+
var partial_arg$2 = 3;
122+
123+
var anInt$1 = (function (param) {
124+
return still2Args$1(partial_arg$2, param);
125+
})(5);
126+
127+
exports.Uncurried = Uncurried;
128+
exports.E = E;
129+
exports.StandardNotation = StandardNotation;
130+
exports.testRaise = testRaise$1;
131+
exports.l = l$1;
132+
exports.partial = partial$1;
133+
exports.ll = ll$1;
134+
exports.withOpts = withOpts$1;
135+
exports.still2Args = still2Args$1;
136+
exports.anInt = anInt$1;
137+
/* l Not a pure module */

jscomp/test/uncurried_cast.res

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
module Uncurried = {
2+
let raise = (. e) => raise(e)
3+
4+
module List = {
5+
let map = (. l, f) => Belt.List.mapU(l, f)
6+
}
7+
}
8+
9+
exception E
10+
11+
module StandardNotation = {
12+
open Uncurried
13+
14+
let testRaise = () => raise(E)
15+
16+
let l = List.map(.list{1, 2}, (. x) => x + 1)
17+
let partial = List.map(list{1, 2})
18+
let ll = partial((. x) => x + 1)
19+
20+
let withOpts = (. ~x=3, y, ~z=4, w) => x + y + z + w
21+
type unc2 = (. ~z: int=?, int) => int
22+
let still2Args : unc2 = withOpts(4)
23+
let anInt = still2Args(~z=3)(. 5)
24+
}
25+
26+
@@uncurried
27+
28+
open Uncurried
29+
30+
let testRaise = () => raise(E)
31+
32+
let l = List.map(list{1, 2}, x => x + 1)
33+
let partial = List.map(. list{1, 2})
34+
let ll = partial(.x => x + 1)
35+
36+
let withOpts = (~x=3, y, ~z=4, w) => x + y + z + w
37+
type unc2 = (~z: int=?, int) => int
38+
let still2Args : unc2 = withOpts(. 4)
39+
let anInt = still2Args(. ~z=3)(5)

lib/4.06.1/unstable/js_compiler.ml

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43647,6 +43647,26 @@ and type_application env funct (sargs : sargs) : targs * Types.type_expr =
4364743647
tvar || List.mem l ls
4364843648
in
4364943649
let ignored = ref [] in
43650+
let extract_uncurried_type t =
43651+
match (expand_head env t).desc with
43652+
| Tconstr (Pdot (Pdot(Pident {name = "Js"},"Fn",_),_,_),[t],_) -> t
43653+
| _ -> t in
43654+
let lower_uncurried_arity ~nargs t newT =
43655+
match (expand_head env t).desc with
43656+
| Tconstr (Pdot (Pdot(Pident {name = "Js"},"Fn",_),a,_),[_],_) ->
43657+
let arity =
43658+
if String.sub a 0 5 = "arity"
43659+
then int_of_string (String.sub a 5 (String.length a - 5))
43660+
else 0 in
43661+
let newarity = arity - nargs in
43662+
if newarity > 0 then
43663+
let a = "arity" ^ string_of_int newarity in
43664+
let lid:Longident.t = Ldot (Ldot (Lident "Js", "Fn"), a) in
43665+
let path = Env.lookup_type lid env in
43666+
newconstr path [newT]
43667+
else newT
43668+
| _ -> newT
43669+
in
4365043670
let rec type_unknown_args (args : lazy_args) omitted ty_fun (syntax_args : sargs)
4365143671
: targs * _ =
4365243672
match syntax_args with
@@ -43756,8 +43776,12 @@ and type_application env funct (sargs : sargs) : targs * Types.type_expr =
4375643776
end;
4375743777
([Nolabel, Some exp], ty_res)
4375843778
| _ ->
43759-
let ty = funct.exp_type in
43760-
type_args [] [] ~ty_fun:ty (instance env ty) ~sargs
43779+
let ty = extract_uncurried_type funct.exp_type in
43780+
let targs, ret_t = type_args [] [] ~ty_fun:ty (instance env ty) ~sargs in
43781+
let ret_t =
43782+
if funct.exp_type == ty then ret_t
43783+
else lower_uncurried_arity funct.exp_type ~nargs:(List.length !ignored + List.length sargs) ret_t in
43784+
targs, ret_t
4376143785

4376243786
and type_construct env loc lid sarg ty_expected attrs =
4376343787
let opath =
@@ -154453,6 +154477,11 @@ let transformStructureItem ~config mapper item =
154453154477
config.hasReactComponent <- true;
154454154478
check_string_int_attribute_iter.structure_item
154455154479
check_string_int_attribute_iter item;
154480+
let pval_type =
154481+
match pval_type.ptyp_desc with
154482+
| Ptyp_constr ({txt = Ldot (Ldot (Lident "Js", "Fn"), _)}, [t]) -> t
154483+
| _ -> pval_type
154484+
in
154456154485
let coreTypeOfAttr = React_jsx_common.coreTypeOfAttrs pval_attributes in
154457154486
let typVarsOfCoreType =
154458154487
coreTypeOfAttr
@@ -154519,12 +154548,21 @@ let transformStructureItem ~config mapper item =
154519154548
React_jsx_common.raiseErrorMultipleReactComponent ~loc:pstr_loc
154520154549
else (
154521154550
config.hasReactComponent <- true;
154522-
let binding =
154523-
match binding.pvb_expr.pexp_desc with
154551+
let rec removeArityRecord expr =
154552+
match expr.pexp_desc with
154524154553
| Pexp_record
154525154554
([({txt = Ldot (Ldot (Lident "Js", "Fn"), _)}, e)], None) ->
154526-
{binding with pvb_expr = e}
154527-
| _ -> binding
154555+
e
154556+
| Pexp_apply (forwardRef, [(label, e)]) ->
154557+
{
154558+
expr with
154559+
pexp_desc =
154560+
Pexp_apply (forwardRef, [(label, removeArityRecord e)]);
154561+
}
154562+
| _ -> expr
154563+
in
154564+
let binding =
154565+
{binding with pvb_expr = removeArityRecord binding.pvb_expr}
154528154566
in
154529154567
let coreTypeOfAttr =
154530154568
React_jsx_common.coreTypeOfAttrs binding.pvb_attributes

0 commit comments

Comments
 (0)