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
This PR updates the documentation for the main imperative loop
constructs provided by Gerbil: **do, do-while, while,** and **until.**
Each entry is organized into the following logical sections:
1. **Signature:** The full technical signature of the form.
2. **Summary:** A single, high-level sentence explaining its core
purpose.
3. **Description:** A detailed explanation of its behavior, arguments,
return value, and edge cases.
4. **Example(s):** Practical, self-contained examples demonstrating
common use cases.
5. **Context and Usage:** A crucial section explaining *why* and *when*
to use this form compared to alternatives.
6. **See Also:** Links to related forms to improve discoverability.
Note: English is not my native language (I’m a French speaker). The text
was written with the help of Gemini and Google Translate.
Copy file name to clipboardExpand all lines: doc/reference/gerbil/core/expression.md
+168-6Lines changed: 168 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -192,22 +192,184 @@ concurrent evaluation in a multiple threads; it will also support failures and e
192
192
the thunk, and will issue an error if someone attempts to reenter such escaped thunks.
193
193
194
194
## do
195
-
TODO
195
+
```scheme
196
+
(do ((var init step ...) ...)
197
+
(test result ...)
198
+
command ...)
199
+
```
200
+
201
+
The `do` macro is Gerbil's most general and powerful imperative looping construct. It allows for the creation of loops with multiple, interdependent state variables.
202
+
203
+
The loop works as follows:
204
+
1. First, each `var` is bound to its respective `init` value.
205
+
2. At the beginning of each iteration, the `test` is evaluated.
206
+
3. If `test` is truthy, the `result` expressions are evaluated, and the value of the last `result` expression is returned. If no `result` expressions are provided, a void value is returned.
207
+
4. If `test` is false, the `command` expressions in the loop body are evaluated in order.
208
+
5. Finally, the `step` expressions for each variable are evaluated. All `step` expressions are computed using the variable values from the previous iteration. After all steps are computed, the variables are simultaneously updated with their new values, and the loop proceeds to the next iteration (starting again at step 2).
209
+
210
+
::: tip Examples
211
+
212
+
**- Initializing a vector:**
213
+
this example uses a counter `i` to fill a pre-allocated vector. The loop body is used for the side effect of setting the vector's elements.
214
+
215
+
```scheme
216
+
;; Create a 5-element vector and fill it with values 0 through 4.
217
+
(do ((vec (make-vector 5))
218
+
(i 0 (+ i 1)))
219
+
((= i 5) vec)
220
+
(vector-set! vec i i))
221
+
222
+
;; => #(0 1 2 3 4)
223
+
```
224
+
225
+
**- Reversing a list:**
226
+
this example shows how `do` can build a result without a loop body. The logic is entirely contained in the variable updates and the termination clause.
227
+
228
+
```scheme
229
+
;; Reverse the list '(a b c d).
230
+
(do ((source '(a b c d) (cdr source))
231
+
(result '() (cons (car source) result)))
232
+
((null? source) result))
233
+
234
+
;; => '(d c b a)
235
+
```
236
+
:::
237
+
238
+
### Context and Usage
239
+
240
+
`do` is the appropriate tool for complex, stateful iteration.
241
+
242
+
* Compared to `while` and `until`, which are ideal for simple loops based on a single condition, `do` excels when you need to manage multiple state variables that update in parallel on each iteration.
243
+
* Compared to functional iteration constructs like `foldl` or `map`, `do` should be used when you specifically need imperative control, side effects in the loop body, or when the state updates are too complex to fit elegantly into a fold operation.
244
+
245
+
### See Also
246
+
247
+
-[`while`](#while)
248
+
-[`until`](#until)
249
+
-`foldl`
250
+
-[`do-while`](#do-while)
196
251
197
252
## do-while
198
-
TODO
253
+
```scheme
254
+
(do-while ((var init step ...) ...)
255
+
(test result ...)
256
+
command ...)
257
+
=>
258
+
(do ((var init step ...) ...)
259
+
((not test) result ...)
260
+
command ...)
261
+
```
199
262
263
+
Inverted form of [`do`](#do) macro that executes its body **at least once**, and continues to iterate as long as the `test` evaluates to a truthy value.
200
264
201
-
## while
265
+
The `do-while` macro provides a loop that checks its continuation condition **after** each execution of its body. This guarantees that the loop body is always executed at least once.
266
+
267
+
The evaluation order is as follows:
268
+
1. The `var` bindings are established with their `init` values.
269
+
2. The `command` expressions in the body are evaluated in order.
270
+
3. The `test` is evaluated.
271
+
4. If `test` is truthy, the `step` expressions are evaluated to compute the new values for the variables, and the loop continues from step 2.
272
+
5. If `test` is false, the loop terminates. The `result` expressions are then evaluated, and the value of the last one is returned.
273
+
274
+
::: tip Example
275
+
276
+
Processing a message queue, each iteration must first fetch a message from the queue before checking whether to continue.
277
+
278
+
```scheme
279
+
;; read a message, process it, stop when dequeue! returns the sentinel 'no-msg
Use `do-while` for situations where an action must be performed before the condition to continue can be evaluated.
289
+
290
+
* Compared to `while` and `do`, which test the condition before the first iteration, `do-while` is useful when the loop body itself is what produces the value needed for the test (e.g., reading from a file, getting user input, fetching an item from a queue).
291
+
* Like `do`, it is an imperative construct. For functional transformations of lists, prefer higher-order functions like `foldl` or `map`.
292
+
293
+
### See Also
294
+
295
+
-[`do`](#do)
296
+
-[`while`](#while)
297
+
-[`until`](#until)
298
+
299
+
300
+
## while
301
+
```scheme
203
302
(while test body ...)
204
303
```
205
304
206
-
Imperative loop; the body is evaluated while the `test` expression is truthy.
305
+
An imperative loop that executes its `body` repeatedly as long as the `test` expression evaluates to a truthy value.
306
+
307
+
At the beginning of each iteration, the `test` expression is evaluated. If the result is a truthy value (any value other than `#f`), the `body` expressions are executed in order, and the loop repeats. If the `test` expression evaluates to `#f`, the loop terminates immediately.
308
+
309
+
This behavior means the loop body may be executed **zero or more times**. If the initial `test` is false, the body will never be executed. The `while` macro always returns a void value.
310
+
311
+
::: tip Example
312
+
313
+
A simple counter from 0 to 4.
314
+
315
+
```scheme
316
+
(let ((i 0))
317
+
(while (< i 5)
318
+
(print i " ")
319
+
(set! i (+ i 1))))
320
+
321
+
;; => 0 1 2 3 4
322
+
```
323
+
:::
324
+
325
+
### Context and Usage
326
+
327
+
`while` is the ideal construct for simple imperative loops where the condition must be checked before the loop body is executed for the first time.
328
+
329
+
* Compared to [`do-while`](#do-while), which executes its body at least once, `while` may not execute its body at all if the condition is initially false.
330
+
* Compared to [`do`](#do), which is designed to handle multiple, complex loop variables with dedicated update steps, `while` is simpler and better suited for loops controlled by a single condition where state is managed manually within the body (e.g., with `set!`).
331
+
332
+
### See Also
333
+
334
+
-[`do`](#do)
335
+
-[`do-while`](#do-while)
336
+
-[`until`](#until)
337
+
207
338
208
339
## until
340
+
```scheme
341
+
(until test body ...)
342
+
=>
343
+
(while (not test) body ...)
209
344
```
210
-
(util test body ...)
345
+
346
+
An imperative loop that is the semantic inverse of [`while`](#while). It executes its `body` repeatedly until the `test` expression evaluates to a truthy value.
347
+
348
+
At the beginning of each iteration, the `test` expression is evaluated. If the result is `#f`, the `body` expressions are executed in order, and the loop continues. If the `test` expression evaluates to a truthy value (any value other than `#f`), the loop terminates immediately.
349
+
350
+
Like [`while`](#while), the loop body may be executed **zero or more times**. The `until` macro always returns a void value.
351
+
352
+
::: tip Example
353
+
A simple countdown from 5 to 1.
354
+
355
+
```scheme
356
+
(let ((i 5))
357
+
(until (zero? i)
358
+
(print i " ")
359
+
(set! i (- i 1))))
360
+
361
+
;; => 5 4 3 2 1
211
362
```
363
+
:::
364
+
365
+
### Context and Usage
366
+
367
+
`until` is a stylistic alternative to [`while`](#while) that improves code readability when the loop's logic is defined by its termination condition rather than its continuation condition.
368
+
369
+
Use `until` when you want to emphasize the condition that stops the loop, as it can make the code's intent more self-evident.
370
+
371
+
### See Also
212
372
213
-
Imperative loop; the body is evaluated until the `test` expression is truthy.
0 commit comments