What's new in release 7.3
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.
bar
-> Belt.Array.mapU((.b)=>b.foo /*no type annotation needed */)
unit
value is compiled into 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.
let log = x => Js.log(x)
The generated code used to be
function log(x){
console.log(x);
return /* () */ 0;
}
It's now
function 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
let popUndefined = s =>
switch (s.root) {
| None => Js.undefined
| Some(x) =>
s.root = x.tail;
Js.Undefined.return(x.head);
};
function 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
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:
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) {
}
--- 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
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 here: https://github.com/BuckleScript/bucklescript/blob/master/Changes.md#73