You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: _blogposts/2025-09-01-let-unwrap.mdx
+10-11Lines changed: 10 additions & 11 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,12 +4,12 @@ date: "2025-09-01"
4
4
badge: roadmap
5
5
title: let?
6
6
description: |
7
-
A new let-unwrap syntax just landed in ReScript. Experimental!
7
+
A new let-unwrap syntax just landed in ReScript. Experimental!
8
8
---
9
9
10
10
After long discussions we finally decided on an unwrap syntax for both the `option` and `result` types that we are happy with and that still matches the explicitness of ReScript we all like.
11
11
12
-
`let?` or `let-unwrap` is a tiny syntax that unwraps `result`/`option` values and *early-returns* on `Error`/`None`. It’s explicitly **experimental** and **disabled by default** behind a new "experimental features" gate. See below how to enable it.
12
+
`let?` or `let-unwrap` is a tiny syntax that unwraps `result`/`option` values and _early-returns_ on `Error`/`None`. It’s explicitly **experimental** and **disabled by default** behind a new "experimental features" gate. See below how to enable it.
13
13
14
14
Before showing off this new feauture, let's explore why it is useful. Consider a chain of `async` functions that are dependent on the result of the previous one. The naive way to write this in modern ReScript with `async`/`await` is to just `switch` on the results.
15
15
@@ -29,9 +29,10 @@ let getUser = async id =>
29
29
}
30
30
```
31
31
32
-
Two observations:
33
-
1. with every `switch` expression, this function gets nested deeper.
34
-
2. The `Error` branch of every `switch` is just an identity mapper (neither wrapper nor contents change).
32
+
Two observations:
33
+
34
+
1. with every `switch` expression, this function gets nested deeper.
35
+
2. The `Error` branch of every `switch` is just an identity mapper (neither wrapper nor contents change).
35
36
36
37
The only alternative in ReScript was always to use some specialized methods:
37
38
@@ -65,7 +66,7 @@ With `let?`, we can now safely focus on the the happy-path in the scope of the f
65
66
66
67
This desugars to a **sequence** of early-returns that you’d otherwise write by hand, so there’s **no extra runtime cost** and it plays nicely with `async/await` as the example above suggests.
67
68
68
-
Of course, it also works for `option` with `Some(...)`.
69
+
Of course, it also works for `option` with `Some(...)`.
69
70
70
71
```rescript
71
72
let getActiveUser = user => {
@@ -74,7 +75,7 @@ let getActiveUser = user => {
74
75
}
75
76
```
76
77
77
-
It also works with the unhappy path, with `Error(...)` or `None` as the main type and `Ok(...)` or `Some(...)` as the implicitly mapped types.
78
+
It also works with the unhappy path, with `Error(...)` or `None` as the main type and `Ok(...)` or `Some(...)` as the implicitly mapped types.
78
79
79
80
```rescript
80
81
let getNoUser = user => {
@@ -88,11 +89,11 @@ let decodeUserWithHumanReadableError = user => {
88
89
}
89
90
```
90
91
91
-
Beware it targets built-ins only, namely `result` and `option`. Custom variants still need `switch`. And it is for block or local bindings only; top-level usage is rejected.
92
+
Beware it targets built-ins only, namely `result` and `option`. Custom variants still need `switch`. And it is for block or local bindings only; top-level usage is rejected.
92
93
93
94
```rescript
94
95
let? Ok(user) = await fetchUser("1")
95
-
// ^^^^^^^ ERROR: `let?` is not allowed for top-level bindings.
96
+
// ^^^^^^^ ERROR: `let?` is not allowed for top-level bindings.
0 commit comments