use crate::{Cons, HList, Nil};
use super::{FoldFn, Folder};
pub trait RFold<Accumulator, Folder>: HList {
fn rfold(self, init: Accumulator, folder: Folder) -> Accumulator;
}
impl<A, F> RFold<A, F> for Nil {
fn rfold(self, init: A, _: F) -> A {
init
}
}
impl<A, F, Head, Tail> RFold<A, F> for Cons<Head, Tail>
where
F: FnMut(A, Head) -> A,
Tail: RFoldWithFolder<A, F>,
{
fn rfold(self, init: A, folder: F) -> A {
let Cons(head, tail) = self;
let (init, mut folder) = tail.rfold_with_folder(init, folder);
folder(init, head)
}
}
impl<A, FHead, FTail, Head, Tail> RFold<A, Cons<FHead, FTail>> for Cons<Head, Tail>
where
FHead: FnOnce(A, Head) -> A,
Tail: RFold<A, FTail>,
{
fn rfold(self, init: A, folder: Cons<FHead, FTail>) -> A {
let Cons(head, tail) = self;
let Cons(folder_head, folder_tail) = folder;
let init = tail.rfold(init, folder_tail);
folder_head(init, head)
}
}
impl<A, F, Head, Tail> RFold<A, Folder<F>> for Cons<Head, Tail>
where
F: FoldFn<A, Head>,
Tail: RFoldWithFolder<A, Folder<F>>,
{
fn rfold(self, init: A, folder: Folder<F>) -> A {
let Cons(head, tail) = self;
let (init, mut folder) = tail.rfold_with_folder(init, folder);
folder.fold(init, head)
}
}
trait RFoldWithFolder<Accumulator, Folder>: HList {
fn rfold_with_folder(self, init: Accumulator, folder: Folder) -> (Accumulator, Folder);
}
impl<A, F> RFoldWithFolder<A, F> for Nil {
fn rfold_with_folder(self, init: A, folder: F) -> (A, F) {
(init, folder)
}
}
impl<A, F, Head, Tail> RFoldWithFolder<A, F> for Cons<Head, Tail>
where
F: FnMut(A, Head) -> A,
Tail: RFoldWithFolder<A, F>,
{
fn rfold_with_folder(self, init: A, folder: F) -> (A, F) {
let Cons(head, tail) = self;
let (init, mut folder) = tail.rfold_with_folder(init, folder);
let init = folder(init, head);
(init, folder)
}
}
impl<A, F, Head, Tail> RFoldWithFolder<A, Folder<F>> for Cons<Head, Tail>
where
F: FoldFn<A, Head>,
Tail: RFoldWithFolder<A, Folder<F>>,
{
fn rfold_with_folder(self, init: A, folder: Folder<F>) -> (A, Folder<F>) {
let Cons(head, tail) = self;
let (init, mut folder) = tail.rfold_with_folder(init, folder);
let init = folder.fold(init, head);
(init, folder)
}
}