Skip to content

Commit 555522e

Browse files
committed
PoC of let.unwrap
1 parent d9bfbc8 commit 555522e

File tree

5 files changed

+307
-0
lines changed

5 files changed

+307
-0
lines changed

compiler/frontend/ast_attributes.ml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,12 @@ let has_bs_optional (attrs : t) : bool =
199199
true
200200
| _ -> false)
201201

202+
let has_unwrap_attr (attrs : t) : bool =
203+
Ext_list.exists attrs (fun ({txt}, _) ->
204+
match txt with
205+
| "let.unwrap" -> true
206+
| _ -> false)
207+
202208
let iter_process_bs_int_as (attrs : t) =
203209
let st = ref None in
204210
Ext_list.iter attrs (fun (({txt; loc}, payload) as attr) ->

compiler/frontend/ast_attributes.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ val iter_process_bs_string_as : t -> string option
4646

4747
val has_bs_optional : t -> bool
4848

49+
val has_unwrap_attr : t -> bool
50+
4951
val iter_process_bs_int_as : t -> int option
5052

5153
type as_const_payload = Int of int | Str of string * External_arg_spec.delim

compiler/frontend/bs_builtin_ppx.ml

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,75 @@ let expr_mapper ~async_context ~in_function_def (self : mapper)
143143
] ) ->
144144
default_expr_mapper self
145145
{e with pexp_desc = Pexp_ifthenelse (b, t_exp, Some f_exp)}
146+
(* Transform:
147+
- `@let.unwrap let Ok(inner_pat) = expr`
148+
- `@let.unwrap let Some(inner_pat) = expr`
149+
...into switches *)
150+
| Pexp_let
151+
( Nonrecursive,
152+
[
153+
{
154+
pvb_pat =
155+
{
156+
ppat_desc =
157+
Ppat_construct
158+
( {txt = Lident (("Ok" | "Some") as variant_name)},
159+
Some _inner_pat );
160+
} as pvb_pat;
161+
pvb_expr;
162+
pvb_attributes;
163+
};
164+
],
165+
body )
166+
when Ast_attributes.has_unwrap_attr pvb_attributes -> (
167+
let variant =
168+
match variant_name with
169+
| "Ok" -> `Result
170+
| _ -> `Option
171+
in
172+
match pvb_expr.pexp_desc with
173+
| Pexp_pack _ -> default_expr_mapper self e
174+
| _ ->
175+
let ok_case =
176+
{
177+
Parsetree.pc_bar = None;
178+
pc_lhs = pvb_pat;
179+
pc_guard = None;
180+
pc_rhs = body;
181+
}
182+
in
183+
let loc = Location.none in
184+
let error_case =
185+
match variant with
186+
| `Result ->
187+
{
188+
Parsetree.pc_bar = None;
189+
pc_lhs =
190+
Ast_helper.Pat.construct ~loc
191+
{txt = Lident "Error"; loc}
192+
(Some (Ast_helper.Pat.var ~loc {txt = "e"; loc}));
193+
pc_guard = None;
194+
pc_rhs =
195+
Ast_helper.Exp.construct ~loc
196+
{txt = Lident "Error"; loc}
197+
(Some (Ast_helper.Exp.ident ~loc {txt = Lident "e"; loc}));
198+
}
199+
| `Option ->
200+
{
201+
Parsetree.pc_bar = None;
202+
pc_lhs =
203+
Ast_helper.Pat.construct ~loc {txt = Lident "None"; loc} None;
204+
pc_guard = None;
205+
pc_rhs =
206+
Ast_helper.Exp.construct ~loc {txt = Lident "None"; loc} None;
207+
}
208+
in
209+
default_expr_mapper self
210+
{
211+
e with
212+
pexp_desc = Pexp_match (pvb_expr, [ok_case; error_case]);
213+
pexp_attributes = e.pexp_attributes @ pvb_attributes;
214+
})
146215
| Pexp_let
147216
( Nonrecursive,
148217
[

tests/tests/src/LetUnwrap.mjs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// Generated by ReScript, PLEASE EDIT WITH CARE
2+
3+
4+
function doStuffWithResult(s) {
5+
if (s === "s") {
6+
return {
7+
TAG: "Ok",
8+
_0: "hello"
9+
};
10+
} else {
11+
return {
12+
TAG: "Error",
13+
_0: "InvalidString"
14+
};
15+
}
16+
}
17+
18+
function doNextStuffWithResult(s) {
19+
if (s === "s") {
20+
return {
21+
TAG: "Ok",
22+
_0: "hello"
23+
};
24+
} else {
25+
return {
26+
TAG: "Error",
27+
_0: "InvalidNext"
28+
};
29+
}
30+
}
31+
32+
function getXWithResult(s) {
33+
let y = doStuffWithResult(s);
34+
if (y.TAG !== "Ok") {
35+
return {
36+
TAG: "Error",
37+
_0: y._0
38+
};
39+
}
40+
let y$1 = y._0;
41+
let x = doNextStuffWithResult(y$1);
42+
if (x.TAG === "Ok") {
43+
return {
44+
TAG: "Ok",
45+
_0: x._0 + y$1
46+
};
47+
} else {
48+
return {
49+
TAG: "Error",
50+
_0: x._0
51+
};
52+
}
53+
}
54+
55+
let x = getXWithResult("s");
56+
57+
let someResult;
58+
59+
someResult = x.TAG === "Ok" ? x._0 : (
60+
x._0 === "InvalidNext" ? "nope!" : "nope"
61+
);
62+
63+
function doStuffWithOption(s) {
64+
if (s === "s") {
65+
return "hello";
66+
}
67+
68+
}
69+
70+
function doNextStuffWithOption(s) {
71+
if (s === "s") {
72+
return "hello";
73+
}
74+
75+
}
76+
77+
function getXWithOption(s) {
78+
let y = doStuffWithOption(s);
79+
if (y === undefined) {
80+
return;
81+
}
82+
let x = doNextStuffWithOption(y);
83+
if (x !== undefined) {
84+
return x + y;
85+
}
86+
87+
}
88+
89+
let x$1 = getXWithOption("s");
90+
91+
let someOption = x$1 !== undefined ? x$1 : "nope";
92+
93+
async function doStuffResultAsync(s) {
94+
if (s === "s") {
95+
return {
96+
TAG: "Ok",
97+
_0: {
98+
s: "hello"
99+
}
100+
};
101+
} else {
102+
return {
103+
TAG: "Error",
104+
_0: "FetchError"
105+
};
106+
}
107+
}
108+
109+
async function decodeResAsync(res) {
110+
let match = res.s;
111+
if (match === "s") {
112+
return {
113+
TAG: "Ok",
114+
_0: res.s
115+
};
116+
} else {
117+
return {
118+
TAG: "Error",
119+
_0: "DecodeError"
120+
};
121+
}
122+
}
123+
124+
async function getXWithResultAsync(s) {
125+
let res = await doStuffResultAsync(s);
126+
if (res.TAG !== "Ok") {
127+
return {
128+
TAG: "Error",
129+
_0: res._0
130+
};
131+
}
132+
let res$1 = res._0;
133+
console.log(res$1.s);
134+
let x = await decodeResAsync(res$1);
135+
if (x.TAG === "Ok") {
136+
return {
137+
TAG: "Ok",
138+
_0: x._0
139+
};
140+
} else {
141+
return {
142+
TAG: "Error",
143+
_0: x._0
144+
};
145+
}
146+
}
147+
148+
export {
149+
doStuffWithResult,
150+
doNextStuffWithResult,
151+
getXWithResult,
152+
someResult,
153+
doStuffWithOption,
154+
doNextStuffWithOption,
155+
getXWithOption,
156+
someOption,
157+
doStuffResultAsync,
158+
decodeResAsync,
159+
getXWithResultAsync,
160+
}
161+
/* x Not a pure module */

tests/tests/src/LetUnwrap.res

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
let doStuffWithResult = s =>
2+
switch s {
3+
| "s" => Ok("hello")
4+
| _ => Error(#InvalidString)
5+
}
6+
7+
let doNextStuffWithResult = s =>
8+
switch s {
9+
| "s" => Ok("hello")
10+
| _ => Error(#InvalidNext)
11+
}
12+
13+
let getXWithResult = s => {
14+
@let.unwrap let Ok(y) = doStuffWithResult(s)
15+
@let.unwrap let Ok(x) = doNextStuffWithResult(y)
16+
Ok(x ++ y)
17+
}
18+
19+
let someResult = switch getXWithResult("s") {
20+
| Ok(x) => x
21+
| Error(#InvalidString) => "nope"
22+
| Error(#InvalidNext) => "nope!"
23+
}
24+
25+
let doStuffWithOption = s =>
26+
switch s {
27+
| "s" => Some("hello")
28+
| _ => None
29+
}
30+
31+
let doNextStuffWithOption = s =>
32+
switch s {
33+
| "s" => Some("hello")
34+
| _ => None
35+
}
36+
37+
let getXWithOption = s => {
38+
@let.unwrap let Some(y) = doStuffWithOption(s)
39+
@let.unwrap let Some(x) = doNextStuffWithOption(y)
40+
Some(x ++ y)
41+
}
42+
43+
let someOption = switch getXWithOption("s") {
44+
| Some(x) => x
45+
| None => "nope"
46+
}
47+
48+
type res = {s: string}
49+
50+
let doStuffResultAsync = async s => {
51+
switch s {
52+
| "s" => Ok({s: "hello"})
53+
| _ => Error(#FetchError)
54+
}
55+
}
56+
57+
let decodeResAsync = async res => {
58+
switch res.s {
59+
| "s" => Ok(res.s)
60+
| _ => Error(#DecodeError)
61+
}
62+
}
63+
64+
let getXWithResultAsync = async s => {
65+
@let.unwrap let Ok({s} as res) = await doStuffResultAsync(s)
66+
Console.log(s)
67+
@let.unwrap let Ok(x) = await decodeResAsync(res)
68+
Ok(x)
69+
}

0 commit comments

Comments
 (0)