From 515e72c1805a454c82741dc452d8fcb2f60c0c2b Mon Sep 17 00:00:00 2001 From: Patrick Ecker Date: Fri, 30 Dec 2022 23:23:08 +0100 Subject: [PATCH 01/26] Add async/await as keywords in HLJS and codemirror --- plugins/cm-rescript-mode.js | 2 +- plugins/rescript-highlightjs.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/cm-rescript-mode.js b/plugins/cm-rescript-mode.js index 406381aa8..008a0f57e 100644 --- a/plugins/cm-rescript-mode.js +++ b/plugins/cm-rescript-mode.js @@ -36,7 +36,7 @@ CodeMirror.defineSimpleMode("rescript", { token: ["keyword", "keyword2", null, "def"] }, { - regex: /(?:and|as|assert|catch|constraint|downto|else|exception|export|external|false|for|if|import|in|include|lazy|let|module|mutable|of|open|private|switch|to|true|try|type|when|while|with\!)\b/, + regex: /(?:and|as|assert|catch|async|await|constraint|downto|else|exception|export|external|false|for|if|import|in|include|lazy|let|module|mutable|of|open|private|switch|to|true|try|type|when|while|with\!)\b/, token: "keyword" }, { diff --git a/plugins/rescript-highlightjs.js b/plugins/rescript-highlightjs.js index 7c37a4749..d1efcf34d 100644 --- a/plugins/rescript-highlightjs.js +++ b/plugins/rescript-highlightjs.js @@ -29,7 +29,7 @@ module.exports = function(hljs) { keyword: 'and as assert catch constraint downto else exception export external false for ' + 'if import in include lazy let module mutable of open private rec switch ' + - 'to true try type when while with', + 'to true try type when while with async await', // not reliable //built_in: //'array bool bytes char exn|5 float int int32 int64 list lazy_t|5 nativeint|5 ref string unit', From 65115c38fbaf868a973692e13ef3f6749755e426 Mon Sep 17 00:00:00 2001 From: Patrick Ecker Date: Fri, 30 Dec 2022 23:23:48 +0100 Subject: [PATCH 02/26] Add rough async/await doc --- data/sidebar_manual_latest.json | 1 + pages/docs/manual/latest/async-await.mdx | 210 +++++++++++++++++++++++ pages/docs/manual/latest/promise.mdx | 4 +- 3 files changed, 213 insertions(+), 2 deletions(-) create mode 100644 pages/docs/manual/latest/async-await.mdx diff --git a/data/sidebar_manual_latest.json b/data/sidebar_manual_latest.json index b75c6034c..fcd726d21 100644 --- a/data/sidebar_manual_latest.json +++ b/data/sidebar_manual_latest.json @@ -27,6 +27,7 @@ "exception", "lazy-values", "promise", + "async-await", "module", "import-export", "attribute", diff --git a/pages/docs/manual/latest/async-await.mdx b/pages/docs/manual/latest/async-await.mdx new file mode 100644 index 000000000..dc3bd8486 --- /dev/null +++ b/pages/docs/manual/latest/async-await.mdx @@ -0,0 +1,210 @@ +--- +title: "Async / Await" +description: "Async / await for asynchronous operations" +canonical: "/docs/manual/latest/async-await" +--- + + + +# Async / Await + +***Since 10.1*** + +Use the `async` / `await` keywords to make asynchronous, `Promise` based code easier to read and write. If you are already familiar with JS' `async` / `await`, you will most likely be able to use the syntax right away as is. + +**Some basics:** +- You may only use `await` in `async` function bodies +- `await` may only be called on a `promise` value +- `await` calls are expressions (pattern matching!) +- A function returning a `promise<'a>` is equivalent to an `async` function returning a value `'a` (important for writing signature files and bindings) +- `promise` values and types returned from an `async` function don't auto-collapse + +## How it looks + +Let's start with a quick example to show-case the syntax: + + + +```res +// Some fictive functionality that offers asynchronous network actions +@val external fetchUserMail: string => promise = "GlobalAPI.fetchUserMail" +@val external sendAnalytics: string => promise = "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 + ""); +} +``` + + + +As we can see above, an `async` function is defined via the `async` keyword right before the function's parameter list. In the function body, we are now able to use the `await` keyword to explicitly wait for a `Promise` value and assign its content to a let binding `email`. + +Everything we've just saw was essentially what we are used to `async` / `await` in JS, but there's still a few details that are specific to ReScript. The next few sections will go through all the details that are specific to the ReScript type system. + +## Types and `async` functions + +### No `promise` type in inline return types + +When typing the return type of an `async` function inline, we completely omit the `promise<...>` type and just state the actual type we want to return. As an example, we would type a `logUserDetails` function like this: + + +```res +// Instead of promise we return `unit` instead. +// The boxing into a promise is already done implicitly +// by the compiler. +let logUserDetails = async (userId: string): unit => { + Js.log("...") +} +``` + +**Note:** This was a deliberate design decision. More details on the rationale can be found [here](https://github.com/rescript-lang/rescript-compiler/pull/5913#issuecomment-1359003870). + +### Promises don't auto-collapse in async functions + +As a JS developer you'd expect a `promise<'a>` to collapse into another `promise<'a>` when returned in an `async` function. This is not the case in ReScript. Use the `await` function to unwrap any nested promises instead. + +```res +let fetchData = async (userId: string): string => { + // We can't just return the result of `fetchUserMail`, otherwise we'd get a + // type error due to our function return type of type `string` + await fetchUserMail(userId) +} +``` + +### `async` function type signatures + +Function type signatures (i.e defined in signature files) don't differentiate between `async` and conventional functions. Every function with a `promise` return type are `async` functions; hence we use the `promise` return type. + +```resi +// Demo.resi + +let fetchUserMail: string => promise +``` + +The same logic applies to type definitions in `.res` files: + +```res +// function type +type someAsyncFn = int => promise + +// Function type annotation +let fetchData: string => promise = async (userId) => { + await fetchUserMail(userId) +} +``` + +For completeness reasons, let's also show-case the difference between type definitions and inline type definitions: + +```res +// Note how the inline return type uses `string`, while the type definition uses `promise` +let fetchData: string => promise = async (userId: string): string { + await fetchuserMail(userId) +} +``` + +(The last example was only mentioned for education purposes. Don't do that in your actual code.) + +## Common usage examples + +### Error handling + +As with any synchronous code, you may use `try / catch` or `switch` to pattern match on errors. + +```res example +let logUserDetails = async (userId: string): result => { + let email = await fetchUserMail(userId) + + // await can be used within a `try` body + try { + Js.log(`Email address for user ${userId}: ${email}`) + await sendAnalytics(`User details have been logged for ${userId}`) + Ok() + } catch { + // In case some generic JS exception has been thrown due to unknown interop reasons + | JsError(_) => Error("Could not send analytics") + } +} +``` + +### Piping `await` calls + +It is possible + + + +```res example +@val external fetchUserMail: string => promise = "GlobalAPI.fetchUserMail" + +let fetchData = async () => { + let mail = {await fetchUserMail("1234")}->Js.String2.toUpperCase + Js.log(`All upper-cased mail: ${mail}`) +} +``` + +```js +async function fetchData(param) { + var mail = (await GlobalAPI.fetchUserMail("1234")).toUpperCase(); + console.log("All upper-cased mail: " + mail + ""); +} +``` + + + +### Pattern matching on `await` calls + +Of course we can also go fancy with all kinds of pattern matching combinations. + + + +```res example +@val external fetchUserMail: string => promise = "GlobalAPI.fetchUserMail" + +let fetchData = async () => { + switch (await fetchUserMail("user1"), await fetchUserMail("user2")) { + | (user1Mail, user2Mail) => { + Js.log("user 1 mail: " ++ user1Mail) + Js.log("user 2 mail: " ++ user2Mail) + } + + | exception JsError(err) => Js.log2("Some error occurred", err) + } +} +``` + +```js +async function fetchData(param) { + var val; + var val$1; + try { + val = await GlobalAPI.fetchUserMail("user1"); + val$1 = await GlobalAPI.fetchUserMail("user2"); + } + catch (raw_err){ + var err = Caml_js_exceptions.internalToOCamlException(raw_err); + if (err.RE_EXN_ID === "JsError") { + console.log("Some error occurred", err._1); + return ; + } + throw err; + } + console.log("user 1 mail: " + val); + console.log("user 2 mail: " + val$1); +} +``` + + + diff --git a/pages/docs/manual/latest/promise.mdx b/pages/docs/manual/latest/promise.mdx index 619e4cfc4..43e2ad5a2 100644 --- a/pages/docs/manual/latest/promise.mdx +++ b/pages/docs/manual/latest/promise.mdx @@ -1,10 +1,10 @@ --- -title: "Async & Promise" +title: "Promises" description: "JS Promise handling in ReScript" canonical: "/docs/manual/latest/promise" --- -# Async & Promise +# Promise Support for `async` and `await` is added in compiler version 10.1. The majority of existing code is based on promises. The new Promise API bindings make async code look better than with old promises. From b734d6ac3d70fbf1e8ed92bb0e43b56597e7ef94 Mon Sep 17 00:00:00 2001 From: Patrick Ecker Date: Fri, 30 Dec 2022 23:24:05 +0100 Subject: [PATCH 03/26] Small markdown tweaks --- src/components/Markdown.res | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Markdown.res b/src/components/Markdown.res index da20981c4..7adaf5cf0 100644 --- a/src/components/Markdown.res +++ b/src/components/Markdown.res @@ -150,7 +150,7 @@ module H2 = { module H3 = { @react.component let make = (~id, ~children) => -

+

children @@ -190,7 +190,7 @@ module InlineCode = { @react.component let make = (~children) => + className="md-inline-code px-2 py-0.5 text-gray-60 font-mono rounded-sm bg-gray-10-tr border border-gray-90 border-opacity-5"> children } From 57fdf408853a613d265ce24207455b3a439f2bd3 Mon Sep 17 00:00:00 2001 From: Patrick Ecker Date: Wed, 1 Feb 2023 16:10:02 +0100 Subject: [PATCH 04/26] Update async/await --- pages/docs/manual/latest/async-await.mdx | 242 +++++++++++++++++------ 1 file changed, 186 insertions(+), 56 deletions(-) diff --git a/pages/docs/manual/latest/async-await.mdx b/pages/docs/manual/latest/async-await.mdx index dc3bd8486..38ef4b208 100644 --- a/pages/docs/manual/latest/async-await.mdx +++ b/pages/docs/manual/latest/async-await.mdx @@ -4,25 +4,31 @@ description: "Async / await for asynchronous operations" canonical: "/docs/manual/latest/async-await" --- +import "src/Demo.mjs" + + +
+ +```res prelude +@val external fetchUserMail: string => promise = "GlobalAPI.fetchUserMail" +@val external sendAnalytics: string => promise = "GlobalAPI.sendAnalytics" +``` + +
+ # Async / Await ***Since 10.1*** -Use the `async` / `await` keywords to make asynchronous, `Promise` based code easier to read and write. If you are already familiar with JS' `async` / `await`, you will most likely be able to use the syntax right away as is. - -**Some basics:** -- You may only use `await` in `async` function bodies -- `await` may only be called on a `promise` value -- `await` calls are expressions (pattern matching!) -- A function returning a `promise<'a>` is equivalent to an `async` function returning a value `'a` (important for writing signature files and bindings) -- `promise` values and types returned from an `async` function don't auto-collapse +ReScript comes with `async` / `await` support to make asynchronous, `Promise` based code easier to read and write. This feature is very similar to its JS equivalent, so if you are already familiar with JS' `async` / `await`, you will feel right at home. ## How it looks Let's start with a quick example to show-case the syntax: + ```res @@ -53,41 +59,22 @@ async function logUserDetails(userId) { As we can see above, an `async` function is defined via the `async` keyword right before the function's parameter list. In the function body, we are now able to use the `await` keyword to explicitly wait for a `Promise` value and assign its content to a let binding `email`. -Everything we've just saw was essentially what we are used to `async` / `await` in JS, but there's still a few details that are specific to ReScript. The next few sections will go through all the details that are specific to the ReScript type system. +You will probably notice that this looks very similar to `async` / `await` in JS, but there's still a few details that are specific to ReScript. The next few sections will go through all the details that are specific to the ReScript type system. -## Types and `async` functions +## Basics -### No `promise` type in inline return types - -When typing the return type of an `async` function inline, we completely omit the `promise<...>` type and just state the actual type we want to return. As an example, we would type a `logUserDetails` function like this: - - -```res -// Instead of promise we return `unit` instead. -// The boxing into a promise is already done implicitly -// by the compiler. -let logUserDetails = async (userId: string): unit => { - Js.log("...") -} -``` - -**Note:** This was a deliberate design decision. More details on the rationale can be found [here](https://github.com/rescript-lang/rescript-compiler/pull/5913#issuecomment-1359003870). - -### Promises don't auto-collapse in async functions +- You may only use `await` in `async` function bodies +- `await` may only be called on a `promise` value +- `await` calls are expressions, therefore they can be used in pattern matching (`switch`) +- A function returning a `promise<'a>` is equivalent to an `async` function returning a value `'a` (important for writing signature files and bindings) +- `promise` values and types returned from an `async` function don't auto-collapse -As a JS developer you'd expect a `promise<'a>` to collapse into another `promise<'a>` when returned in an `async` function. This is not the case in ReScript. Use the `await` function to unwrap any nested promises instead. -```res -let fetchData = async (userId: string): string => { - // We can't just return the result of `fetchUserMail`, otherwise we'd get a - // type error due to our function return type of type `string` - await fetchUserMail(userId) -} -``` +## Types and `async` functions ### `async` function type signatures -Function type signatures (i.e defined in signature files) don't differentiate between `async` and conventional functions. Every function with a `promise` return type are `async` functions; hence we use the `promise` return type. +Function type signatures (i.e defined in signature files) don't require any special keywords for `async` usage. Whenever you want to type an `async` function, use a `promise` return type. ```resi // Demo.resi @@ -97,7 +84,7 @@ let fetchUserMail: string => promise The same logic applies to type definitions in `.res` files: -```res +```res example // function type type someAsyncFn = int => promise @@ -107,42 +94,99 @@ let fetchData: string => promise = async (userId) => { } ``` -For completeness reasons, let's also show-case the difference between type definitions and inline type definitions: +**BUT:** When typing `async` functions in your implementation files, you need to omit the `promise<'a>` type: + +```res +// This function is compiled into a `string => promise` type. +// The promise<...> part is implicitly added by the compiler. +let fetchData = async (userId: string): string => { + await fetchUserMail("test") +} +``` + +For completeness reasons, let's expand the full signature and inline type definitions in one code snippet: ```res // Note how the inline return type uses `string`, while the type definition uses `promise` let fetchData: string => promise = async (userId: string): string { - await fetchuserMail(userId) + await fetchUserMail(userId) } ``` -(The last example was only mentioned for education purposes. Don't do that in your actual code.) +**Note:** In a practical scenario you'd either use a type signature, or inline types, not both at the same time. In case you are interested in the design decisions, check out [this discussion](https://github.com/rescript-lang/rescript-compiler/pull/5913#issuecomment-1359003870). -## Common usage examples +### `async` uncurried functions -### Error handling +The `async` keyword does also work for uncurried functions. -As with any synchronous code, you may use `try / catch` or `switch` to pattern match on errors. +```res +let fetchData = async (. userId: string): string { + await fetchUserMail(userId) +} +``` -```res example -let logUserDetails = async (userId: string): result => { - let email = await fetchUserMail(userId) +### Promises don't auto-collapse in async functions + +In JS, nested promises (i.e. `promise>`) will automatically collapse into a flat promise (`promise<'a>`). This is not the case in ReScript. Use the `await` function to manually unwrap any nested promises within an `async` function instead. + +```res +let fetchData = async (userId: string): string => { + // We can't just return the result of `fetchUserMail`, otherwise we'd get a + // type error due to our function return type of type `string` + await fetchUserMail(userId) +} +``` - // await can be used within a `try` body +## Error handling + +You may use `try / catch` or `switch` to handle exceptions during async execution. + +```res +// For simulation purposes +let authenticate = async () => { + raise(Js.Exn.raiseRangeError("Authentication failed.")) +} + +let checkAuth = async () => { try { - Js.log(`Email address for user ${userId}: ${email}`) - await sendAnalytics(`User details have been logged for ${userId}`) - Ok() + await authenticate() } catch { - // In case some generic JS exception has been thrown due to unknown interop reasons - | JsError(_) => Error("Could not send analytics") + | Js.Exn.Error(e) => + switch Js.Exn.message(e) { + | Some(msg) => Js.log("JS error thrown: " ++ msg) + | None => Js.log("Some other exception has been thrown") + } } } ``` -### Piping `await` calls +Note how we are essentially catching JS errors the same way as described in our [Exception](exception#catch-rescript-exceptions-from-js) section. -It is possible +You may unify error and value handling in a single switch as well: + +```res +let authenticate = async () => { + raise(Js.Exn.raiseRangeError("Authentication failed.")) +} + +let checkAuth = async () => { + switch await authenticate() { + | _ => Js.log("ok") + | exception Js.Exn.Error(e) => + switch Js.Exn.message(e) { + | Some(msg) => Js.log("JS error thrown: " ++ msg) + | None => Js.log("Some other exception has been thrown") + } + } +} +``` + +**Important:** When using `await` with a `switch`, always make sure to put the actual await call in the `switch` expression, otherwise your `await` error will not be caught. + +## Piping `await` calls + +You may want to pipe the result of an `await` call right into another function. +This can be done by wrapping your `await` calls in a new `{}` closure. @@ -164,9 +208,11 @@ async function fetchData(param) { -### Pattern matching on `await` calls +Note how the original closure was removed in the final JS output. No extra allocations! -Of course we can also go fancy with all kinds of pattern matching combinations. +## Pattern matching on `await` calls + +`await` calls are just another kind of expression, so you can use `switch` pattern matching for more complex logic. @@ -208,3 +254,87 @@ async function fetchData(param) { +## `await` multiple promises + +We can utilize the `Js.Promise2` module to handle multiple promises. E.g. let's use `Js.Promise2.all` to wait for multiple promises before continuing the program: + +```res +let pauseReturn = async (value, timeout) => { + Js.Promise2.make((~resolve, ~reject) => { + Js.Global.setTimeout(() => { + resolve(. value) + }, timeout)->ignore + }) +} + +let logMultipleValues = async () => { + let value1 = await pauseReturn("value1", 2000) + let value2 = await pauseReturn("value2", 1200) + let value3 = await pauseReturn("value3", 500) + + let all = await Js.Promise2.all([value1, value2, value3]) + + switch all { + | [v1, v2, v3] => Js.log(`All values: ${v1}, ${v2}, ${v3}`) + | _ => Js.log("this should never happen") + } +} +``` + +## JS Interop with `async` functions + +`async` / `await` practically works with any function that returns a `promise<'a>` value. Map your `promise` returning function via an `external`, and use it in an `async` function as usual. + +Here's a full example of using the MDN `fetch` API, using `async` / `await` to simulate a login: + +```res +// A generic Response type for typing our fetch requests +module Response = { + type t<'data> + @send external json: t<'data> => Promise.t<'data> = "json" +} + +// A binding to our globally available `fetch` function. `fetch` is a +// standardized function to retrieve data from the network that is available in +// all modern browsers. +@val @scope("globalThis") +external fetch: ( + string, + 'params, +) => Promise.t, "error": Js.Nullable.t}>> = + "fetch" + +// We now use our asynchronous `fetch` function to simulate a login. +// Note how we use `await` with regular functions returning a `promise`. +let login = async (email: string, password: string) => { + let body = { + "email": email, + "password": password, + } + + let params = { + "method": "POST", + "headers": { + "Content-Type": "application/json", + }, + "body": Js.Json.stringifyAny(body), + } + + try { + let response = await fetch("https://reqres.in/api/login", params) + let data = await response->Response.json + + switch Js.Nullable.toOption(data["error"]) { + | Some(msg) => Error(msg) + | None => + switch Js.Nullable.toOption(data["token"]) { + | Some(token) => Ok(token) + | None => Error("Didn't return a token") + } + } + } catch { + | _ => Error("Unexpected network error occurred") + } +} +``` + From 1235548fc421c6bb1e540e52e827d356d4b23de8 Mon Sep 17 00:00:00 2001 From: Patrick Ecker Date: Wed, 1 Feb 2023 16:34:18 +0100 Subject: [PATCH 05/26] Update promise and async await docs --- pages/docs/manual/latest/async-await.mdx | 4 +- pages/docs/manual/latest/promise.mdx | 90 ++++++++++++++++++++---- 2 files changed, 80 insertions(+), 14 deletions(-) diff --git a/pages/docs/manual/latest/async-await.mdx b/pages/docs/manual/latest/async-await.mdx index 38ef4b208..cfb94180a 100644 --- a/pages/docs/manual/latest/async-await.mdx +++ b/pages/docs/manual/latest/async-await.mdx @@ -291,7 +291,7 @@ Here's a full example of using the MDN `fetch` API, using `async` / `await` to s // A generic Response type for typing our fetch requests module Response = { type t<'data> - @send external json: t<'data> => Promise.t<'data> = "json" + @send external json: t<'data> => promise<'data> = "json" } // A binding to our globally available `fetch` function. `fetch` is a @@ -301,7 +301,7 @@ module Response = { external fetch: ( string, 'params, -) => Promise.t, "error": Js.Nullable.t}>> = +) => promise, "error": Js.Nullable.t}>> = "fetch" // We now use our asynchronous `fetch` function to simulate a login. diff --git a/pages/docs/manual/latest/promise.mdx b/pages/docs/manual/latest/promise.mdx index 43e2ad5a2..c4ba35b2c 100644 --- a/pages/docs/manual/latest/promise.mdx +++ b/pages/docs/manual/latest/promise.mdx @@ -6,29 +6,95 @@ canonical: "/docs/manual/latest/promise" # Promise -Support for `async` and `await` is added in compiler version 10.1. The majority of existing code is based on promises. The new Promise API bindings make async code look better than with old promises. +> **Note:** Starting from ReScript 10.1 and above, we recommend using [async / await](./async-await) when interacting with Promises. -## Promise (new) +## `promise` type -Our up to date Promise bindings are currently not part of the the standard library. For now, please install them separately: +*Since 10.1* -```sh -npm install @ryyppy/rescript-promise +In ReScript, every JS promise is represented with the globally available `promise<'a>` type. For ReScript versions < 10.1, use its original alias `Js.Promise.t<'a>` instead. + +Here's a usage example in a function signature: + +```resi +// User.resi file + +type user = {name: string} + +let fetchUser: string => promise +``` + +To work with promise values (instead of using `async` / `await`) you may want to use the built in `Js.Promise2` module. + +## Js.Promise2 + +A builtin module to create, chain and manipulate promises. + +**Note:** This is an intermediate replacement for the `Js.Promise` module. It is designed to work with the `->` operator and should be used in favour of it's legacy counterpart. We are aware that the `Belt`, `Js` and `Js.xxx2` situation is confusing; a proper solution will hopefully be part of our upcoming `v11` release. + +### Creating a promise + +```res +let p1 = Js.Promise2.make((~resolve, ~reject) => { + // We use uncurried functions for resolve / reject + // for cleaner JS output without unintended curry calls + resolve(. "hello world") +}) + +let p2 = Js.Promise2.resolve("some value") + +// You can only reject `exn` values for streamlined catch handling +exception MyOwnError(string) +let p3 = Js.Promise2.reject(MyOwnError("some rejection")) +``` + +### Access the contents and transform a promise + +```res +let logAsyncMessage = () => { + open Js.Promise2 + Js.Promise2.resolve("hello world") + ->then(msg => { + // then callbacks require the result to be resolved explicitly + resolve("Message: " ++ msg) + }) + ->then(msg => { + Js.log(msg) + + // Even if there is no result, we need to use resolve() to return a promise + resolve() + }) + ->ignore // Requires ignoring due to unhandled return value +} ``` -In your `bsconfig.json`: +For comparison, the `async` / `await` version of the same code would look like this: -```json -{ - "bs-dependencies": ["@ryyppy/rescript-promise"] +```res +let logAsyncMessage = async () => { + let msg = await Js.Promise2.resolve("hello world") + Js.log(`Message: ${msg}`) } ``` -_Alternatively you may vendor the [`Promise.res` / `Promise.resi` files](https://github.com/ryyppy/rescript-promise/tree/master/src) files in your app codebase if you want to have more control._ +Way better. -You can find the APIs and full usage examples [here](https://github.com/ryyppy/rescript-promise#usage). +### Run multiple promises in parallel + +In case you want to launch multiple promises in parallel, use `Js.Promise2.all`: + +```res +@val +external fetchMessage: string => promise = "global.fetchMessage" + +let logAsyncMessage = async () => { + let messages = await Js.Promise2.all([fetchMessage("message1"), fetchMessage("message2")]) + + Js.log(Belt.Array.joinWith(messages, ", ")) +} +``` -## Promise (legacy) +## Js.Promise module (legacy - do not use) > **Note:** The `Js.Promise` bindings are following the outdated data-last convention from a few years ago. We kept those APIs for backwards compatibility, so for now please use [`rescript-promise`](https://github.com/ryyppy/rescript-promise) until we upstream the new bindings to our standard library. From c30ee0a1bc8fb243afe0bbf13de7e71a5b50a089 Mon Sep 17 00:00:00 2001 From: Patrick Ecker Date: Wed, 1 Feb 2023 16:45:52 +0100 Subject: [PATCH 06/26] Improve formatting for Since 10.1 --- pages/docs/manual/latest/async-await.mdx | 2 +- pages/docs/manual/latest/promise.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/docs/manual/latest/async-await.mdx b/pages/docs/manual/latest/async-await.mdx index cfb94180a..84cf6cf5c 100644 --- a/pages/docs/manual/latest/async-await.mdx +++ b/pages/docs/manual/latest/async-await.mdx @@ -20,7 +20,7 @@ import "src/Demo.mjs" # Async / Await -***Since 10.1*** +**Since 10.1** ReScript comes with `async` / `await` support to make asynchronous, `Promise` based code easier to read and write. This feature is very similar to its JS equivalent, so if you are already familiar with JS' `async` / `await`, you will feel right at home. diff --git a/pages/docs/manual/latest/promise.mdx b/pages/docs/manual/latest/promise.mdx index c4ba35b2c..1be9b3877 100644 --- a/pages/docs/manual/latest/promise.mdx +++ b/pages/docs/manual/latest/promise.mdx @@ -10,7 +10,7 @@ canonical: "/docs/manual/latest/promise" ## `promise` type -*Since 10.1* +**Since 10.1** In ReScript, every JS promise is represented with the globally available `promise<'a>` type. For ReScript versions < 10.1, use its original alias `Js.Promise.t<'a>` instead. From 94f9354ba8d263c825807315c7b17791786c3876 Mon Sep 17 00:00:00 2001 From: Patrick Ecker Date: Wed, 1 Feb 2023 16:46:12 +0100 Subject: [PATCH 07/26] Add async/await to syntax widget --- misc_docs/syntax/language_async.mdx | 45 +++++++++++++++++++++++ misc_docs/syntax/language_async_await.mdx | 41 +++++++++++++++++++++ misc_docs/syntax/language_await.mdx | 38 +++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 misc_docs/syntax/language_async.mdx create mode 100644 misc_docs/syntax/language_async_await.mdx create mode 100644 misc_docs/syntax/language_await.mdx diff --git a/misc_docs/syntax/language_async.mdx b/misc_docs/syntax/language_async.mdx new file mode 100644 index 000000000..616b9eff3 --- /dev/null +++ b/misc_docs/syntax/language_async.mdx @@ -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 + + + +```res +// Some fictive functionality that offers asynchronous network actions +@val external fetchUserMail: string => promise = "GlobalAPI.fetchUserMail" +@val external sendAnalytics: string => promise = "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 + ""); +} +``` + + + +### References + +- [Async / Await](/docs/manual/latest/async-await) diff --git a/misc_docs/syntax/language_async_await.mdx b/misc_docs/syntax/language_async_await.mdx new file mode 100644 index 000000000..e1b9a9a33 --- /dev/null +++ b/misc_docs/syntax/language_async_await.mdx @@ -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 + + + +```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); +} +``` + + + +### References + +- [Uncurried Function](/docs/manual/latest/function#uncurried-function) diff --git a/misc_docs/syntax/language_await.mdx b/misc_docs/syntax/language_await.mdx new file mode 100644 index 000000000..611993648 --- /dev/null +++ b/misc_docs/syntax/language_await.mdx @@ -0,0 +1,38 @@ +--- +id: "await" +keywords: ["await"] +name: "await" +summary: "This is the `await` keyword." +category: "languageconstructs" +--- + +**Since 10.1** + +Use the `await` within an `async` function to unwrap a promise value in a seamingly synchronous manner. + +### Example + + + +```res +@val external queryMessagesApi: string => promise = "global.queryMessagesApi" + +let fetchMessages = async () => { + let message = await queryMessagesApi("message-id-1") + + Js.log(message) +} +``` + +```js +async function fetchMessages(param) { + var message = await global.queryMessagesApi("message-id-1"); + console.log(message); +} +``` + + + +### References + +- [Async / Await](/docs/manual/latest/async-await) From 44cfb736ea6ce8ed38efc911c75dad7bcfce61ba Mon Sep 17 00:00:00 2001 From: Patrick Ecker Date: Wed, 1 Feb 2023 16:51:45 +0100 Subject: [PATCH 08/26] Add async/await to cheatsheet --- pages/docs/manual/latest/overview.mdx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pages/docs/manual/latest/overview.mdx b/pages/docs/manual/latest/overview.mdx index 9efad93a7..e9e3767f5 100644 --- a/pages/docs/manual/latest/overview.mdx +++ b/pages/docs/manual/latest/overview.mdx @@ -101,6 +101,15 @@ canonical: "/docs/manual/latest/overview" | `const f = function(arg) {...}` | `let f = (arg) => {...}` | | `add(4, add(5, 6))` | Same | +### Async Function / Await + +| JavaScript | ReScript | +| ---------------------------------------- | ---------------------------------- | +| `async (arg) => {...}` | Same | +| `async function named(arg) {...}` | `let named = async (arg) => {...}` | +| `await somePromise` | Same | +| `async (arg): Promise => {...}` | `async (): string => {...}` (note the return type)| + ### Blocks From c48f114ff864d524d6df6403cc4c3e2a524c6b06 Mon Sep 17 00:00:00 2001 From: Patrick Ecker Date: Wed, 1 Feb 2023 22:21:43 +0100 Subject: [PATCH 09/26] Add blogpost --- _blogposts/2022-12-14-release-10-1.mdx | 302 +++++++++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 _blogposts/2022-12-14-release-10-1.mdx diff --git a/_blogposts/2022-12-14-release-10-1.mdx b/_blogposts/2022-12-14-release-10-1.mdx new file mode 100644 index 000000000..5cdc19333 --- /dev/null +++ b/_blogposts/2022-12-14-release-10-1.mdx @@ -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 rescript@10.1 +``` + +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 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 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 = "GlobalAPI.fetchUserMail" +@val external sendAnalytics: string => promise = "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 +``` + +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/react@0.11` 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/react@0.11` 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! + + + +```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) => { +
{React.string(`Hello ${name}`)}
+ } +} + +module ClassicModeExample = { + // "classic" mode will compile jsx to React.createElement calls + @@jsxConfig({version: 4, mode: "classic"}) + + @react.component + let make = (~name) => { +
{React.string(`Hello ${name}`)}
+ } +} + +module NoDecoratorExample = { + // No need for `makeProps` anymore + type props = {name: string} + + let make = (props: props) => { +
{React.string(`Hello ${props.name}`)}
+ } +} + +module ReactInterfaceExample: { + @react.component + let make: (~name: string, ~age: int=?) => React.element +} = { + @react.component + let make = (~name, ~age=0) => { +
+ {React.string( + `Hello ${name}, you are ${Belt.Int.toString(age)} years old.`, + )} +
+ } +} + +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" + } +
{React.string(msg)}
+ } +} + +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"} + + + } +} + +let root = +
+ + + + + + +
+``` + +```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 , +} +``` +
+ +### 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. From 4ceec0708c41cdb66c8720fb30de95402087b30a Mon Sep 17 00:00:00 2001 From: Patrick Ecker Date: Wed, 1 Feb 2023 22:24:27 +0100 Subject: [PATCH 10/26] Rename blogpost date --- .../{2022-12-14-release-10-1.mdx => 2023-02-02-release-10-1.mdx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename _blogposts/{2022-12-14-release-10-1.mdx => 2023-02-02-release-10-1.mdx} (100%) diff --git a/_blogposts/2022-12-14-release-10-1.mdx b/_blogposts/2023-02-02-release-10-1.mdx similarity index 100% rename from _blogposts/2022-12-14-release-10-1.mdx rename to _blogposts/2023-02-02-release-10-1.mdx From 917c905ca794feee85456504de4fc03c6c179c04 Mon Sep 17 00:00:00 2001 From: Patrick Ecker Date: Wed, 1 Feb 2023 22:25:04 +0100 Subject: [PATCH 11/26] Small text improvement --- _blogposts/2023-02-02-release-10-1.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_blogposts/2023-02-02-release-10-1.mdx b/_blogposts/2023-02-02-release-10-1.mdx index 5cdc19333..0b3bf0604 100644 --- a/_blogposts/2023-02-02-release-10-1.mdx +++ b/_blogposts/2023-02-02-release-10-1.mdx @@ -23,7 +23,7 @@ npm install rescript npm install rescript@10.1 ``` -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 code generation and React 18 support. +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. From dc70b895c62e19f44d7aceac3a90db8c2a1c6dc3 Mon Sep 17 00:00:00 2001 From: Patrick Ecker Date: Wed, 1 Feb 2023 22:26:48 +0100 Subject: [PATCH 12/26] Remove temporary Demo.mjs import --- pages/docs/manual/latest/async-await.mdx | 2 -- 1 file changed, 2 deletions(-) diff --git a/pages/docs/manual/latest/async-await.mdx b/pages/docs/manual/latest/async-await.mdx index 84cf6cf5c..2fd125865 100644 --- a/pages/docs/manual/latest/async-await.mdx +++ b/pages/docs/manual/latest/async-await.mdx @@ -4,8 +4,6 @@ description: "Async / await for asynchronous operations" canonical: "/docs/manual/latest/async-await" --- -import "src/Demo.mjs" -
From 8eef73463a809bdd4125429edc0edf4bdfae95d4 Mon Sep 17 00:00:00 2001 From: Patrick Ecker Date: Wed, 1 Feb 2023 22:49:07 +0100 Subject: [PATCH 13/26] Improve promise docs note --- pages/docs/manual/latest/promise.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/docs/manual/latest/promise.mdx b/pages/docs/manual/latest/promise.mdx index 1be9b3877..b07b3d969 100644 --- a/pages/docs/manual/latest/promise.mdx +++ b/pages/docs/manual/latest/promise.mdx @@ -96,7 +96,7 @@ let logAsyncMessage = async () => { ## Js.Promise module (legacy - do not use) -> **Note:** The `Js.Promise` bindings are following the outdated data-last convention from a few years ago. We kept those APIs for backwards compatibility, so for now please use [`rescript-promise`](https://github.com/ryyppy/rescript-promise) until we upstream the new bindings to our standard library. +> **Note:** The `Js.Promise` bindings are following the outdated data-last convention from a few years ago. We kept those APIs for backwards compatibility. Either use `Js.Promise2` or a third-party promise binding instead. ReScript has built-in support for [JavaScript promises](api/js/promise). The 3 functions you generally need are: From aa9d556c751321132cb499d60d6e6700478abc74 Mon Sep 17 00:00:00 2001 From: Patrick Ecker Date: Wed, 1 Feb 2023 22:51:10 +0100 Subject: [PATCH 14/26] Expand sentence in promise docs --- pages/docs/manual/latest/promise.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/docs/manual/latest/promise.mdx b/pages/docs/manual/latest/promise.mdx index b07b3d969..c99216908 100644 --- a/pages/docs/manual/latest/promise.mdx +++ b/pages/docs/manual/latest/promise.mdx @@ -30,7 +30,7 @@ To work with promise values (instead of using `async` / `await`) you may want to A builtin module to create, chain and manipulate promises. -**Note:** This is an intermediate replacement for the `Js.Promise` module. It is designed to work with the `->` operator and should be used in favour of it's legacy counterpart. We are aware that the `Belt`, `Js` and `Js.xxx2` situation is confusing; a proper solution will hopefully be part of our upcoming `v11` release. +> **Note:** This is an intermediate replacement for the `Js.Promise` module. It is designed to work with the `->` operator and should be used in favour of it's legacy counterpart. We are aware that the `Belt`, `Js` and `Js.xxx2` situation is confusing; a proper solution will hopefully be part of our upcoming `v11` release. ### Creating a promise @@ -77,7 +77,7 @@ let logAsyncMessage = async () => { } ``` -Way better. +Needless to say, the async / await version offers better ergonomics and less opportunities to run into type issues. ### Run multiple promises in parallel From 6b991f08cd7105f32e2d3e7c0c5ecfbfc1703cd1 Mon Sep 17 00:00:00 2001 From: Patrick Ecker Date: Wed, 1 Feb 2023 22:53:04 +0100 Subject: [PATCH 15/26] Improve Promise.all example --- pages/docs/manual/latest/promise.mdx | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/pages/docs/manual/latest/promise.mdx b/pages/docs/manual/latest/promise.mdx index c99216908..c2b840b31 100644 --- a/pages/docs/manual/latest/promise.mdx +++ b/pages/docs/manual/latest/promise.mdx @@ -83,6 +83,9 @@ Needless to say, the async / await version offers better ergonomics and less opp In case you want to launch multiple promises in parallel, use `Js.Promise2.all`: + + + ```res @val external fetchMessage: string => promise = "global.fetchMessage" @@ -90,10 +93,26 @@ external fetchMessage: string => promise = "global.fetchMessage" let logAsyncMessage = async () => { let messages = await Js.Promise2.all([fetchMessage("message1"), fetchMessage("message2")]) - Js.log(Belt.Array.joinWith(messages, ", ")) + Js.log(Js.Array2.joinWith(messages, ", ")) } ``` +```js +async function logAsyncMessage(param) { + var messages = await Promise.all([ + global.fetchMessage("message1"), + global.fetchMessage("message2") + ]); + console.log(messages.join(", ")); +} + +export { + logAsyncMessage , +} +``` + + + ## Js.Promise module (legacy - do not use) > **Note:** The `Js.Promise` bindings are following the outdated data-last convention from a few years ago. We kept those APIs for backwards compatibility. Either use `Js.Promise2` or a third-party promise binding instead. From 3635caa0620e6a8ceecb9cca3ec34de5f1a6eb3b Mon Sep 17 00:00:00 2001 From: Patrick Ecker Date: Wed, 1 Feb 2023 22:54:37 +0100 Subject: [PATCH 16/26] Add "the" --- _blogposts/2023-02-02-release-10-1.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_blogposts/2023-02-02-release-10-1.mdx b/_blogposts/2023-02-02-release-10-1.mdx index 0b3bf0604..cfbfbb4ed 100644 --- a/_blogposts/2023-02-02-release-10-1.mdx +++ b/_blogposts/2023-02-02-release-10-1.mdx @@ -27,7 +27,7 @@ This version comes with two major language improvements we've all been waiting f 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 details. +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 From 48fd6ac085f8d7e871ee1db16642dedd06ea7775 Mon Sep 17 00:00:00 2001 From: Patrick Ecker Date: Wed, 1 Feb 2023 23:08:39 +0100 Subject: [PATCH 17/26] Update roadmap page --- pages/community/roadmap.mdx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/pages/community/roadmap.mdx b/pages/community/roadmap.mdx index 11f46c4fd..4863da100 100644 --- a/pages/community/roadmap.mdx +++ b/pages/community/roadmap.mdx @@ -6,4 +6,16 @@ canonical: "/community/roadmap" # Roadmap -TODO +ReScript development is an Open Source effort coordinated by individiual Open Source contributors and members of the [ReScript Association](https://rescript-association.org). + +For latest development updates, please check out the [ReScript forum](https://forum.rescript-lang.org) development section and the [ReScript blog](/blog), or follow the relevant GitHub milestones on the rescript-compiler repository. + +## Current focus + +Major v11.0 release (see [v11 milestone](https://github.com/rescript-lang/rescript-compiler/issues?q=is%3Aopen+is%3Aissue+milestone%3Av11.0)). + +- Uncurried functions +- Remove unnecessary Stdlib modules +- Explore better Belt / Js module alternative, tooling for markdown documentation generation, and more. + +**Note:** Release goals may be subject of change. From 93fad268422f32dd40833ece5e9bf865863fd285 Mon Sep 17 00:00:00 2001 From: Patrick Ecker Date: Thu, 2 Feb 2023 07:55:59 +0100 Subject: [PATCH 18/26] Update npmjs link --- _blogposts/2023-02-02-release-10-1.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_blogposts/2023-02-02-release-10-1.mdx b/_blogposts/2023-02-02-release-10-1.mdx index cfbfbb4ed..5a19c5694 100644 --- a/_blogposts/2023-02-02-release-10-1.mdx +++ b/_blogposts/2023-02-02-release-10-1.mdx @@ -13,7 +13,7 @@ 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): +Use `npm` to install the newest [10.1 release](https://www.npmjs.com/package/rescript/v/10.1.2): ``` npm install rescript From 9fbd49ed5549687ac2434c75e31d5187a3d532d2 Mon Sep 17 00:00:00 2001 From: Patrick Ecker Date: Thu, 2 Feb 2023 09:29:28 +0100 Subject: [PATCH 19/26] Apply suggestions from code review Co-authored-by: Christoph Knittel --- _blogposts/2023-02-02-release-10-1.mdx | 8 ++++---- pages/docs/manual/latest/async-await.mdx | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/_blogposts/2023-02-02-release-10-1.mdx b/_blogposts/2023-02-02-release-10-1.mdx index 5a19c5694..267a13b2b 100644 --- a/_blogposts/2023-02-02-release-10-1.mdx +++ b/_blogposts/2023-02-02-release-10-1.mdx @@ -23,11 +23,11 @@ npm install rescript npm install rescript@10.1 ``` -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. +This version comes with two major language improvements we've all been waiting for. **async/await support** for an easy way to write 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. +Feel free to check the [Changelog](https://github.com/rescript-lang/rescript-compiler/blob/master/CHANGELOG.md#1011) for all the details. ## New `async` / `await` syntax @@ -80,10 +80,10 @@ If you are already using a third-party promise library like [ryyppy/rescript-pro 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/react@0.11` and `React@18`. +- **Two new transformation modes**. JSX v4 comes with a `classic` mode (= `React.createElement`) and `automatic` mode (= `jsx-runtime` calls). The latter is the new default, moving forward with `rescript/react@0.11` 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/react@0.11` or higher) +- **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 its JS counterpart. (requires `rescript/react@0.11` 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! diff --git a/pages/docs/manual/latest/async-await.mdx b/pages/docs/manual/latest/async-await.mdx index 2fd125865..d16fdc2b3 100644 --- a/pages/docs/manual/latest/async-await.mdx +++ b/pages/docs/manual/latest/async-await.mdx @@ -57,7 +57,7 @@ async function logUserDetails(userId) { As we can see above, an `async` function is defined via the `async` keyword right before the function's parameter list. In the function body, we are now able to use the `await` keyword to explicitly wait for a `Promise` value and assign its content to a let binding `email`. -You will probably notice that this looks very similar to `async` / `await` in JS, but there's still a few details that are specific to ReScript. The next few sections will go through all the details that are specific to the ReScript type system. +You will probably notice that this looks very similar to `async` / `await` in JS, but there are still a few details that are specific to ReScript. The next few sections will go through all the details that are specific to the ReScript type system. ## Basics From c64d1ef8bdf741561233cd1916d558c563385714 Mon Sep 17 00:00:00 2001 From: Patrick Ecker Date: Thu, 2 Feb 2023 09:40:39 +0100 Subject: [PATCH 20/26] Apply suggestions from code review --- _blogposts/2023-02-02-release-10-1.mdx | 2 +- pages/docs/manual/latest/async-await.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/_blogposts/2023-02-02-release-10-1.mdx b/_blogposts/2023-02-02-release-10-1.mdx index 267a13b2b..ec787688a 100644 --- a/_blogposts/2023-02-02-release-10-1.mdx +++ b/_blogposts/2023-02-02-release-10-1.mdx @@ -284,7 +284,7 @@ Make sure to also check out the [rescript-react changelog](https://github.com/re ## 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: +Our contributors are already one step ahead and are currently working on improvements for the next major v11 release. 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). diff --git a/pages/docs/manual/latest/async-await.mdx b/pages/docs/manual/latest/async-await.mdx index d16fdc2b3..dd7bb0105 100644 --- a/pages/docs/manual/latest/async-await.mdx +++ b/pages/docs/manual/latest/async-await.mdx @@ -65,7 +65,7 @@ You will probably notice that this looks very similar to `async` / `await` in JS - `await` may only be called on a `promise` value - `await` calls are expressions, therefore they can be used in pattern matching (`switch`) - A function returning a `promise<'a>` is equivalent to an `async` function returning a value `'a` (important for writing signature files and bindings) -- `promise` values and types returned from an `async` function don't auto-collapse +- `promise` values and types returned from an `async` function don't auto-collapse into a "flat promise" like in JS (more on this later) ## Types and `async` functions From 7427d76d2db92cf088de8211fa4e85330db58911 Mon Sep 17 00:00:00 2001 From: Patrick Ecker Date: Thu, 2 Feb 2023 09:45:17 +0100 Subject: [PATCH 21/26] Apply suggestions from code review (zth) Co-authored-by: Gabriel Nordeborn --- _blogposts/2023-02-02-release-10-1.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/_blogposts/2023-02-02-release-10-1.mdx b/_blogposts/2023-02-02-release-10-1.mdx index ec787688a..c597e18ee 100644 --- a/_blogposts/2023-02-02-release-10-1.mdx +++ b/_blogposts/2023-02-02-release-10-1.mdx @@ -84,6 +84,8 @@ ReScript 10.1 now ships with JSX v4. Here's what's new: - **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 its JS counterpart. (requires `rescript/react@0.11` or higher) +- **Paving the way for better editor tooling.** The new representation in JSX v4 opens up for better editor tooling, like providing typed completion for values and callbacks of JSX props. +- **Better type inference of props.** Type inference when passing for example variants that are defined in the same module as the component is much improved. With the earlier JSX version, you'd often need to write code like this in order for the compiler to understand which variant you're passing: `