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: RFCs/FS-1152-For-loop-with-accumulation.md
+52-2Lines changed: 52 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,3 +1,8 @@
1
+
To do:
2
+
- Justification of the fundamentality of folds by counting usage - why improve folds specifically?
3
+
- More on dismissing CE alternative
4
+
- Looks too similar to a for loop?
5
+
1
6
# F# RFC FS-1152 - Fold loops
2
7
3
8
The design suggestion ["for-with" syntactic sugar for folds](https://github.com/fsharp/fslang-suggestions/issues/1362) has not yet been marked "approved in principle".
@@ -442,6 +447,51 @@ for <pattern_enumeration_item> in <expression_sequence> with <pattern_accumulato
442
447
443
448
This syntax analysis reveals an insight that the fold loop behaves identically to the `for` loop `with` accumulation. Note that the design process started from `fold` functions, then used an overload on `for`, then naturally evolved to an extension on existing `for` loops. We did not force the idea of folding onto an extension of existing `for` loops, instead maintaining the orthogonality between fold loops and existing usages of the `for` keyword. This shows the design of an extension on the `for` loop is coincidental from overloading the `for` keyword, instead of tacking on the meaning of `fold` as with an extension of the `for` loop as the starting point - an important semantic difference.
444
449
450
+
## Doesn't this look too similar to a regular `for` loop despite the differences?
451
+
452
+
tl;dr Yes, and this is intentional - it reveals a fundamental unification.
453
+
454
+
On the surface, this proposal is overloading the `for` keyword into two forms, one that evaluates to unit and another new form that actually evaluates to a real value, which is final accumulator state, while threading the accumulator each iteration. It seems a bit surprising that the `with` keyword and accumulator state changes the type of the whole for expression. Adding a `with` clause might seem like a superficial change to regular `for` loops.
455
+
456
+
However, this syntax highlights a profound realization: traditional `for` loops are actually a specialized case of fold loops with a unit accumulator.
457
+
458
+
```fsharp
459
+
// Traditional for loop
460
+
for x in xs do
461
+
printfn $"{x}"
462
+
463
+
// Is equivalent to:
464
+
for x in xs with _ = () do
465
+
printfn $"{x}" // Body must return unit
466
+
```
467
+
468
+
where the accumulator binding is discarded `_`, with its initial value set to the unit value `()`, and the new accumulator value is the value of the loop body which can only be the unit value `()`.
469
+
470
+
This is consistent in two ways:
471
+
- For discards
472
+
```fsharp
473
+
// Regular expression in do position
474
+
do 1 // Warning: result implicitly ignored
475
+
476
+
// Traditional for loop
477
+
for x in xs do 1 // Warning: result implicitly ignored
478
+
479
+
// Unit-accumulator fold loop
480
+
for x in xs with () = () do 1 // Same warning
481
+
```
482
+
483
+
- For types
484
+
```fsharp
485
+
let y = 1 // y : int
486
+
487
+
// Non-unit fold loop
488
+
for x in xs with y = 1 do
489
+
y + x // accumulator : int
490
+
```
491
+
The `with` clause changes the loop body from an implicit `unit -> unit` (traditional loop) to arbitrary `'State -> 'State` (fold loop). The presence of the `with` clause doesn't just add an accumulator - it adds flexibility to the loop from a side-effect-only construct (`unit -> unit`) to a value-producing operation (`'State -> 'State`).
492
+
493
+
This explains why the loop's return type changes: it's not an arbitrary overload but a natural consequence of the accumulator type. The syntactic similarity is actually a strength - it reveals the deep connection between iteration and accumulation that was previously obscured.
494
+
445
495
## Are there better alternatives to `with` keyword?
446
496
447
497
tl;dr No.
@@ -468,7 +518,7 @@ for x in xs mutable s = init do ... // "mutable" isn't used without "let" an
468
518
469
519
## Can the accumulator initializer be simplified?
470
520
471
-
tl;dr No.
521
+
tl;dr No, it's technically infeasible.
472
522
473
523
Some may also suggest that re-declaring the accumulator initial state is redundant in the case of folding over different sequences one after another.
474
524
```fs
@@ -478,7 +528,7 @@ However, a direct abbreviation from `effects, model = effects, model` to `effect
478
528
479
529
## Can it be used with computation expressions?
480
530
481
-
**tl;dr No.** The fold loop body cannot contain computation expression operations, as it's designed as a pure accumulation expression. This maintains consistency with how subexpressions work throughout F#.
531
+
tl;dr No. The fold loop body cannot contain computation expression operations, as it's designed as a pure accumulation expression. This maintains consistency with how subexpressions work throughout F#.
0 commit comments