Announcing BuckleScript 7.3
Overview
We are happy to announce that bs-platform@7.3
is available for testing, you
can try it with npm install bs-platform@7.3.1
.
For those unfamiliar with bs-platform, it is the platform for compiling ReasonML and OCaml to fast and readable JavaScript.
This is a major release with some highlighted features as below:
Generalized uncurry calling convention support
You can use an uncurried function as conveniently as a curried one now, this is an exciting change that we wrote a separate post for details.
For uncurried support, we also fixed a long standing issue so that type inference follows naturally using the new encoding.
REbar
-> Belt.Array.mapU((.b)=>b.foo /*no type annotation needed */)
The unit
value now compiles to undefined
In ReasonML, when a function does not return any meaningful value, it returns a
value that is ()
of type unit
. In native backend, the dummy value ()
is
compiled into a const zero. We used to inherit this in JS backend as well.
However, this is a semantics mismatch since in JS, if the function does not
return anything, it defaults to undefined
. In this release, we make it more
consistent with JS: compiling ()
into undefined
. Since in JS, return
undefined
can be ignored in tail position, this leads to some other nice
enhancement.
RElet log = x => Js.log(x)
The generated code used to be
JSfunction log(x){
console.log(x);
return /* () */ 0;
}
It's now
JSfunction log(x){
console.log(x)
}
Various improvements in code generation
We have increased the readability of the generated code in several common places, we believe that we reached an important milestone that if you write code using features that have counterparts in JS, the generated code is readable. This is not a small achievement given that quite a lot of the compiler code base is shared between native backend and JS backend.
There are some features that are not available in JS, for example, complex pattern matches, the readability of those pieces of generated code will continue being improved.
Take several enhancement below as examples:
Meaningful pattern match variable names
RElet popUndefined = s =>
switch (s.root) {
| None => Js.undefined
| Some(x) =>
s.root = x.tail;
Js.Undefined.return(x.head);
};
DIFFfunction popUndefined(s) {
- var match = s.root;
- if (match !== null) {
- s.root = match.tail;
- return match.head;
+ var x = s.root;
+ if (x !== undefined) {
+ s.root = x.tail;
+ return x.head;
}
}
When pattern match against a compounded expression, the compiler used to use a
temporary name match
, now we employ better heuristics to generate meaningful
names for such temporary variables.
Eliminate intermediate variable names when inlining
DIFF function everyU(arr, b) {
var len = arr.length;
- var arr$1 = arr;
var _i = 0;
- var b$1 = b;
- var len$1 = len;
while(true) {
var i = _i;
- if (i === len$1) {
+ if (i === len) {
return true;
- } else if (b$1(arr$1[i])) {
- _i = i + 1 | 0;
- continue ;
- } else {
+ }
+ if (!b(arr[i])) {
return false;
}
+ _i = i + 1 | 0;
+ continue ;
};
}
The above diff is the generated code for Belt.Array.everyU
, the intermediate
variables were introduced when inlining an auxiliary function, such duplication
were removed in this release.
Flatten if/else branch making use of JS's early return
idiom
Take the same diff from above, you will notice that the second else
following
if(..) continue
is removed.
Below are similar diffs benefiting from such enhancement:
DIFF function has(h, key) {
@@ -133,21 +123,18 @@ function has(h, key) {
var nid = Caml_hash_primitive.caml_hash_final_mix(Caml_hash_primitive.caml_hash_mix_string(0, key)) & (h_buckets.length - 1 | 0);
var bucket = h_buckets[nid];
if (bucket !== undefined) {
- var key$1 = key;
var _cell = bucket;
while(true) {
var cell = _cell;
- if (cell.key === key$1) {
+ if (cell.key === key) {
return true;
- } else {
- var match = cell.next;
- if (match !== undefined) {
- _cell = match;
- continue ;
- } else {
- return false;
- }
}
+ var nextCell = cell.next;
+ if (nextCell === undefined) {
+ return false;
+ }
+ _cell = nextCell;
+ continue ;
};
} else {
return false;
@@ -155,17 +142,17 @@ function has(h, key) {
}
DIFF--- a/lib/js/belt_List.js
+++ b/lib/js/belt_List.js
@@ -15,9 +15,8 @@ function head(x) {
function headExn(x) {
if (x) {
return x[0];
- } else {
- throw new Error("headExn");
}
+ throw new Error("headExn");
}
For loop minor-enhancement
DIFF function shuffleInPlace(xs) {
var len = xs.length;
- for(var i = 0 ,i_finish = len - 1 | 0; i <= i_finish; ++i){
+ for(var i = 0; i < len; ++i){
swapUnsafe(xs, i, Js_math.random_int(i, len));
}
- return /* () */0;
+
}
Reason's for .. in
only provide closed interval iterating, so it is quite
common to write for (i in 0 to Array.length(x) - 1) { .. }
, we did the
tweaking above to make the generated code more readable.
A full list of changes is available in our Changelog file.