diff options
-rw-r--r-- | src/gleam/iterator.gleam | 41 | ||||
-rw-r--r-- | src/gleam/list.gleam | 9 | ||||
-rw-r--r-- | test/gleam/iterator_test.gleam | 33 |
3 files changed, 77 insertions, 6 deletions
diff --git a/src/gleam/iterator.gleam b/src/gleam/iterator.gleam index 2421657..30fdcf9 100644 --- a/src/gleam/iterator.gleam +++ b/src/gleam/iterator.gleam @@ -1078,3 +1078,44 @@ pub fn fold_until( iterator.continuation |> do_fold_until(f, initial) } + +fn do_try_fold( + over continuation: fn() -> Action(a), + with f: fn(a, acc) -> Result(acc, err), + from accumulator: acc, +) -> Result(acc, err) { + case continuation() { + Stop -> Ok(accumulator) + Continue(elem, next) -> { + try accumulator = f(elem, accumulator) + do_try_fold(next, f, accumulator) + } + } +} + +/// A variant of fold that might fail. +/// +/// +/// The folding function should return `Result(accumulator, error) +/// If the returned value is `Ok(accumulator)` try_fold will try the next value in the list. +/// If the returned value is `Error(error)` try_fold will stop and return that error. +/// +/// ## Examples +/// +/// > [1, 2, 3, 4] +/// > |> iterator.from_list() +/// > |> try_fold(0, fn(i, acc) { +/// > case i < 3 { +/// > True -> Ok(acc + i) +/// > False -> Error(Nil) +/// > } +/// > }) +/// Error(Nil) +/// +pub fn try_fold( + over iterator: Iterator(e), + from initial: acc, + with f: fn(e, acc) -> Result(acc, err), +) -> Result(acc, err) { + do_try_fold(iterator.continuation, f, initial) +} diff --git a/src/gleam/list.gleam b/src/gleam/list.gleam index 62e69f9..5cfe4a5 100644 --- a/src/gleam/list.gleam +++ b/src/gleam/list.gleam @@ -614,11 +614,10 @@ pub fn try_fold( ) -> Result(b, e) { case collection { [] -> Ok(accumulator) - [first, ..rest] -> - case fun(first, accumulator) { - Ok(next_accumulator) -> try_fold(rest, next_accumulator, fun) - Error(err) -> Error(err) - } + [first, ..rest] -> { + try accumulator = fun(first, accumulator) + try_fold(rest, accumulator, fun) + } } } diff --git a/test/gleam/iterator_test.gleam b/test/gleam/iterator_test.gleam index 8de4251..84292e4 100644 --- a/test/gleam/iterator_test.gleam +++ b/test/gleam/iterator_test.gleam @@ -430,7 +430,7 @@ pub fn interleave_test() { |> should.equal([1, 100, 2, 3, 4]) } -// a |> from_list |> fold_until(a, f) == a |> list.fold_until(_, a, f) +// a |> from_list |> fold_until(acc, f) == a |> list.fold_until(acc, f) pub fn fold_until_test() { let test = fn(subject, acc, f) { subject @@ -455,3 +455,34 @@ pub fn fold_until_test() { |> iterator.fold_until([], f) |> should.equal([5, 4, 3, 2, 1]) } + +// a |> from_list |> try_fold(acc, f) == a |> list.try_fold(acc, f) +pub fn try_fold_test() { + let test = fn(subject, acc, fun) { + subject + |> iterator.from_list() + |> iterator.try_fold(acc, fun) + |> should.equal(list.try_fold(subject, acc, fun)) + } + + let f = fn(e, acc) { + case e % 2 { + 0 -> Ok(e + acc) + _ -> Error("tried to add an odd number") + } + } + test([], 0, f) + test([2, 4, 6], 0, f) + test([1, 2, 3], 0, f) + test([1, 2, 3, 4, 5, 6, 7, 8], 0, f) + + [0, 2, 4, 6] + |> iterator.from_list() + |> iterator.try_fold(0, f) + |> should.equal(Ok(12)) + + [1, 2, 3, 4] + |> iterator.from_list() + |> iterator.try_fold(0, f) + |> should.equal(Error("tried to add an odd number")) +} |