-
-
Notifications
You must be signed in to change notification settings - Fork 251
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
Prepare 10.1 release #633
Changes from 17 commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
515e72c
Add async/await as keywords in HLJS and codemirror
ryyppy 65115c3
Add rough async/await doc
ryyppy b734d6a
Small markdown tweaks
ryyppy 57fdf40
Update async/await
ryyppy 1235548
Update promise and async await docs
ryyppy c30ee0a
Improve formatting for Since 10.1
ryyppy 94f9354
Add async/await to syntax widget
ryyppy 44cfb73
Add async/await to cheatsheet
ryyppy c48f114
Add blogpost
ryyppy 4ceec07
Rename blogpost date
ryyppy 917c905
Small text improvement
ryyppy dc70b89
Remove temporary Demo.mjs import
ryyppy 8eef734
Improve promise docs note
ryyppy aa9d556
Expand sentence in promise docs
ryyppy 6b991f0
Improve Promise.all example
ryyppy 3635caa
Add "the"
ryyppy 48fd6ac
Update roadmap page
ryyppy 93fad26
Update npmjs link
ryyppy 9fbd49e
Apply suggestions from code review
ryyppy c64d1ef
Apply suggestions from code review
ryyppy 7427d76
Apply suggestions from code review (zth)
ryyppy ab1681f
Improve roadmap
ryyppy eae4e5b
Address individual feedback
ryyppy d1255ac
Fix async/await example
ryyppy 3b697a7
More polish
ryyppy e384ba9
Decorator -> Attribute
ryyppy File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. | ||
ryyppy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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. | ||
ryyppy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## 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. | ||
ryyppy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
(*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: | ||
ryyppy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
- **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`. | ||
ryyppy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- **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) | ||
ryyppy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
ryyppy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 = { | ||
ryyppy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// 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} /> | ||
} | ||
} | ||
|
||
ryyppy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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. | ||
|
||
ryyppy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
## 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: | ||
ryyppy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
- 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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,6 +27,7 @@ | |
"exception", | ||
"lazy-values", | ||
"promise", | ||
"async-await", | ||
"module", | ||
"import-export", | ||
"attribute", | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.