Skip to content

Commit 2dd6b31

Browse files
committed
Refine structure, clarify features & breaking change
1 parent c19690a commit 2dd6b31

File tree

1 file changed

+158
-51
lines changed

1 file changed

+158
-51
lines changed

_blogposts/2021-02-09-release-9-0.mdx

Lines changed: 158 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -10,63 +10,105 @@ description: |
1010

1111
## Introduction
1212

13-
ReScript is a soundly typed language with an optimizing compiler focused on the JS platform.
14-
It's focused on type safety, performance and JS interop. It used to be called BuckleScript.
13+
We are happy to announce ReScript 9.0!
1514

16-
[[email protected]](https://www.npmjs.com/package/bs-platform/v/9.0.0) is now available, you can try it via
15+
ReScript is a strongly typed language that compiles to efficient, fully-typed and human-readable JavaScript. It comes with one of the fastest JS compiler toolchains available today and offers first class support for ReactJS development.
16+
17+
Use `npm` to install the newest [9.0.0 release](https://www.npmjs.com/package/bs-platform/v/9.0.0) with the following command:
1718

1819
```
19-
20+
npm install [email protected] --save-dev
2021
```
2122

22-
The changes are listed [here](https://github.com/rescript-lang/rescript-compiler/blob/master/Changes.md#90).
23+
In this post we will highlight the most notable changes. The full changelog for this release can be found [here](https://github.com/rescript-lang/rescript-compiler/blob/master/Changes.md#90).
2324

24-
We will go through some highlighted user visible changes.
25+
## Compiler Improvements
2526

26-
### Customized light weight stdlib
27+
### New External Stdlib Configuration
2728

2829
This is a long-awaited [feature request](https://github.com/rescript-lang/rescript-compiler/pull/2171).
2930

30-
Since this release, users can add such lines in bsconfig.json to make our compiler pakcage a dev dependency.
31+
Our compiler comes with a set of stdlib modules (such as `Belt`, `Pervasives`, etc.) for core functionality. Compiled ReScript code relies on the JS runtime version of these stdlib modules.
32+
33+
In previous versions, users couldn't ship their compiled JS code without defining a `package.json` dependency on `bs-platform`. Whenever a ReScript developer wanted to publish a package just for pure JS consumption / lean container deployment, they were required to use a bundler to bundle up their library / stdlib code, which made things way more complex and harder to understand.
34+
35+
To fix this problem, we now publish our pre-compiled stdlib JS files as a separate npm package called [`@rescript/std`](https://www.npmjs.com/package/@rescript/std). Each new `bs-platform` release has a matching `@rescript/std` release for runtime compatibility.
36+
37+
We also introduced a new configuration within our `bsconfig.json` file to tell the compiler to use our pre-compiled package instead:
3138

3239
```json
33-
"external-stdlib" : "@rescript/std"
40+
{
41+
/* ... */
42+
"external-stdlib" : "@rescript/std"
43+
}
44+
```
45+
46+
With this configuration set, compiled JS code will now point to the defined `external-stdlib` path:
47+
48+
<CodeTab labels={["ReScript", "JavaScript"]}>
49+
50+
```res
51+
Belt.Array.forEach([1,2,3], (num) => Js.log(num))
3452
```
3553

36-
Our current compiler package is quite large since it ships both prebuilt compilers for 3 major platforms
37-
and the standard library. With this configuration, the compiler will generate js files on top of `@rescript/std` so that
38-
the compiler package is not needed after the JS files are generated. This is helpful if you want to export ReScript based
39-
libraries to JS users while not asking JS users to install a large dependency.
54+
```js
55+
// Note the import path starting with "@rescript/std"
56+
import * as Array from "@rescript/std/lib/es6/belt_Array.mjs";
57+
58+
Belt_Array.forEach([
59+
1,
60+
2,
61+
3
62+
], (function (num) {
63+
console.log(num);
64+
65+
}));
66+
```
67+
68+
</CodeTab>
69+
70+
The JavaScript output above was compiled with an `es6` target, but will also work with `commonjs`.
4071

41-
Caveat: make sure the version of `@rescript/std` is the same as `bs-platform`, use this configuration only when the package size
42-
is indeed a problem for you.
72+
**Important:** When using this option, you need to make sure that the version number of `bs-platform` and `@rescript/std` matches with the same version number in your `package.json` file, otherwise you'll eventually run into runtime problems due to mismatching stdlib behavior!
4373

44-
### Zero-cost bundle size when adding ReScript
74+
To prevent unnecessary complications, only use this feature when...
75+
- You want to ship a library for JS / TS consumers without making them depend on `bs-platform`
76+
- You can't depend on `bs-platform` due to toolchain size (docker containers, low-storage deployment devices, etc)
4577

46-
With each release, we keep a close eye on generating code that is optimized for tree-shaking.
47-
We believe we reached a milestone that ReScript adds close to zero cost to your bundle-size.
48-
Unlike many other programming languages compiled into JS, the bundled code is almost ReScript runtime free and
49-
the generated library code fits the tree-shaking principle very well.
78+
### Less Bundle Bloat when Adding ReScript
5079

51-
To demonstrate what we achieved, we made a [repo](https://github.com/bobzhang/zero-cost-rescript) so that
52-
you can try it and see how good the bundled code is.
80+
With each release, we keep a close eye on generating code that is optimized for tree-shaking. We believe we reached a milestone where ReScript doesn't add any bloat to your bundle-size (this is what we call our "zero-cost" philosophy).
5381

82+
Unlike many other compile-to-js languages, the bundled code is almost ReScript runtime free and the generated library code fits the tree-shaking principle very well.
5483

55-
### Improved code generation for pattern match
84+
We made a small [demo repo](https://github.com/bobzhang/zero-cost-rescript) and added the precompiled JS bundles to demonstrate what we've achieved. Check it out!
5685

57-
We continue improving the quality of generated code in this release.
86+
### Improved Code Generation for Pattern Matching
5887

59-
Take this [issue](https://github.com/rescript-lang/rescript-compiler/issues/4924) for example:
88+
We fine-tuned our pattern matching engine to optimize the JS output even more. Here is an example of a pretty substantial optimization, based on [this issue](https://github.com/rescript-lang/rescript-compiler/issues/4924):
6089

6190
```res
91+
type test =
92+
| NoArg
93+
| AnotherNoArg
94+
| OtherArg(int)
95+
6296
let test = x =>
6397
switch x {
6498
| NoArg => true
6599
| _ => false
66100
}
67101
```
68102

69-
It used to generate the following code:
103+
Now let's have a look at how the JS output would look like in comparison to older versions:
104+
105+
<CodeTab labels={["9.0 Output", "8.4 Output" ]}>
106+
107+
```js
108+
function test(x){
109+
return x === 0
110+
}
111+
```
70112

71113
```js
72114
function test(x) {
@@ -76,20 +118,18 @@ function test(x) {
76118
return false;
77119
}
78120
}
79-
80-
```
81-
which now gets optimized to:
82-
```js
83-
function test(x){
84-
return x === 0
85-
}
86121
```
87122

88-
This is possible because our optimizer will try to analyze several predicates and get rid of redundant ones.
89-
More diffs can be found [here](https://github.com/rescript-lang/rescript-compiler/pull/4927/files?file-filters%5B%5D=.js).
90123

91-
Another important improvement is that we fixed the pattern match offset issue, which leads to the consequence, that magic numbers will not be generated for complex pattern matches anymore.
92-
Below is a representative diff resulting from this cleanup:
124+
</CodeTab>
125+
126+
As you can see, the 9.0 compiler removes all the unnecessary `typeof` checks!
127+
128+
This is possible because our optimizer will try to analyze several predicates and get rid of redundant ones. More diffs can be found [here](https://github.com/rescript-lang/rescript-compiler/pull/4927/files?file-filters%5B%5D=.js).
129+
130+
Another important improvement is that we fixed the pattern match offset issue, which lead to the consequence that magic numbers will not be generated for complex pattern matches anymore.
131+
132+
For those interested in the details, here is a representative diff resulting from this cleanup:
93133

94134
```diff
95135
function is_space(param){
@@ -105,30 +145,97 @@ function is_space(param){
105145
}
106146
```
107147

108-
### Syntax changes
148+
## Syntax Improvements
149+
150+
### `when` -> `if`
151+
152+
Starting from 9.0, [`when` clauses](/docs/manual/latest/pattern-matching-destructuring#when-clause) within a `switch` statement will automatically convert to the `if` keyword instead.
153+
154+
```res
155+
switch person1 {
156+
| Student({reportCard: {gpa}}) if gpa < 0.5 =>
157+
Js.log("What's happening")
158+
| _ => () // do nothing
159+
}
160+
```
161+
162+
The syntax parser and printer will automatically take care of older code, so no action required for existing codebases. The `when` keyword is deprecated.
163+
109164

110-
We introduced two minor tweaks for the concrete syntax.
165+
### Cleaner Polyvariant Syntax
111166

112-
- It is now recommended to use `if` instead of `when` in pattern match guards
167+
Polyvariants with invalid identifier names (e.g. names including hypens `-`), don't require any special escaping syntax anymore:
168+
169+
<CodeTab labels={["New (9.0)", "Old (8.4)"]}>
170+
171+
```res
172+
type animation = [ #"ease-in" | #"ease-out" ]
173+
```
113174

114175
```res
115-
switch expr {
116-
| pat if predicate => // was: pat when predicate
176+
type animation = [ #\"ease-in" | #\"ease-out" ]
177+
```
178+
179+
</CodeTab>
180+
181+
We introduced this change to allow easier interop with existing JS string enums. In pure ReScript code, we'd still recommend our users to stick with valid identifier names instead (e.g. `easeIn` instead of `ease-in`).
182+
183+
184+
## Breaking Changes
185+
186+
This release comes with a minor breaking change that shouldn't have much impact on the upgrade of existing codebases.
187+
188+
### Nested Records within Objects
189+
190+
Previously, if you wrote `{"user": {age: 10}}`, the inner record was interpreted as an object instead of a record (`{"user": {"age": 10}}`); this is a byproduct of some internal interop transformation details; with the ReScript syntax, this went from understandable to confusing, so we're changing it so that the inner record is indeed now treated as a record. This is an obvious fix, but a breaking change if you were accidentally leveraging that nested record as object.
191+
192+
Here is a code example before and after the change. Note how the `user` record secretly turns into a ReScipt object in the previous version:
193+
194+
<CodeTab labels={["9.0 Example", "8.4 Example" ]}>
195+
196+
```res
197+
type user = {
198+
age: int
117199
}
200+
201+
let data = {
202+
"user": {
203+
age: 1
204+
}
205+
}
206+
207+
// This is the way: `age` should be usable via record accessor
208+
let age = data["user"].age
118209
```
119210

120-
`when` is still supported but it will be deprecated in the future.
211+
```res
212+
type user = {
213+
age: int
214+
}
215+
216+
let data = {
217+
"user": {
218+
age: 1
219+
}
220+
}
221+
222+
// This was the problem: The record implicitly turned
223+
// into a ReScript object (which is confusing)
224+
let age = data["user"]["age"]
225+
```
226+
227+
</CodeTab>
228+
229+
More discussions on this change can be found [here](https://forum.rescript-lang.org/t/fixing-the-semantics-of-nested-objects-breaking-changes/976).
230+
121231

122-
- explicit nested object literal syntax
232+
## Closing Note
123233

234+
We only highlighted a few user-facing features, but there are also some pretty interesting internal changes happening right now.
124235

125-
`{ "keya": { keyb: 3 } }` used to be interpreted as `{ "keya": { "keyb": 3 } } `, such implicity makes
126-
us not able to embed regular records inside JS object literals. With this release,
127-
JS object literals have to be written explicitly and `{ "keya": { keyb: 3 } }` will be interpreted as a regular
128-
record inside a JS object literal. More discussions can be found [here](https://forum.rescript-lang.org/t/fixing-the-semantics-of-nested-objects-breaking-changes/976).
236+
For example, we are tinkering with the idea on using WASM to replace Camlp4, and we are also working on a generalized visitor pattern that doesn't require objects.
129237

238+
We will discuss these topics in a separate development post, but we are already excited about the new possibilities this will bring within the compiler toolchain.
130239

131-
There are also some pretty interesting internal changes in this release.
132-
For example, using WASM to replace Camlp4 and a generalized visitor pattern without using objects, which we will discuss in a separate post.
133240

134-
Happy Hacking! -- Hongbo Zhang
241+
Happy Hacking!

0 commit comments

Comments
 (0)