Skip to content

Commit dcad394

Browse files
authored
Merge pull request #222 from reason-association/release_9.0
release 9.0
2 parents 258a8c1 + 4c726e8 commit dcad394

11 files changed

+381
-52
lines changed

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

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
---
2+
author: hongbo
3+
date: "2021-02-09"
4+
previewImg: https://res.cloudinary.com/dmm9n7v9f/image/upload/v1612974395/Reason%20Association/rescript-lang.org/compiler_release_9_0_szd11o.jpg
5+
category: compiler
6+
title: ReScript 9.0
7+
badge: release
8+
description: |
9+
Featuring a new external stdlib configuration, some syntax improvements and a small breaking change for nested records.
10+
---
11+
12+
## Introduction
13+
14+
We are happy to announce ReScript 9.0!
15+
16+
ReScript is a robustly typed language that compiles to efficient and human-readable JavaScript. It comes with one of the fastest build toolchains and offers first class support for interoperating with ReactJS and other existing JavaScript code.
17+
18+
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:
19+
20+
```
21+
npm install [email protected] --save-dev
22+
```
23+
24+
You can also try our new release in the [Online Playground](/try).
25+
26+
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).
27+
28+
## Compiler Improvements
29+
30+
### New External Stdlib Configuration
31+
32+
This is a long-awaited [feature request](https://github.com/rescript-lang/rescript-compiler/pull/2171).
33+
34+
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.
35+
36+
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.
37+
38+
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.
39+
40+
We also introduced a new configuration within our `bsconfig.json` file to tell the compiler to use our pre-compiled package instead:
41+
42+
```json
43+
{
44+
/* ... */
45+
"external-stdlib" : "@rescript/std"
46+
}
47+
```
48+
49+
With this configuration set, compiled JS code will now point to the defined `external-stdlib` path:
50+
51+
<CodeTab labels={["ReScript", "JavaScript"]}>
52+
53+
```res
54+
Belt.Array.forEach([1, 2, 3], (num) => Js.log(num))
55+
```
56+
57+
```js
58+
// Note the import path starting with "@rescript/std"
59+
import * as Array from "@rescript/std/lib/es6/belt_Array.mjs";
60+
61+
Belt_Array.forEach([
62+
1,
63+
2,
64+
3
65+
], (function (num) {
66+
console.log(num);
67+
68+
}));
69+
```
70+
71+
</CodeTab>
72+
73+
The JavaScript output above was compiled with an `es6` target, but will also work with `commonjs`.
74+
75+
**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!
76+
77+
To prevent unnecessary complications, only use this feature when...
78+
- You want to ship a library for JS / TS consumers without making them depend on `bs-platform`
79+
- You can't depend on `bs-platform` due to toolchain size (docker containers, low-storage deployment devices, etc)
80+
81+
### Less Bundle Bloat when Adding ReScript
82+
83+
With each release we keep a close eye on generating code that is optimized for tree-shaking. We also believe that we reached a milestone where ReScript reliably produces output that has almost no impact on our final JS bundle-sizes (this is what we call our "zero-cost" philosophy).
84+
85+
The bundled code is almost ReScript runtime free because our generated library code fits the tree-shaking principle really well. Tools like `esbuild` can easily drop unnecessary code and make sure that the final code stays lean.
86+
87+
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!
88+
89+
### Improved Code Generation for Pattern Matching
90+
91+
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):
92+
93+
```res
94+
type test =
95+
| NoArg
96+
| AnotherNoArg
97+
| OtherArg(int)
98+
99+
let test = x =>
100+
switch x {
101+
| NoArg => true
102+
| _ => false
103+
}
104+
```
105+
106+
The snippet above will compile to the following JS output:
107+
108+
<CodeTab labels={["9.0 Output", "8.4 Output" ]}>
109+
110+
```js
111+
function test(x){
112+
return x === 0
113+
}
114+
```
115+
116+
```js
117+
function test(x) {
118+
if (typeof x === "number") {
119+
return x === 0;
120+
} else {
121+
return false;
122+
}
123+
}
124+
```
125+
126+
127+
</CodeTab>
128+
129+
As you can see, the 9.0 compiler removes all the unnecessary `typeof` checks!
130+
131+
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).
132+
133+
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.
134+
135+
For those interested in the details, here is a representative diff resulting from this cleanup:
136+
137+
```diff
138+
function is_space(param){
139+
- var switcher = param - 9 | 0;
140+
- if (switcher > 4 || switcher < 0) {
141+
- return switcher == 23 ;
142+
+ if (param > 13 || param < 9) {
143+
+ return param === 32;
144+
} else {
145+
- return switcher !== 2;
146+
+ return param != 11;
147+
}
148+
}
149+
```
150+
151+
## Syntax Improvements
152+
153+
### `when` -> `if`
154+
155+
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.
156+
157+
<CodeTab labels={["New (9.0)", "Old (8.4)"]}>
158+
159+
```res
160+
switch person1 {
161+
| Student({reportCard: {gpa}}) if gpa < 0.5 =>
162+
Js.log("What's happening")
163+
| _ => () // do nothing
164+
}
165+
```
166+
167+
```res
168+
switch person1 {
169+
| Student({reportCard: {gpa}}) when gpa < 0.5 =>
170+
Js.log("What's happening")
171+
| _ => () // do nothing
172+
}
173+
```
174+
175+
</CodeTab>
176+
177+
The `when` keyword is deprecated. The syntax will continue supporting it and the formatter will automatically convert to `if`, for a pain-free upgrade.
178+
179+
### Cleaner Polyvariant Syntax
180+
181+
Polyvariants with invalid identifier names (e.g. names including hypens `-`), don't require any special escaping syntax anymore:
182+
183+
<CodeTab labels={["New (9.0)", "Old (8.4)"]}>
184+
185+
```res
186+
type animation = [ #"ease-in" | #"ease-out" ]
187+
```
188+
189+
```res
190+
type animation = [ #\"ease-in" | #\"ease-out" ]
191+
```
192+
193+
</CodeTab>
194+
195+
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`).
196+
197+
198+
## Breaking Changes
199+
200+
This release comes with a minor breaking change that shouldn't have much impact on the upgrade of existing codebases.
201+
202+
### Nested Records within Objects
203+
204+
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.
205+
206+
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:
207+
208+
<CodeTab labels={["9.0 Example", "8.4 Example" ]}>
209+
210+
```res
211+
type user = {
212+
age: int
213+
}
214+
215+
let data = {
216+
"user": {
217+
age: 1
218+
}
219+
}
220+
221+
// This is the way: `age` should be usable via record accessor
222+
let age = data["user"].age
223+
```
224+
225+
```res
226+
type user = {
227+
age: int
228+
}
229+
230+
let data = {
231+
"user": {
232+
age: 1
233+
}
234+
}
235+
236+
// This was the problem: The record implicitly turned
237+
// into a ReScript object (which is confusing)
238+
let age = data["user"]["age"]
239+
```
240+
241+
</CodeTab>
242+
243+
More discussions on this change can be found [here](https://forum.rescript-lang.org/t/fixing-the-semantics-of-nested-objects-breaking-changes/976).
244+
245+
## Closing Note
246+
247+
We only highlighted a few user-facing features, but there are also some pretty interesting internal changes happening right now.
248+
249+
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.
250+
251+
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.
252+
253+
254+
Happy Hacking!

data/blog_posts.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"release-9-0" : "2021-02-09-release-9-0.mdx",
23
"release-8-3-pt2": "2020-09-25-release-8-3-2.mdx",
34
"release-8-3-pt1": "2020-09-25-release-8-3.mdx",
45
"new-rescript-logo": "2020-08-28-new-rescript-logo.mdx",

src/Blog.js

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Blog.res

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
module Link = Next.Link
1717

1818
let _rescriptDefaultImg = "https://res.cloudinary.com/dmm9n7v9f/image/upload/v1598616442/reason%20association/rescript-lang.org/art-3-rescript-launch_ovoibg.jpg"
19-
let planetPreviewImg = "https://res.cloudinary.com/dmm9n7v9f/image/upload/v1587479463/Reason%20Association/reasonml.org/reasonml_art2_1280_vhzxnz.png"
19+
let _planetPreviewImg = "https://res.cloudinary.com/dmm9n7v9f/image/upload/v1587479463/Reason%20Association/reasonml.org/reasonml_art2_1280_vhzxnz.png"
20+
21+
let defaultPreviewImg = "https://res.cloudinary.com/dmm9n7v9f/image/upload/v1598616442/Reason%20Association/rescript-lang.org/Art-3-rescript-launch_ovoibg.jpg"
2022

2123
// For encoding reasons, see https://shripadk.github.io/react/docs/jsx-gotchas.html
2224
let middleDotSpacer = " " ++ (Js.String.fromCharCode(183) ++ " ")
@@ -109,7 +111,7 @@ module BlogCard = {
109111
let className = "absolute top-0 h-full w-full object-cover"
110112
switch previewImg {
111113
| Some(src) => <img className src />
112-
| None => <img className src=planetPreviewImg />
114+
| None => <img className src=defaultPreviewImg />
113115
}
114116
}
115117
</a>
@@ -169,7 +171,7 @@ module FeatureCard = {
169171
let className = "absolute top-0 h-full w-full object-cover"
170172
switch previewImg {
171173
| Some(src) => <img className src />
172-
| None => <img className src=planetPreviewImg />
174+
| None => <img className src=defaultPreviewImg />
173175
}
174176
}
175177
</a>

src/Blog.resi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module Malformed: {
1212
}
1313
}
1414

15-
let planetPreviewImg: string
15+
let defaultPreviewImg: string
1616

1717
type params = {slug: string}
1818
type props = {

src/BlogArticle.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/BlogArticle.res

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ let default = (props: props) => {
186186
title={title ++ " | ReScript Blog"}
187187
description=?{description->Js.Null.toOption}
188188
canonical=?{canonical->Js.Null.toOption}
189-
ogImage={previewImg->Js.Null.toOption->Belt.Option.getWithDefault(Blog.planetPreviewImg)}
189+
ogImage={previewImg->Js.Null.toOption->Belt.Option.getWithDefault(Blog.defaultPreviewImg)}
190190
/>
191191
<div className="mb-10 md:mb-20">
192192
<BlogHeader

0 commit comments

Comments
 (0)