Since my starting point was creating a common base for the internal iterator methods, and then improving forward/reverse correspondance, this is taking me far from the starting point. But so far it seems pretty exciting.
It does seem to work well with carrier, the “fold_ok” implementation would look like this:
/// Starting with initial accumulator `init`, combine the accumulator
/// with each iterator element using the closure `g` until it returns
/// an error or the iterator’s end is reached.
/// The last carrier value is returned.
fn fold_ok<C, G>(&mut self, init: C::Success, mut g: G) -> C
where Self: Sized,
G: FnMut(C::Success, Self::Item) -> C,
C: Carrier,
{
let mut accum = init;
while let Some(elt) = self.next() {
accum = g(accum, elt)?;
}
C::from_success(accum)
}
And let’s look at the complicated example with Take that recurses by switching “monad” instance, and that works with carrier as well. We just use it as a generalized Result, though:
struct Take<I> {
n: usize,
iter: I,
}
enum TakeStop<L, R> {
Their(L),
Our(R),
}
impl<I> Iterator for Take<I> where I: Iterator
{
fn fold_ok<C, G>(&mut self, init: C::Success, mut g: G) -> C
where G: FnMut(C::Success, Self::Item) -> C,
C: Carrier,
{
if self.n == 0 {
return C::from_success(init);
}
let n = &mut self.n;
let result = self.iter.fold_ok(init, move |acc, elt| {
*n -= 1;
match g(acc, elt).translate() {
Err(e) => Err(TakeStop::Their(e)),
Ok(x) => {
if *n == 0 {
Err(TakeStop::Our(x))
} else {
Ok(x)
}
}
}
});
match result {
Err(TakeStop::Their(e)) => C::from_error(e),
Err(TakeStop::Our(x)) => C::from_success(x),
Ok(x) => C::from_success(x)
}
}
}