Skip to content

Prepare 10.1 release #633

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Feb 2, 2023
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
302 changes: 302 additions & 0 deletions _blogposts/2023-02-02-release-10-1.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
---
author: rescript-team
date: "2023-02-02"
title: ReScript 10.1
badge: release
description: |
Async/await & better Promise support, JSX v4, and more!
---

## Introduction

We are happy to announce ReScript 10.1!

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.

Use `npm` to install the newest [10.1 release](https://www.npmjs.com/package/bs-platform/v/10.1.1):

```
npm install rescript

# or

npm install [email protected]
```

This version comes with two major language improvements we've all been waiting for. **async/await support** for an easy way asynchronous code in a synchronous manner, and a **new JSX transform** with better ergonomics, code generation and React 18 support.

Alongside the major changes, there have been many bugfixes and other improvements that won't be covered in this post.

Feel free check the [Changelog](https://github.com/rescript-lang/rescript-compiler/blob/master/CHANGELOG.md#1011) for all the details.

## New `async` / `await` syntax

Async / await has arrived. Similar to its JS counterparts, you are now able to define `async` functions and use the `await` operator to unwrap a promise value. This allows writing asynchronous code in a synchronous fashion.

**Example:**

```res
// Some fictive functionality that offers asynchronous network actions
@val external fetchUserMail: string => promise<string> = "GlobalAPI.fetchUserMail"
@val external sendAnalytics: string => promise<unit> = "GlobalAPI.sendAnalytics"

// We use the `async` keyword to allow the use of `await` in the function body
let logUserDetails = async (userId: string) => {
// We use `await` to fetch the user email from our fictive user endpoint
let email = await fetchUserMail(userId)

await sendAnalytics(`User details have been logged for ${userId}`)

Js.log(`Email address for user ${userId}: ${email}`)
}
```

To learn more about our async / await feature, check out the relevant [manual section](/docs/manual/latest/async-await).

## New `promise` builtin type and `Js.Promise2` module

In previous versions of ReScript, promises were expressed as a `Js.Promise.t<'a>` type, which was a little tedious to type. From now on, users may use the `promise<'a>` type instead.

Quick example of a `.resi` file using the new `promise` type:

```resi
// User.resi
type user

let fetchUser: string => promise<user>
```

Way easier on the eyes, don't you think? Note that the new `promise` type is fully compatible with `Js.Promise.t` (no breaking changes).

Additionally, we also introduced the `Js.Promise2` module as a stepping stone to migrate `Js.Promise` based code to a first-pipe (->) friendly solution.

(*Sidenote*: We know that our users want a solution to unify `Belt`, `Js` and `Js.xxx2` and have a fully featured "standard library". Good news is that we have a solution in the pipeline to fix this. `Js.Promise2` was introduced to ease the process later on and is not supposed to be the panacea of promise handling.)

If you are already using a third-party promise library like [ryyppy/rescript-promise](https://github.com/ryyppy/rescript-promise) or similar, there's no need to migrate, but we highly encourage you to mix and match it with `async` / `await`.


## New JSX v4 syntax

ReScript 10.1 now ships with JSX v4. Here's what's new:

- **Cleaner interop.** Due to recent improvements in the type checker, the `@react.component` transformation doesn't require any `makeProps` convention anymore. `make` functions will now be transformed into a `prop` type and a component function. That's it.
- **Two new transformation modes**. JSX v4 will come with a `classic` mode (= `React.createElement`) and `automatic` mode (= `jsx-runtime` calls). The latter will be the new default, moving forward with `rescript/[email protected]` and `React@18`.
- **Allow mixing JSX configurations on the project and module level.** Gradually mix and match JSX transformations and modes without migrating any old code!
- **Pass `prop` types** to `@react.component`. You can now fine tune `@react.component` with your specific prop type needs. Very useful for libraries and frameworks to define component interfaces.
- **Revisited props spread operator.** This will allow users to spread records in JSX without sacrificing their sanity. Note that this implementation has harder constraints than it's JS counterpart. (requires `rescript/[email protected]` or higher)

Code tells more than words, so here's a non-exhaustive code example to highlight the different JSX features. Make sure to also check out the JS output and play around with the code in our newest playground!

<CodeTab labels={["ReScript", "JS Output"]}>

```res
// Set the jsx configuration per module
@@jsxConfig({version: 4, mode: "automatic"})

module AutomaticModeExample = {
// "automatic" mode will compile jsx to the React 18 compatible
// jsx-runtime calls
@@jsxConfig({version: 4, mode: "automatic"})

@react.component
let make = (~name) => {
<div> {React.string(`Hello ${name}`)} </div>
}
}

module ClassicModeExample = {
// "classic" mode will compile jsx to React.createElement calls
@@jsxConfig({version: 4, mode: "classic"})

@react.component
let make = (~name) => {
<div> {React.string(`Hello ${name}`)} </div>
}
}

module NoDecoratorExample = {
// No need for `makeProps` anymore
type props = {name: string}

let make = (props: props) => {
<div> {React.string(`Hello ${props.name}`)} </div>
}
}

module ReactInterfaceExample: {
@react.component
let make: (~name: string, ~age: int=?) => React.element
} = {
@react.component
let make = (~name, ~age=0) => {
<div>
{React.string(
`Hello ${name}, you are ${Belt.Int.toString(age)} years old.`,
)}
</div>
}
}

module PropTypeInjectionExample = {
// Let's assume we have a prop type that we wanna enforce
// as our labeled arguments
type someoneElsesProps = {isHuman: bool}

// Here we tell the `react.component` decorator what props to infer.
// Useful for e.g. NextJS usage, or to create components that should
// comply to certain library component interfaces
@react.component(: someoneElsesProps)
let make = (~isHuman) => {
let msg = switch isHuman {
| true => "hello human"
| false => "hello fellow computer"
}
<div> {React.string(msg)} </div>
}
}

module PropSpreadExample = {
// Note: This will require @rescript/react 0.11 or later
@@jsxConfig({version: 4, mode: "automatic"})

@react.component
let make = () => {
let props = {NoDecoratorExample.name: "World"}

<NoDecoratorExample {...props} />
}
}

let root =
<div>
<AutomaticModeExample name="Automatic" />
<ClassicModeExample name="Classic" />
<NoDecoratorExample name="NoDecorator" />
<ReactInterfaceExample name="Interface" />
<PropTypeInjectionExample isHuman=true />
<PropSpreadExample />
</div>
```

```js
import * as React from "react";
import * as JsxRuntime from "react/jsx-runtime";

function Playground$AutomaticModeExample(props) {
return JsxRuntime.jsx("div", {
children: "Hello " + props.name + ""
});
}

var AutomaticModeExample = {
make: Playground$AutomaticModeExample
};

function Playground$ClassicModeExample(props) {
return React.createElement("div", undefined, "Hello " + props.name + "");
}

var ClassicModeExample = {
make: Playground$ClassicModeExample
};

function make(props) {
return JsxRuntime.jsx("div", {
children: "Hello " + props.name + ""
});
}

var NoDecoratorExample = {
make: make
};

function Playground$ReactInterfaceExample(props) {
var age = props.age;
var age$1 = age !== undefined ? age : 0;
return JsxRuntime.jsx("div", {
children: "Hello " + props.name + ", you are " + String(age$1) + " years old."
});
}

var ReactInterfaceExample = {
make: Playground$ReactInterfaceExample
};

function Playground$PropTypeInjectionExample(props) {
var msg = props.isHuman ? "hello human" : "hello fellow computer";
return JsxRuntime.jsx("div", {
children: msg
});
}

var PropTypeInjectionExample = {
make: Playground$PropTypeInjectionExample
};

function Playground$PropSpreadExample(props) {
return JsxRuntime.jsx(make, {
name: "World"
});
}

var PropSpreadExample = {
make: Playground$PropSpreadExample
};

var root = JsxRuntime.jsxs("div", {
children: [
JsxRuntime.jsx(Playground$AutomaticModeExample, {
name: "Automatic"
}),
JsxRuntime.jsx(Playground$ClassicModeExample, {
name: "Classic"
}),
JsxRuntime.jsx(make, {
name: "NoDecorator"
}),
JsxRuntime.jsx(Playground$ReactInterfaceExample, {
name: "Interface"
}),
JsxRuntime.jsx(Playground$PropTypeInjectionExample, {
isHuman: true
}),
JsxRuntime.jsx(Playground$PropSpreadExample, {})
]
});

export {
AutomaticModeExample ,
ClassicModeExample ,
NoDecoratorExample ,
ReactInterfaceExample ,
PropTypeInjectionExample ,
PropSpreadExample ,
root ,
}
```
</CodeTab>

### How to migrate to JSX v4?

We provide a full [migration guide](/docs/react/latest/migrate-react) with all the details of an migration.

Make sure to also check out the [rescript-react changelog](https://github.com/rescript-lang/rescript-react/blob/master/CHANGELOG.md) as well.

## What's next?

Our contributors are already one step ahead and work on major improvements in our next upcoming major release (v11). Things that are currently being explored:

- Make uncurried functions the default. This will be a huge change in terms of how we do interop and will open completely new ways to interact with existing codebases. It will also allow us to improve tooling in ways that wouldn't have been possible in a curried language.
- Explorations for a community "standard library" that goes beyond `Belt` and `Js.*`. This will also involve disabling / removing global "Stdlib" modules that shouldn't be used (e.g. `Array`, `List`, etc).
- New tooling to generate markdown from docstrings (module, type and value level). This will be super simple, but very effective.
- Explorations for a [localized documentation page](https://forum.rescript-lang.org/t/translation-project-rescript-lang-org/4022) (currently in a slowed-down exploration phase, but we will be getting there)

Check out the [v11](https://github.com/rescript-lang/rescript-compiler/issues?q=is%3Aopen+is%3Aissue+milestone%3Av11.0) milestone on our `rescript-lang` repo for more details on future improvements.

## That's it

We hope you enjoy the newest improvements as much as we do.

As always, in case there's any issues / problems, make sure to report bugs to [rescript-lang/rescript-compiler](https://github.com/rescript-lang/rescript-compiler) (language / syntax / jsx), [rescript-lang/rescript-react](https://github.com/rescript-lang/rescript-react) (React 16 / 18 binding) or [rescript-association/rescript-lang.org](https://github.com/rescript-association/rescript-lang.org) (documentation) repositories.

Also feel free to visit the [ReScript forum](https://forum.rescript-lang.org/) to ask questions and connect with other ReScripters.
1 change: 1 addition & 0 deletions data/sidebar_manual_latest.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"exception",
"lazy-values",
"promise",
"async-await",
"module",
"import-export",
"attribute",
Expand Down
45 changes: 45 additions & 0 deletions misc_docs/syntax/language_async.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
id: "async"
keywords: ["async"]
name: "async"
summary: "This is the `async` keyword."
category: "languageconstructs"
---

**Since 10.1**

Use the `async` keyword to make a function asynchronous. An async function may use the [await](#await) keyword to unwrap a promise value in a seamingly synchronous manner.

### Example

<CodeTab labels={["ReScript", "JS Output"]}>

```res
// Some fictive functionality that offers asynchronous network actions
@val external fetchUserMail: string => promise<string> = "GlobalAPI.fetchUserMail"
@val external sendAnalytics: string => promise<unit> = "GlobalAPI.sendAnalytics"

// We use the `async` keyword to allow the use of `await` in the function body
let logUserDetails = async (userId: string) => {
// We use `await` to fetch the user email from our fictive user endpoint
let email = await fetchUserMail(userId)

await sendAnalytics(`User details have been logged for ${userId}`)

Js.log(`Email address for user ${userId}: ${email}`)
}
```

```js
async function logUserDetails(userId) {
var email = await GlobalAPI.fetchUserMail(userId);
await GlobalAPI.sendAnalytics("User details have been logged for " + userId + "");
console.log("Email address for user " + userId + ": " + email + "");
}
```

</CodeTab>

### References

- [Async / Await](/docs/manual/latest/async-await)
41 changes: 41 additions & 0 deletions misc_docs/syntax/language_async_await.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
id: "uncurried-function"
keywords: ["async", "await"]
name: "async/await"
summary: "This is an `uncurried function`."
category: "languageconstructs"
---

ReScript functions are curried by default, however in certain cases you may need an uncurried function. In these cases we add a `.` in the argument list.

When calling uncurried functions, we must also include a `.` in the argument list.

### Example

<CodeTab labels={["ReScript", "JS Output"]}>

```res
let add = (. x, y) => {
x + y
}

let withCallback = (cb: (. int) => unit) => {
cb(. 1)
}
```

```js
function add(x, y) {
return (x + y) | 0;
}

function withCallback(cb) {
return cb(1);
}
```

</CodeTab>

### References

- [Uncurried Function](/docs/manual/latest/function#uncurried-function)
Loading