Skip to content

Commit

Permalink
Add Iter.try_each and Iter.try_reduce
Browse files Browse the repository at this point in the history
These work similar to Iter.each and Iter.reduce respectively, except
they stop at the first Error they encounter.

This fixes #638.

Changelog: added
  • Loading branch information
yorickpeterse committed Nov 4, 2023
1 parent c1a5760 commit 7314fd3
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 0 deletions.
52 changes: 52 additions & 0 deletions std/src/std/iter.inko
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,25 @@ trait pub Iter[T] {
}
}

# Calls the closure for every value in `self`, stopping at the first `Error`
# returned by the closure.
#
# # Examples
#
# let res = [10, 0, 30].into_iter.try_each fn (val) {
# if val > 0 { Result.Ok(nil) } else { Result.Error('test') }
# }
#
# res # => Result.Error('test')
fn pub mut try_each[E](func: fn (T) -> Result[Nil, E]) -> Result[Nil, E] {
loop {
match self.next {
case Some(v) -> try func.call(v)
case _ -> return Result.Ok(nil)
}
}
}

# Calls the closure for every value in `self`, passing an index and the value
# to the closure.
#
Expand Down Expand Up @@ -328,6 +347,39 @@ trait pub Iter[T] {
}
}

# Combines all values in the iterator into the specified accumulator, stopping
# at the first `Error` that is encountered.
#
# This method is similar to `Iter.reduce`, except the given closure is
# expected to return a `Result` wrapping the accumulator. If the return value
# is an `Ok`, iteration continues. If the return value is an `Error`,
# iteration stops and the `Error` is returned.
#
# # Examples
#
# let iter = [Result.Ok(1), Result.Error('test'), Result.Ok(2)].into_iter
# let result = iter.try_reduce(0) fn (acc, val) {
# match val {
# case Ok(val) -> Result.Ok(acc + val)
# case err -> err
# }
# }
#
# result # => Result.Error('test')
fn pub mut try_reduce[A, E](
accumulator: A,
func: fn (A, T) -> Result[A, E],
) -> Result[A, E] {
let mut result = accumulator

loop {
match self.next {
case Some(v) -> result = try func.call(result, v)
case _ -> return Result.Ok(result)
}
}
}

# Returns an `Iter` that produces chunks of values.
#
# Each chunk is up to the amount specified by the `size` argument. If the
Expand Down
32 changes: 32 additions & 0 deletions std/test/std/test_iter.inko
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,38 @@ fn pub tests(t: mut Tests) {
t.equal([10, 20, 30].iter.last, Option.Some(30))
}

t.test('Iter.try_each') fn (t) {
let vals = []
let res = [10, 0, 30].into_iter.try_each fn (val) {
if val > 0 {
vals.push(val)
Result.Ok(nil)
} else {
Result.Error('test')
}
}

t.equal(res, Result.Error('test'))
t.equal(vals, [10])
}

t.test('Iter.try_reduce') fn (t) {
let iter = [Result.Ok(10), Result.Error('test'), Result.Ok(20)].into_iter
let vals = []
let result = iter.try_reduce(0) fn (acc, val) {
match val {
case Ok(val) -> {
vals.push(val)
Result.Ok(acc + val)
}
case err -> err
}
}

t.equal(result, Result.Error('test'))
t.equal(vals, [10])
}

t.test('Stream.new') fn (t) {
let mut idx = 0
let iter = Stream.new fn move {
Expand Down

0 comments on commit 7314fd3

Please sign in to comment.