Skip to content

Commit 0135550

Browse files
authored
Justifying simplification of folds
1 parent 23a218c commit 0135550

File tree

1 file changed

+27
-7
lines changed

1 file changed

+27
-7
lines changed

RFCs/FS-1152-For-loop-with-accumulation.md

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
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-
61
# F# RFC FS-1152 - Fold loops
72

83
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".
@@ -25,6 +20,23 @@ Extending the `for <pat> in <expr> do` syntax, a new optional clause with an acc
2520

2621
# Motivation
2722

23+
Why `fold`s in particular instead of coming up with a proposal that improves on more collection functions? Let's see how much `fold` is used compared to other collection functions:
24+
- `map`: [176k files](https://github.com/search?type=code&q=language%3Afsharp+map) (includes the `Map` type)
25+
- `iter`: [103k files](https://github.com/search?type=code&q=language%3Afsharp+iter)
26+
- **`fold`: [90.1k files](https://github.com/search?type=code&q=language%3Afsharp+fold)**
27+
- `choose`: [40.7k files](https://github.com/search?type=code&q=language%3Afsharp+choose)
28+
- `collect`: 26.1k files = [106k files](https://github.com/search?type=code&q=language%3Afsharp+collect) raw - [79.9k files](https://github.com/search?type=code&q=language%3Afsharp+collection) for `System.Collections`
29+
- `reduce`: [17.8k files](https://github.com/search?type=code&q=language%3Afsharp+reduce)
30+
- `scan`: [14.8k files](https://github.com/search?type=code&q=language%3Afsharp+scan)
31+
- `map2`: [10k files](https://github.com/search?type=code&q=language%3Afsharp+map2)
32+
- `map3`: [10k files](https://github.com/search?type=code&q=language%3Afsharp+map3)
33+
- `fold2`: [2.9k files](https://github.com/search?type=code&q=language%3Afsharp+fold2)
34+
- `mapFold`: [2.3k files](https://github.com/search?type=code&q=language%3Afsharp+mapFold)
35+
36+
`fold` stands out in terms of actual usage - and yet it has an abysmal syntax. Other highly common functions like `map`, `choose`, `collect` already have a nice syntax, and yet `fold` does not.
37+
38+
Moreover, `fold` is a fundamental operation in pure functional programming because it is a general-purpose function that can define many other collection functions like `map`, `reduce`, and `choose`, just like how `iter` corresponds to the imperative `for` loop. This suggests an opportunity to improve on the usage of `fold`s to make them palatable.
39+
2840
Let's start with how this is used in practice - the summarization of a sequence into a value, aka the fold operation, compared across different language features.
2941

3042
```fs
@@ -346,9 +358,17 @@ let sum xs = fold 0 { for x in xs -> (+) x } // variation 1
346358
let sum xs = fold 0 { for acc, x in xs -> acc + x } // variation 2
347359
let sum xs = fold { for acc, x in 0, xs -> acc + x } // variation 3
348360
```
349-
Generally, computation expressions have hard to understand error messages for overloaded computation expression methods and are are [notoriously difficult to debug](https://github.com/dotnet/fsharp/issues/13342). These are fixable with enough investments in CEs.
361+
The variations that abuse tuple syntax don't help diagnose tuple ordering or placement.
362+
363+
One can even argue that computation expressions offer the opportunity to not just improve on folds but also other forms like `reduce` and `scan`, with support for custom operation keywords:
364+
```fs
365+
let sum xs = fold 0 { for x in xs -> accumulator + x } // 'accumulator' being a custom operation keyword
366+
```
367+
This specific example does not play well with tuple accumulators unless F# adds tuple member access by index.
368+
369+
Generally, computation expressions have hard to understand error messages for overloaded computation expression methods and are are [notoriously difficult to debug](https://github.com/dotnet/fsharp/issues/13342). These are fixable with enough investments in CEs, but the Microsoft F# team size got reduced from 6 to 2 + Copilot on March 2025, this work takes much much longer than just adding a `with` clause to `for` loops.
350370

351-
What's unfixable is the non-orthogonality to an existing computation expression context unlike the for loop which allows `yield` inside to yield to an outer list expression instead of being limited by a fold CE. Moreover, CEs also show a heavily different syntax compared to for loops and folds which add hinderance to understanding - each CE usage requires following CE methods which add indirection to understanding, only hiding large chunks of logic like `async` or `task` should worth CE usage. It would be much simpler to just write the underlying code (this critique also applies to `option` and `result` CEs for example). Since folds are so common in pure functional programming, they deserve a simple-to-use syntax as fold loops provide, instead of tucked away in hard-to-understand CE syntax.
371+
What's unfixable is that CEs also show a heavily different syntax compared to `for` loops and folds which add hinderance to understanding - each CE usage requires following CE methods which add indirection to understanding, only hiding large chunks of logic like `async` or `task` should worth CE usage. It would be much simpler to just write the underlying code (this critique also applies to `option` and `result` CEs for example). Since folds are so common in pure functional programming, they deserve a simple-to-use syntax as fold loops provide, instead of tucked away in hard-to-understand CE syntax.
352372

353373
## Summary
354374

0 commit comments

Comments
 (0)