diff --git a/src/foreach.md b/src/foreach.md index a87781df..d3f874b5 100644 --- a/src/foreach.md +++ b/src/foreach.md @@ -76,7 +76,7 @@ stylistic. We can use the `foreach` syntax to iterate through any collection type that implements the `ForEach` trait. In particular, the `ForEach` trait -defines a single signature: +defines a single signature: ```flix /// @@ -103,3 +103,80 @@ trait ForEach[t] { ``` > **Note:** Flix expects the expression body of a `foreach` to have type `Unit`. + +## ForEach Combinators + +The `ForEach` module provides four combinators that transform how a collection is +iterated: `withIndex`, `withFilter`, `withMap`, and `withZip`. Each combinator +wraps a collection and returns a new `ForEach`-compatible value that can be used +directly with the `foreach` syntax. + +### Iterating with an Index + +The `withIndex` combinator pairs each element with its zero-based index: + +```flix +use ForEach.withIndex; +def main(): Unit \ IO = + let langs = List#{"Flix", "Haskell", "Scala"}; + foreach ((i, lang) <- withIndex(langs)) { + println("${i}: ${lang}") + } +``` + +This prints: + +``` +0: Flix +1: Haskell +2: Scala +``` + +### Filtering Elements + +The `withFilter` combinator skips elements that do not satisfy a predicate: + +```flix +use ForEach.withFilter; +def main(): Unit \ IO = + let numbers = List#{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + foreach (x <- withFilter(x -> x `Int32.modulo` 2 == 0, numbers)) { + println("${x}") + } +``` + +This prints only the even numbers: `2`, `4`, `6`, `8`, `10`. + +### Mapping Elements + +The `withMap` combinator applies a transformation to each element before it is +yielded: + +```flix +use ForEach.withMap; +def main(): Unit \ IO = + let numbers = List#{1, 2, 3, 4, 5}; + foreach (x <- withMap(x -> x * 10, numbers)) { + println("${x}") + } +``` + +This prints `10`, `20`, `30`, `40`, `50`. + +### Zipping Two Collections + +The `withZip` combinator zips two collections element-wise, producing pairs. The +iteration stops when the shorter collection is exhausted: + +```flix +use ForEach.withZip; +def main(): Unit \ IO = + let names = List#{"Alice", "Bob", "Carol"}; + let ages = List#{30, 25, 40}; + foreach ((name, age) <- withZip(names, ages)) { + println("${name} is ${age} years old") + } +``` + +> **Note:** `withZip` requires both collections to implement the `Iterable` +> trait (not just `ForEach`).