Skip to content

Commit 7c3026c

Browse files
committed
Add unboxed
1 parent 7b696f9 commit 7c3026c

File tree

2 files changed

+156
-0
lines changed

2 files changed

+156
-0
lines changed

layouts/ManualDocsLayout.re

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ let basicNavs = [|
6565
{name: "Module", href: "/docs/manual/latest/module"},
6666
{name: "Import & Export", href: "/docs/manual/latest/import-export"},
6767
{name: "Attribute (Decorator)", href: "/docs/manual/latest/attribute"},
68+
{name: "Unboxed", href: "/docs/manual/latest/unboxed"},
6869
{name: "Reserved Keywords", href: "/docs/manual/latest/reserved-keywords"},
6970
|];
7071

pages/docs/manual/latest/unboxed.mdx

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
---
2+
title: "Unboxed"
3+
description: "Unbox a wrapper"
4+
canonical: "/docs/manual/latest/unboxed"
5+
---
6+
7+
Consider a ReScript variant with a single payload, and a record with a single field:
8+
9+
<CodeTab labels={["ReScript", "JS Output"]}>
10+
11+
```res
12+
type name = Name(string)
13+
let studentName = Name("Joe")
14+
15+
type greeting = {message: string}
16+
let hi = {message: "hello!"}
17+
```
18+
```js
19+
var studentName = /* Name */{
20+
_0: "Joe"
21+
};
22+
23+
var hi = {
24+
message: "hello!"
25+
};
26+
```
27+
28+
</CodeTab>
29+
30+
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).
31+
32+
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`:
33+
34+
<CodeTab labels={["ReScript", "JS Output"]}>
35+
36+
```res
37+
@unboxed
38+
type name = Name(string)
39+
let studentName = Name("Joe")
40+
41+
@unboxed
42+
type greeting = {message: string}
43+
let hi = {message: "hello!"}
44+
```
45+
```js
46+
var studentName = "Joe";
47+
48+
var hi = "hello!";
49+
```
50+
51+
</CodeTab>
52+
53+
Check the new output! Clean.
54+
55+
## Usage
56+
57+
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.
58+
59+
Suppose you have a game with a local/global coordinate system:
60+
61+
<CodeTab labels={["ReScript", "JS Output"]}>
62+
63+
```res example
64+
type coordinates = {x: float, y: float}
65+
66+
let renderDot = (coordinates) => {
67+
Js.log3("Pretend to draw at:", coordinates.x, coordinates.y)
68+
}
69+
70+
let toWorldCoordinates = (localCoordinates) => {
71+
{
72+
x: localCoordinates.x +. 10.,
73+
y: localCoordinates.x +. 20.,
74+
}
75+
}
76+
77+
let playerLocalCoordinates = {x: 20.5, y: 30.5}
78+
79+
renderDot(playerLocalCoordinates)
80+
```
81+
```js
82+
function renderDot(coordinates) {
83+
console.log("Pretend to draw at:", coordinates.x, coordinates.y);
84+
}
85+
86+
function toWorldCoordinates(localCoordinates) {
87+
return {
88+
x: localCoordinates.x + 10,
89+
y: localCoordinates.x + 20
90+
};
91+
}
92+
93+
var playerLocalCoordinates = {
94+
x: 20.5,
95+
y: 30.5
96+
};
97+
98+
renderDot(playerLocalCoordinates);
99+
```
100+
101+
</CodeTab>
102+
103+
Oops, that's wrong! `renderDot` should have taken global coordinates, not local ones... Let's prevent passing the wrong kind of coordinates:
104+
105+
<CodeTab labels={["ReScript", "JS Output"]}>
106+
107+
```res example
108+
type coordinates = {x: float, y: float}
109+
@unboxed type localCoordinates = Local(coordinates)
110+
@unboxed type worldCoordinates = World(coordinates)
111+
112+
let renderDot = (World(coordinates)) => {
113+
Js.log3("Pretend to draw at:", coordinates.x, coordinates.y)
114+
}
115+
116+
let toWorldCoordinates = (Local(coordinates)) => {
117+
World({
118+
x: coordinates.x +. 10.,
119+
y: coordinates.x +. 20.,
120+
})
121+
}
122+
123+
let playerLocalCoordinates = Local({x: 20.5, y: 30.5})
124+
125+
// This now errors!
126+
// renderDot(playerLocalCoordinates)
127+
// We're forced to do this instead:
128+
renderDot(playerLocalCoordinates->toWorldCoordinates)
129+
```
130+
```js
131+
function renderDot(coordinates) {
132+
console.log("Pretend to draw at:", coordinates.x, coordinates.y);
133+
}
134+
135+
function toWorldCoordinates(coordinates) {
136+
return {
137+
x: coordinates.x + 10,
138+
y: coordinates.x + 20
139+
};
140+
}
141+
142+
var playerLocalCoordinates = {
143+
x: 20.5,
144+
y: 30.5
145+
};
146+
147+
renderDot(toWorldCoordinates(playerLocalCoordinates));
148+
```
149+
150+
</CodeTab>
151+
152+
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.
153+
154+
As for a record with a single field, the use-cases are a bit more edgy. We won't mention them here.
155+

0 commit comments

Comments
 (0)