|
1 | 1 | ---
|
2 | 2 | author: rescript-team
|
3 |
| -date: "2025-09-01" |
| 3 | +date: "2025-10-14" |
| 4 | +previewImg: /static/blog/rescript-12-let-unwrap.jpg |
4 | 5 | badge: roadmap
|
5 | 6 | title: let?
|
6 | 7 | description: |
|
@@ -55,6 +56,7 @@ Let's rewrite the above example again with our new syntax:
|
55 | 56 | let getUser = async (id) => {
|
56 | 57 | let? Ok(user) = await fetchUser(id)
|
57 | 58 | let? Ok(decodedUser) = decodeUser(user)
|
| 59 | + Console.log(`Got user ${decodedUser.name}!`) |
58 | 60 | let? Ok() = await ensureUserActive(decodedUser)
|
59 | 61 | Ok(decodedUser)
|
60 | 62 | }
|
@@ -96,9 +98,93 @@ let? Ok(user) = await fetchUser("1")
|
96 | 98 | // ^^^^^^^ ERROR: `let?` is not allowed for top-level bindings.
|
97 | 99 | ```
|
98 | 100 |
|
99 |
| -<!-- TODO: demonstrate error handling with polymorphic variants a little more --> |
| 101 | +### A full example with error handling |
100 | 102 |
|
101 |
| -### How to enable it |
| 103 | +<CodeTab labels={["ReScript", "JS Output"]} experiments="LetUnwrap"> |
| 104 | + |
| 105 | +```rescript |
| 106 | +type user = { |
| 107 | + id: string, |
| 108 | + name: string, |
| 109 | + token: string, |
| 110 | +} |
| 111 | +
|
| 112 | +external fetchUser: string => promise< |
| 113 | + result<JSON.t, [> #NetworkError | #UserNotFound | #Unauthorized]>, |
| 114 | +> = "fetchUser" |
| 115 | +
|
| 116 | +external decodeUser: JSON.t => result<user, [> #DecodeError]> = "decodeUser" |
| 117 | +
|
| 118 | +external ensureUserActive: user => promise<result<unit, [> #UserNotActive]>> = |
| 119 | + "ensureUserActive" |
| 120 | +
|
| 121 | +let getUser = async id => { |
| 122 | + let? Ok(user) = await fetchUser(id) |
| 123 | + let? Ok(decodedUser) = decodeUser(user) |
| 124 | + Console.log(`Got user ${decodedUser.name}!`) |
| 125 | + let? Ok() = await ensureUserActive(decodedUser) |
| 126 | + Ok(decodedUser) |
| 127 | +} |
| 128 | +
|
| 129 | +// ERROR! |
| 130 | +// You forgot to handle a possible case here, for example: |
| 131 | +// | Error(#Unauthorized | #UserNotFound | #DecodeError | #UserNotActive) |
| 132 | +let main = async () => { |
| 133 | + switch await getUser("123") { |
| 134 | + | Ok(user) => Console.log(user) |
| 135 | + | Error(#NetworkError) => Console.error("Uh-oh, network error...") |
| 136 | + } |
| 137 | +} |
| 138 | +``` |
| 139 | + |
| 140 | +```js |
| 141 | +async function getUser(id) { |
| 142 | + let e = await fetchUser(id); |
| 143 | + if (e.TAG !== "Ok") { |
| 144 | + return e; |
| 145 | + } |
| 146 | + let e$1 = decodeUser(e._0); |
| 147 | + if (e$1.TAG !== "Ok") { |
| 148 | + return e$1; |
| 149 | + } |
| 150 | + let decodedUser = e$1._0; |
| 151 | + console.log(`Got user ` + decodedUser.name + `!`); |
| 152 | + let e$2 = await ensureUserActive(decodedUser); |
| 153 | + if (e$2.TAG === "Ok") { |
| 154 | + return { |
| 155 | + TAG: "Ok", |
| 156 | + _0: decodedUser |
| 157 | + }; |
| 158 | + } else { |
| 159 | + return e$2; |
| 160 | + } |
| 161 | +} |
| 162 | + |
| 163 | +async function main() { |
| 164 | + let user = await getUser("123"); |
| 165 | + if (user.TAG === "Ok") { |
| 166 | + console.log(user._0); |
| 167 | + return; |
| 168 | + } |
| 169 | + if (user._0 === "NetworkError") { |
| 170 | + console.error("Uh-oh, network error..."); |
| 171 | + return; |
| 172 | + } |
| 173 | + throw { |
| 174 | + RE_EXN_ID: "Match_failure", |
| 175 | + _1: [ |
| 176 | + "playground.res", |
| 177 | + 28, |
| 178 | + 2 |
| 179 | + ], |
| 180 | + Error: new Error() |
| 181 | + }; |
| 182 | +} |
| 183 | +``` |
| 184 | + |
| 185 | +</CodeTab> |
| 186 | + |
| 187 | +## Experimental features |
102 | 188 |
|
103 | 189 | We have added an **experimental-features infrastructure** to the toolchain. If you use the new build system that comes with ReScript 12 by default, you can enable it in `rescript.json` like so:
|
104 | 190 |
|
|
0 commit comments