diff options
-rw-r--r-- | CHANGELOG.md | 1 | ||||
-rw-r--r-- | src/gleam/iterator.gleam | 6 | ||||
-rw-r--r-- | test/gleam/iterator_test.gleam | 20 |
3 files changed, 23 insertions, 4 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 0754990..84a76b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - The `bit_array` module gains the `compare` function. - The `float` modeule gains the `to_precision` function. +- The `try_fold` function in the `iterator` module is now tail recursive. ## v0.40.0 - 2024-08-19 diff --git a/src/gleam/iterator.gleam b/src/gleam/iterator.gleam index ee38bdc..336c0a1 100644 --- a/src/gleam/iterator.gleam +++ b/src/gleam/iterator.gleam @@ -1467,8 +1467,10 @@ fn do_try_fold( case continuation() { Stop -> Ok(accumulator) Continue(elem, next) -> { - use accumulator <- result.try(f(accumulator, elem)) - do_try_fold(next, f, accumulator) + case f(accumulator, elem) { + Ok(result) -> do_try_fold(next, f, result) + Error(_) as error -> error + } } } } diff --git a/test/gleam/iterator_test.gleam b/test/gleam/iterator_test.gleam index 7c3498b..6ae1d4c 100644 --- a/test/gleam/iterator_test.gleam +++ b/test/gleam/iterator_test.gleam @@ -4,6 +4,17 @@ import gleam/iterator.{Done, Next} import gleam/list import gleam/should +@target(erlang) +const recursion_test_cycles = 1_000_000 + +// JavaScript engines crash when exceeding a certain stack size: +// +// - Chrome 106 and NodeJS V16, V18, and V19 crash around 10_000+ +// - Firefox 106 crashes around 35_000+. +// - Safari 16 crashes around 40_000+. +@target(javascript) +const recursion_test_cycles = 40_000 + // a |> from_list |> to_list == a pub fn to_from_list_test() { let testcase = fn(subject) { @@ -505,7 +516,7 @@ pub fn any_test() { // TCO test iterator.repeat(1) - |> iterator.take(1_000_000) + |> iterator.take(recursion_test_cycles) |> iterator.any(satisfying: fn(n) { n % 2 == 0 }) |> should.be_false } @@ -525,7 +536,7 @@ pub fn all_test() { // TCO test iterator.repeat(0) - |> iterator.take(1_000_000) + |> iterator.take(recursion_test_cycles) |> iterator.all(satisfying: fn(n) { n % 2 == 0 }) |> should.be_true } @@ -641,6 +652,11 @@ pub fn try_fold_test() { |> iterator.from_list() |> iterator.try_fold(0, f) |> should.equal(Error("tried to add an odd number")) + + // TCO test + iterator.repeat(1) + |> iterator.take(recursion_test_cycles) + |> iterator.try_fold(0, fn(e, acc) { Ok(e + acc) }) } pub fn first_test() { |