Fork of daniellemaywood.uk/gleam — Wasm codegen work
4.5 kB
269 lines
1use crate::assert_erl;
2
3const BOOL_MODULE: &str = "
4pub fn guard(
5 when condition: Bool,
6 return value: a,
7 otherwise callback: fn() -> a,
8) -> a {
9 case condition {
10 True -> value
11 False -> callback()
12 }
13}
14
15pub fn lazy_guard(
16 when condition: Bool,
17 return consequence: fn() -> a,
18 otherwise alternative: fn() -> a,
19) -> a {
20 case condition {
21 True -> consequence()
22 False -> alternative()
23 }
24}
25";
26
27const RESULT_MODULE: &str = "
28pub fn try(result: Result(a, e), apply f: fn(a) -> Result(b, e)) -> Result(b, e) {
29 case result {
30 Ok(value) -> f(value)
31 Error(error) -> Error(error)
32 }
33}
34
35pub fn then(result: Result(a, e), apply f: fn(a) -> Result(b, e)) -> Result(b, e) {
36 try(result, f)
37}
38
39pub fn map(over result: Result(a, e), with f: fn(a) -> b) -> Result(b, e) {
40 case result {
41 Ok(value) -> Ok(f(value))
42 Error(error) -> Error(error)
43 }
44}
45";
46
47#[test]
48fn inline_higher_order_function() {
49 assert_erl!(
50 ("gleam_stdlib", "gleam/result", RESULT_MODULE),
51 "
52import gleam/result
53
54pub fn main() {
55 result.map(over: Ok(10), with: double)
56}
57
58fn double(x) { x + x }
59"
60 );
61}
62
63#[test]
64fn inline_higher_order_function_with_capture() {
65 assert_erl!(
66 ("gleam_stdlib", "gleam/result", RESULT_MODULE),
67 "
68import gleam/result
69
70pub fn main() {
71 result.try(Ok(10), divide(_, 2))
72}
73
74fn divide(a: Int, b: Int) -> Result(Int, Nil) {
75 case a % b {
76 0 -> Ok(a / b)
77 _ -> Error(Nil)
78 }
79}
80"
81 );
82}
83
84#[test]
85fn inline_higher_order_function_anonymous() {
86 assert_erl!(
87 ("gleam_stdlib", "gleam/result", RESULT_MODULE),
88 "
89import gleam/result
90
91pub fn main() {
92 result.try(Ok(10), fn(value) {
93 Ok({ value + 2 } * 4)
94 })
95}
96"
97 );
98}
99
100#[test]
101fn inline_function_which_calls_other_function() {
102 // `result.then` calls `result.try`, meaning this must be inlined twice to
103 // achieve the desired result.
104 assert_erl!(
105 ("gleam_stdlib", "gleam/result", RESULT_MODULE),
106 "
107import gleam/result
108
109pub fn main() {
110 result.then(Ok(10), Error)
111}
112"
113 );
114}
115
116#[test]
117fn inline_function_with_use() {
118 assert_erl!(
119 ("gleam_stdlib", "gleam/bool", BOOL_MODULE),
120 "
121import gleam/bool
122
123pub fn divide(a, b) {
124 use <- bool.guard(when: b == 0, return: 0)
125 a / b
126}
127"
128 );
129}
130
131#[test]
132fn inline_function_with_use_and_anonymous() {
133 assert_erl!(
134 ("gleam_stdlib", "gleam/bool", BOOL_MODULE),
135 r#"
136import gleam/bool
137
138pub fn divide(a, b) {
139 use <- bool.lazy_guard(b == 0, fn() { panic as "Cannot divide by 0" })
140 a / b
141}
142"#
143 );
144}
145
146#[test]
147fn inline_function_with_use_becomes_tail_recursive() {
148 assert_erl!(
149 ("gleam_stdlib", "gleam/bool", BOOL_MODULE),
150 "
151import gleam/bool
152
153pub fn count(from: Int, to: Int) -> Int {
154 use <- bool.guard(when: from >= to, return: from)
155 echo from
156 count(from + 1, to)
157}
158"
159 );
160}
161
162#[test]
163fn do_not_inline_parameters_used_more_than_once() {
164 // We just use `bool.guard` as the name here because it will be inlined
165 assert_erl!(
166 (
167 "gleam_stdlib",
168 "gleam/bool",
169 "
170pub fn guard(something) {
171 case something {
172 True -> something
173 False -> False
174 }
175}
176"
177 ),
178 "
179import gleam/bool
180
181pub fn main() {
182 bool.guard(True)
183}
184"
185 );
186}
187
188#[test]
189fn do_not_inline_parameters_that_have_side_effects() {
190 assert_erl!(
191 ("gleam_stdlib", "gleam/result", RESULT_MODULE),
192 r#"
193import gleam/result
194
195pub fn main() {
196 result.map(Ok(10), do_side_effects())
197}
198
199fn do_side_effects() {
200 let function = fn(x) { x + 1 }
201 panic as "Side effects"
202 function
203}
204"#
205 );
206}
207
208#[test]
209fn inline_anonymous_function_call() {
210 assert_erl!(
211 "
212pub fn main() {
213 fn(a, b) { #(a, b) }(42, False)
214}
215"
216 );
217}
218
219#[test]
220fn inline_anonymous_function_in_pipe() {
221 assert_erl!(
222 "
223pub fn main() {
224 1 |> fn(x) { x + 1 } |> fn(y) { y * y }
225}
226"
227 );
228}
229
230#[test]
231fn inline_function_capture_in_pipe() {
232 // The function capture is desugared to an anonymous function, so it should
233 // be turned into a direct call to `add`
234 assert_erl!(
235 "
236pub fn main() {
237 1 |> add(4, _)
238}
239
240fn add(a, b) { a + b }
241"
242 );
243}
244
245#[test]
246fn inlining_works_through_blocks() {
247 assert_erl!(
248 "
249pub fn main() {
250 { fn(x) { Ok(x + 1) } }(41)
251}
252"
253 );
254}
255
256#[test]
257fn blocks_get_preserved_when_needed() {
258 assert_erl!(
259 "
260pub fn main() {
261 { 4 |> make_adder }(6)
262}
263
264fn make_adder(a) {
265 fn(b) { a + b }
266}
267"
268 );
269}