Skip to content

Commit 3758f13

Browse files
committed
Merge pull request #412 from bloomberg/non_intrusive_uncurry_support
Non intrusive uncurry support
2 parents da37f48 + 1b513b0 commit 3758f13

28 files changed

+181
-151
lines changed

docs/Curry-and-Uncurry-functions.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ To apply a function, you can do this
1717

1818
```ocaml
1919
f 3 "x"
20-
f_uncurry #@ (3,"x")
20+
f_uncurry (3,"x") [@uncurry]
2121
```
2222
For uncurried function applicaton, BuckleScript is guaranteed to
2323
compile it in the same way as JS code
@@ -42,7 +42,7 @@ Both are correct code, but the second one is more efficient.
4242

4343
```ocaml
4444
let f = fun a b -> a + string_of_int b
45-
let f_uncurry = fun %uncurry a b -> a + string_of_int b
45+
let f_uncurry = fun [@uncurry] (a, b) -> a + string_of_int b
4646
```
4747

4848
- When is uncurried function recommended

docs/OCaml-call-JS.md

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,18 @@ improve the generated code.
3333
}
3434
|}]
3535
```
36-
In the expression level, i.e, `[%bs.raw ...]` user can add a type annotation, the compiler would use such type annotation to deduce its arities. for example, the next three versions:
36+
In the expression level, i.e, `[%bs.raw ...]` user can add a type
37+
annotation, for example:
3738

3839
```ocaml
39-
let f = [%bs.raw ("Math.max" : float -> float -> float) ] 3.0
40-
let f : float -> float -> float = [%bs.raw "Math.max" ] 3.0
41-
let f = ([%bs.raw "Math.max"] : float -> float -> float ) 3.0
40+
let f : float * float -> float [@uncurry] = [%bs.raw "Math.max" ]
41+
in f (3.0, 2.0) [@uncurry]
4242
```
4343
will be translated into
4444

4545
```js
46-
function f(prim){
47-
return Math.max(3.0,prim);
48-
}
46+
var f = Math.max ;
47+
f(3.0,2.0)
4948
```
5049
Caveat:
5150
1. So far we don't do any sanity check in the quoted text (syntax check is a long-term goal)
@@ -220,7 +219,7 @@ On top of this we can write normal OCaml functions, for example:
220219
```OCaml
221220
let assert_equal = eq
222221
let from_suites name suite =
223-
describe name (fun%uncurry () ->
222+
describe name (fun [@uncurry] () ->
224223
List.iter (fun (name, code) -> it name code) suite)
225224
```
226225

@@ -271,8 +270,8 @@ val f : < hi : ('a * 'b -> 'c [@uncurry] ; .. > Js.t -> 'a -> 'b -> 'c
271270
This attribute helps create JavaScript object literal
272271

273272
```ocaml
274-
let a = f ({ hi = fun %uncurry (x,y) -> x + y}[@bs.obj]) 1 2
275-
let b = f ({ hi = fun %uncurry (x,y) -> x +. y}[@bs.obj]) 1. 2.
273+
let a = f ({ hi = fun [@uncurry] (x,y) -> x + y}[@bs.obj]) 1 2
274+
let b = f ({ hi = fun [@uncurry] (x,y) -> x +. y}[@bs.obj]) 1. 2.
276275
```
277276

278277
Generated code is like below

jscomp/lam_compile.ml

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,6 @@ and get_exp_with_args (cxt : Lam_compile_defs.cxt) lam args_lambda
177177
end
178178
) args_lambda ([], []) in
179179

180-
181180
match closed_lambda with
182181
| Some (Lfunction (_, params, body))
183182
when Ext_list.same_length params args_lambda ->
@@ -204,13 +203,13 @@ and get_exp_with_args (cxt : Lam_compile_defs.cxt) lam args_lambda
204203
E.unit
205204
| {name = "CamlinternalMod"; _}, "init_mod" ,
206205
[
207-
_ ;
208-
shape ;
209-
(* Module []
210-
TODO: add a function [empty_shape]
211-
This pattern match is fragile, since it depends
212-
on how we compile [Lconst]
213-
*)
206+
_ ;
207+
shape ;
208+
(* Module []
209+
TODO: add a function [empty_shape]
210+
This pattern match is fragile, since it depends
211+
on how we compile [Lconst]
212+
*)
214213
] when Js_of_lam_module.is_empty_shape shape
215214
->
216215
E.dummy_obj () (* purely type definition*)
@@ -511,8 +510,11 @@ and
511510
compile_lambda cxt
512511
(Lapply (an, (args' @ args), (Lam_util.mk_apply_info App_na)))
513512
(* External function calll *)
514-
| Lapply(Lprim(Pfield (n,_), [ Lprim(Pgetglobal id,[])]), args_lambda,_info) ->
515-
513+
| Lapply(Lprim(Pfield (n,_), [ Lprim(Pgetglobal id,[])]), args_lambda,
514+
{apply_status = App_na | App_ml_full}) ->
515+
(* Note we skip [App_js_full] since [get_exp_with_args] dont carry
516+
this information, we should fix [get_exp_with_args]
517+
*)
516518
get_exp_with_args cxt lam args_lambda id n env
517519

518520

@@ -875,10 +877,10 @@ and
875877
end
876878

877879
| fn :: rest ->
878-
compile_lambda cxt @@
879-
Lambda.Lapply (fn, rest ,
880-
{apply_loc = Location.none;
881-
apply_status = App_js_full})
880+
compile_lambda cxt
881+
(Lapply (fn, rest ,
882+
{apply_loc = Location.none;
883+
apply_status = App_js_full}))
882884
| _ -> assert false
883885
else
884886
begin match args_lambda with

jscomp/ppx_entry.ml

Lines changed: 37 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -279,27 +279,18 @@ let gen_fn_mk loc arity args : Parsetree.expression_desc =
279279

280280

281281

282-
let handle_raw ?ty loc e attrs =
283-
let attrs =
284-
match ty with
285-
| Some ty ->
286-
Parsetree_util.attr_attribute_from_type ty :: attrs
287-
| None -> attrs in
282+
let handle_raw loc e =
288283
Ast_helper.Exp.letmodule
289284
{txt = tmp_module_name; loc }
290285
(Ast_helper.Mod.structure [
291286
Ast_helper.Str.primitive
292-
(Ast_helper.Val.mk ~attrs {loc ; txt = tmp_fn}
287+
(Ast_helper.Val.mk {loc ; txt = tmp_fn}
293288
~prim:[prim]
294-
(Ast_helper.Typ.arrow "" predef_string_type predef_any_type))]
295-
)
296-
(Ast_helper.Exp.constraint_ ~loc
289+
(Ast_helper.Typ.arrow "" predef_string_type predef_any_type))])
297290
(Ast_helper.Exp.apply
298291
(Ast_helper.Exp.ident {txt= Ldot(Lident tmp_module_name, tmp_fn) ; loc})
299292
[("",e)])
300-
(match ty with
301-
| Some ty -> ty
302-
| None -> predef_any_type))
293+
303294

304295

305296

@@ -650,25 +641,10 @@ let rec unsafe_mapper : Ast_mapper.mapper =
650641
PStr
651642
( [{ pstr_desc = Pstr_eval ({
652643
pexp_desc = Pexp_constant (Const_string (_, _)) ;
653-
pexp_attributes = attrs } as e ,
644+
} as e ,
654645
_); pstr_loc = _ }]))
655646
->
656-
657-
handle_raw loc e attrs
658-
| Pexp_extension( {txt = "bs.raw"; loc}, PStr
659-
( [{ pstr_desc = Parsetree.Pstr_eval ({
660-
pexp_desc =
661-
Pexp_constraint (
662-
{pexp_desc = Pexp_constant (Const_string (_, _)) ; _}
663-
as e,
664-
ty)
665-
; pexp_attributes = attrs} , _); }]))
666-
| Pexp_constraint({pexp_desc = Pexp_extension( {txt = "bs.raw"; loc}, PStr
667-
( [{ pstr_desc = Pstr_eval ({
668-
pexp_desc =
669-
Pexp_constant (Const_string (_, _))
670-
; pexp_attributes = attrs} as e , _); }]))}, ty)
671-
-> handle_raw ~ty loc e attrs
647+
handle_raw loc e
672648
| Pexp_extension({txt = "bs.raw"; loc}, (PTyp _ | PPat _ | PStr _))
673649
->
674650
Location.raise_errorf ~loc "bs.raw can only be applied to a string"
@@ -679,24 +655,26 @@ let rec unsafe_mapper : Ast_mapper.mapper =
679655
| Pexp_extension ({txt = "bs.debugger"; loc} , payload)
680656
-> handle_debugger loc payload
681657
(** End rewriting *)
682-
683-
| Pexp_extension
684-
({txt = "uncurry";loc},
685-
PStr
686-
[{
687-
pstr_desc =
688-
Pstr_eval
689-
({pexp_desc =
690-
Pexp_fun ("", None, pat ,
691-
body)},
692-
_)}])
693-
->
694-
begin match body.pexp_desc with
695-
| Pexp_fun _ ->
696-
Location.raise_errorf ~loc
697-
"`fun %%uncurry (param0, param1) -> ` instead of `fun %%uncurry param0 param1 ->` "
698-
| _ -> handle_uncurry_generation loc pat body e mapper
658+
| Pexp_fun ("", None, pat , body)
659+
->
660+
let loc = e.pexp_loc in
661+
begin match Ext_list.exclude_with_fact (function
662+
| {Location.txt = "uncurry"; _}, _ -> true
663+
| _ -> false) e.pexp_attributes with
664+
| None, _ -> Ast_mapper.default_mapper.expr mapper e
665+
| Some _, attrs
666+
->
667+
begin match body.pexp_desc with
668+
| Pexp_fun _ ->
669+
Location.raise_errorf ~loc
670+
{| `fun [@uncurry] (param0, param1) -> `
671+
instead of `fun [@uncurry] param0 param1 ->` |}
672+
| _ ->
673+
handle_uncurry_generation loc pat body
674+
{e with pexp_attributes = attrs } mapper
675+
end
699676
end
677+
700678
| Pexp_apply ({pexp_desc = Pexp_ident {txt = Lident "#@"; loc}},
701679
[("", fn);
702680
("", pat)])
@@ -733,6 +711,18 @@ let rec unsafe_mapper : Ast_mapper.mapper =
733711
) )
734712
])
735713
-> handle_obj_property loc obj name e mapper
714+
| Pexp_apply (fn,
715+
[("", pat)]) ->
716+
let loc = e.pexp_loc in
717+
begin match Ext_list.exclude_with_fact (function
718+
| {Location.txt = "uncurry"; _}, _ -> true
719+
| _ -> false) e.pexp_attributes with
720+
| None, _ -> Ast_mapper.default_mapper.expr mapper e
721+
| Some _, attrs ->
722+
handle_uncurry_application loc fn pat
723+
{e with pexp_attributes = attrs} mapper
724+
end
725+
736726
| Pexp_record (label_exprs, None) ->
737727
begin match (* exclude {[ u with ..]} syntax currently *)
738728
Ext_list.exclude_with_fact

jscomp/runtime/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ $(addsuffix .cmj, $(OTHERS)): caml_builtin_exceptions.cmj block.cmj js.cmj
2121
RUNTIME := $(addsuffix .cmj, $(SOURCE_LIST))
2222

2323

24-
COMPFLAGS += $(MODULE_FLAGS) -I ../stdlib -nostdlib -nopervasives -open Pervasives -w -40 -js-npm-output-path $(npm_package_name):lib/js -js-no-builtin-ppx-mli
24+
COMPFLAGS += $(MODULE_FLAGS) -I ../stdlib -nostdlib -nopervasives -open Pervasives -w -40 -js-npm-output-path $(npm_package_name):lib/js
2525

2626

2727

jscomp/runtime/caml_float.ml

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ let caml_modf_float (x : float) : float * float =
117117
else if Js.Float.is_nan x then Js.Float.nan , Js.Float.nan
118118
else (1. /. x , x)
119119

120-
let caml_ldexp_float = [%bs.raw ({| function (x,exp) {
120+
let caml_ldexp_float : float * int -> float [@uncurry] = [%bs.raw {| function (x,exp) {
121121
exp |= 0;
122122
if (exp > 1023) {
123123
exp -= 1023;
@@ -134,11 +134,11 @@ let caml_ldexp_float = [%bs.raw ({| function (x,exp) {
134134
x *= Math.pow(2, exp);
135135
return x;
136136
}
137-
|} : float -> int -> float)]
137+
|}]
138138

139139

140140

141-
let caml_frexp_float = [%bs.raw ({|function (x) {
141+
let caml_frexp_float : float -> float * int [@uncurry]= [%bs.raw {|function (x) {
142142
if ((x == 0) || !isFinite(x)) return [ x, 0];
143143
var neg = x < 0;
144144
if (neg) x = - x;
@@ -148,7 +148,7 @@ let caml_frexp_float = [%bs.raw ({|function (x) {
148148
if (neg) x = - x;
149149
return [x, exp];
150150
}
151-
|} : float -> float * int )]
151+
|}]
152152

153153
let caml_float_compare (x : float) (y : float ) =
154154
if x = y then 0
@@ -178,18 +178,17 @@ let caml_log1p_float : float -> float = function x ->
178178
if z = 0. then x else x *. log y /. z
179179

180180

181-
let caml_hypot_float = [%bs.raw ({| function (x, y) {
181+
let caml_hypot_float : float * float -> float [@uncurry] = [%bs.raw {| function (x, y) {
182182
var x0 = Math.abs(x), y0 = Math.abs(y);
183183
var a = Math.max(x0, y0), b = Math.min(x0,y0) / (a?a:1);
184184
return a * Math.sqrt(1 + b*b);
185185
}
186-
|} : float -> float -> float)
187-
]
186+
|}]
188187

189188

190-
let caml_log10_float = [%bs.raw ({| function (x) {
189+
let caml_log10_float : float -> float [@uncurry] = [%bs.raw {| function (x) {
191190
return Math.LOG10E * Math.log(x); }
192-
|} : float -> float) ]
191+
|} ]
193192

194193

195194
let caml_cosh_float x = exp x +. exp (-. x) /. 2.

jscomp/runtime/caml_float.mli

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,13 @@ val caml_int32_bits_of_float : float -> int32
3333

3434
val caml_classify_float : float -> fpclass
3535
val caml_modf_float : float -> float * float
36-
val caml_ldexp_float : float -> int -> float
37-
val caml_frexp_float : float -> float * int
36+
37+
val caml_ldexp_float : float * int -> float [@uncurry]
38+
val caml_frexp_float : float -> float * int [@uncurry]
3839
val caml_float_compare : float -> float -> int
3940
val caml_copysign_float : float -> float -> float
4041
val caml_expm1_float : float -> float
4142

42-
val caml_hypot_float : float -> float -> float
43-
val caml_log10_float : float -> float
43+
val caml_hypot_float : float * float -> float [@uncurry]
44+
45+
val caml_log10_float : float -> float [@uncurry]

jscomp/runtime/caml_format.ml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -361,9 +361,8 @@ let aux f (i : nativeint) =
361361
f.filter <- " ";
362362
let n = f.prec -Js.String.length !s in
363363
if n > 0 then
364-
s := repeat n "0" ^ !s
365-
end
366-
;
364+
s := repeat (n, "0")[@uncurry] ^ !s
365+
end ;
367366
finish_formatting f !s
368367

369368
let caml_format_int fmt i =
@@ -485,7 +484,7 @@ let caml_int64_format fmt x =
485484
f.filter <- " ";
486485
let n = f.prec -Js.String.length !s in
487486
if n > 0 then
488-
s := repeat n "0" ^ !s
487+
s := repeat (n, "0") [@uncurry] ^ !s
489488
end;
490489

491490
finish_formatting f !s

jscomp/runtime/caml_int64.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ let to_hex x =
388388
if pad <= 0 then
389389
aux x.hi ^ lo
390390
else
391-
aux x.hi ^ Caml_utils.repeat pad "0" ^ lo
391+
aux x.hi ^ Caml_utils.repeat(pad, "0") [@uncurry] ^ lo
392392

393393
let discard_sign x = {x with hi = Nativeint.logand 0x7fff_ffffn x.hi }
394394

jscomp/runtime/caml_io.ml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ let stdout = {
4444
output = (fun _ s ->
4545
let v =Js.String.length s - 1 in
4646
if [%bs.raw{| (typeof process !== "undefined") && process.stdout && process.stdout.write|}] then
47-
([%bs.raw{| process.stdout.write |} ] : string -> unit) s
47+
([%bs.raw{| process.stdout.write |} ] : string -> unit [@uncurry]) s [@uncurry]
4848
else
4949
if s.[v] = '\n' then
5050
Js.log (Js.String.slice s 0 v)
@@ -86,10 +86,8 @@ let caml_ml_output (oc : out_channel) (str : string) offset len =
8686
else Js.String.slice str offset len in
8787
if [%bs.raw{| (typeof process !== "undefined") && process.stdout && process.stdout.write |}] &&
8888
oc == stdout then
89-
begin
89+
([%bs.raw{| process.stdout.write |}] : string -> unit [@uncurry] ) str [@uncurry]
9090

91-
([%bs.raw{| process.stdout.write |}] : string -> unit ) str
92-
end
9391
else
9492
begin
9593

jscomp/runtime/caml_utils.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
(* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul *)
3232

3333

34-
let repeat : int -> string -> string = [%bs.raw{| (String.prototype.repeat && function (count,self){return self.repeat(count)}) ||
34+
let repeat : int * string -> string [@uncurry] = [%bs.raw{| (String.prototype.repeat && function (count,self){return self.repeat(count)}) ||
3535
function(count , self) {
3636
if (self.length == 0 || count == 0) {
3737
return '';

jscomp/runtime/caml_utils.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,4 @@
2929

3030

3131

32-
val repeat : int -> string -> string
32+
val repeat : int * string -> string [@uncurry]

0 commit comments

Comments
 (0)