diff --git a/misc_docs/syntax/decorator_unboxed.mdx b/misc_docs/syntax/decorator_unboxed.mdx index 4eb9c7c89..05fd81337 100644 --- a/misc_docs/syntax/decorator_unboxed.mdx +++ b/misc_docs/syntax/decorator_unboxed.mdx @@ -7,7 +7,7 @@ category: "decorators" --- The `@unboxed` decorator provides a way to unwrap **variant** constructors -that have a *single* argument, or **record** objects that have a *single* field. +that have no overlap in their runtime representation, or **record** objects that have a *single* field. ### Example @@ -15,7 +15,8 @@ that have a *single* argument, or **record** objects that have a *single* field. ```res @unboxed -type name = Name(string) +type listItemValue = String(string) | Boolean(bool) | Number(float) +let myArray = [String("Hello"), Boolean(true), Boolean(false), Number(13.37)] let studentName = Name("Joe") @unboxed @@ -24,12 +25,16 @@ let hi = {message: "hello!"} ``` ```js -var studentName = "Joe"; +var myArray = ["hello", true, false, 13.37]; var hi = "hello!"; ``` +_`@unboxed` for variants with multiple constructors is available since ReScript `11.0.0`._ + ### References -* [Unboxed](/docs/manual/latest/unboxed) \ No newline at end of file +* [Untagged variants doc](/docs/manual/latest/variant#untagged-variants) +* [Blog post introducing untagged variants](/blog/improving-interop) +* [Unboxed record](/docs/manual/v10.0.0/unboxed) \ No newline at end of file diff --git a/pages/docs/manual/latest/interop-cheatsheet.mdx b/pages/docs/manual/latest/interop-cheatsheet.mdx index 4e189cb45..e2b366408 100644 --- a/pages/docs/manual/latest/interop-cheatsheet.mdx +++ b/pages/docs/manual/latest/interop-cheatsheet.mdx @@ -45,7 +45,7 @@ This is a glossary with examples. All the features are described by later pages. - [`@JSX`](jsx) - `@react.component`: [here](/docs/react/latest/introduction) and [here](https://github.com/reasonml/reason-react) - [`@warning`](attribute#usage) -- [`@unboxed`](unboxed) +- [`@unboxed`](variant#untagged-variants) ### Extension Points diff --git a/pages/docs/manual/latest/unboxed.mdx b/pages/docs/manual/latest/unboxed.mdx deleted file mode 100644 index 6ebe3c4b3..000000000 --- a/pages/docs/manual/latest/unboxed.mdx +++ /dev/null @@ -1,157 +0,0 @@ ---- -title: "Unboxed" -description: "Unbox a wrapper" -canonical: "/docs/manual/latest/unboxed" ---- - -# Unboxed - -Consider a ReScript variant with a single payload, and a record with a single field: - - - -```res -type name = Name(string) -let studentName = Name("Joe") - -type greeting = {message: string} -let hi = {message: "hello!"} -``` -```js -var studentName = /* Name */{ - _0: "Joe" -}; - -var hi = { - message: "hello!" -}; -``` - - - -If you check the JavaScript output, you'll see the `studentName` and `hi` JS object, as expected (see the [variant JS output](variant#javascript-output) and [record JS output](record#javascript-output) sections for details). - -For performance and certain JavaScript interop situations, ReScript offers a way to unwrap (aka unbox) the JS object wrappers from the output for records with a single field and variants with a single constructor and single payload. Annotate their type declaration with the attribute `@unboxed`: - - - -```res -@unboxed -type name = Name(string) -let studentName = Name("Joe") - -@unboxed -type greeting = {message: string} -let hi = {message: "hello!"} -``` -```js -var studentName = "Joe"; - -var hi = "hello!"; -``` - - - -Check the new output! Clean. - -## Usage - -Why would you ever want a variant or a record with a single payload? Why not just... pass the payload? Here's one use-case for variant. - -Suppose you have a game with a local/global coordinate system: - - - -```res example -type coordinates = {x: float, y: float} - -let renderDot = (coordinates) => { - Js.log3("Pretend to draw at:", coordinates.x, coordinates.y) -} - -let toWorldCoordinates = (localCoordinates) => { - { - x: localCoordinates.x +. 10., - y: localCoordinates.x +. 20., - } -} - -let playerLocalCoordinates = {x: 20.5, y: 30.5} - -renderDot(playerLocalCoordinates) -``` -```js -function renderDot(coordinates) { - console.log("Pretend to draw at:", coordinates.x, coordinates.y); -} - -function toWorldCoordinates(localCoordinates) { - return { - x: localCoordinates.x + 10, - y: localCoordinates.x + 20 - }; -} - -var playerLocalCoordinates = { - x: 20.5, - y: 30.5 -}; - -renderDot(playerLocalCoordinates); -``` - - - -Oops, that's wrong! `renderDot` should have taken global coordinates, not local ones... Let's prevent passing the wrong kind of coordinates: - - - -```res example -type coordinates = {x: float, y: float} -@unboxed type localCoordinates = Local(coordinates) -@unboxed type worldCoordinates = World(coordinates) - -let renderDot = (World(coordinates)) => { - Js.log3("Pretend to draw at:", coordinates.x, coordinates.y) -} - -let toWorldCoordinates = (Local(coordinates)) => { - World({ - x: coordinates.x +. 10., - y: coordinates.x +. 20., - }) -} - -let playerLocalCoordinates = Local({x: 20.5, y: 30.5}) - -// This now errors! -// renderDot(playerLocalCoordinates) -// We're forced to do this instead: -renderDot(playerLocalCoordinates->toWorldCoordinates) -``` -```js -function renderDot(coordinates) { - console.log("Pretend to draw at:", coordinates.x, coordinates.y); -} - -function toWorldCoordinates(coordinates) { - return { - x: coordinates.x + 10, - y: coordinates.x + 20 - }; -} - -var playerLocalCoordinates = { - x: 20.5, - y: 30.5 -}; - -renderDot(toWorldCoordinates(playerLocalCoordinates)); -``` - - - -Now `renderDot` only takes `worldCoordinates`. Through a nice combination of using distinct variant types + argument destructuring, we've achieved better safety **without compromising on performance**: the `unboxed` attribute compiled to clean, variant-wrapper-less JS code! Check the output. - -As for a record with a single field, the use-cases are a bit more edgy. We won't mention them here. -