use frunk::{
hlist::{HMappable, HZippable},
prelude::HList,
Generic, HCons, HNil, LabelledGeneric,
};
pub use self::cons_list::{ConsList, ConsListT};
pub mod cons_list;
pub struct Poly<F>(pub F);
pub trait Func<I> {
type Output;
fn call(&mut self, i: I) -> Self::Output;
}
impl<F: Func<Head>, Head, Tail: HMappable<Poly<F>>> HMappable<Poly<F>> for HCons<Head, Tail> {
type Output = HCons<<F as Func<Head>>::Output, <Tail as HMappable<Poly<F>>>::Output>;
fn map(self, mut mapper: Poly<F>) -> Self::Output {
let HCons { head, tail } = self;
HCons {
head: mapper.0.call(head),
tail: tail.map(mapper),
}
}
}
pub trait WithGeneric: Generic {
fn hmap<U: Generic, F>(self, f: F) -> U
where
Self::Repr: HMappable<Poly<F>, Output = U::Repr>;
fn hzip<U: Generic, TU: Generic<Repr = <Self::Repr as HZippable<U::Repr>>::Zipped>>(
self,
other: U,
) -> TU
where
Self::Repr: HZippable<U::Repr>;
fn map_to_list<F, U>(self, f: F) -> ConsList<U, <Self::Repr as MapToList<F, U>>::Output>
where
Self::Repr: MapToList<F, U>;
fn for_each<F>(self, f: F)
where
Self::Repr: ForEach<F>;
}
impl<T: Generic> WithGeneric for T {
fn hmap<U: Generic, F>(self, f: F) -> U
where
Self::Repr: HMappable<Poly<F>, Output = U::Repr>,
{
Generic::from(Generic::into(self).map(Poly(f)))
}
fn hzip<U: Generic, TU: Generic<Repr = <Self::Repr as HZippable<U::Repr>>::Zipped>>(
self,
other: U,
) -> TU
where
Self::Repr: HZippable<U::Repr>,
{
Generic::from(Generic::into(self).zip(Generic::into(other)))
}
fn map_to_list<F, U>(self, f: F) -> ConsList<U, <Self::Repr as MapToList<F, U>>::Output>
where
Self::Repr: MapToList<F, U>,
{
Generic::into(self).map_to_list(f)
}
fn for_each<F>(self, f: F)
where
Self::Repr: ForEach<F>,
{
Generic::into(self).for_each(f)
}
}
pub trait WithLabelledGeneric: LabelledGeneric {
fn hmap<U: LabelledGeneric, F>(self, f: F) -> U
where
Self::Repr: HMappable<Poly<F>, Output = U::Repr>;
fn hzip<
U: LabelledGeneric,
TU: LabelledGeneric<Repr = <Self::Repr as HZippable<U::Repr>>::Zipped>,
>(
self,
other: U,
) -> TU
where
Self::Repr: HZippable<U::Repr>;
fn map_to_list<F, U>(self, f: F) -> ConsList<U, <Self::Repr as MapToList<F, U>>::Output>
where
Self::Repr: MapToList<F, U>;
fn for_each<F>(self, f: F)
where
Self::Repr: ForEach<F>;
}
impl<T: LabelledGeneric> WithLabelledGeneric for T {
fn hmap<U: LabelledGeneric, F>(self, f: F) -> U
where
Self::Repr: HMappable<Poly<F>, Output = U::Repr>,
{
LabelledGeneric::from(LabelledGeneric::into(self).map(Poly(f)))
}
fn hzip<
U: LabelledGeneric,
TU: LabelledGeneric<Repr = <Self::Repr as HZippable<U::Repr>>::Zipped>,
>(
self,
other: U,
) -> TU
where
Self::Repr: HZippable<U::Repr>,
{
LabelledGeneric::from(LabelledGeneric::into(self).zip(LabelledGeneric::into(other)))
}
fn map_to_list<F, U>(self, f: F) -> ConsList<U, <Self::Repr as MapToList<F, U>>::Output>
where
Self::Repr: MapToList<F, U>,
{
LabelledGeneric::into(self).map_to_list(f)
}
fn for_each<F>(self, f: F)
where
Self::Repr: ForEach<F>,
{
LabelledGeneric::into(self).for_each(f)
}
}
pub trait MapToList<F, U>: HList {
type Output: ConsListT<U>;
fn map_to_list(self, f: F) -> ConsList<U, Self::Output>;
}
impl<F, U> MapToList<F, U> for HNil {
type Output = cons_list::Nil<U>;
fn map_to_list(self, _f: F) -> ConsList<U, Self::Output> {
ConsList::nil()
}
}
impl<F: Func<Head, Output = U>, U, Head, Tail: MapToList<F, U>> MapToList<F, U>
for HCons<Head, Tail>
{
type Output = cons_list::Cons<U, <Tail as MapToList<F, U>>::Output>;
fn map_to_list(self, mut f: F) -> ConsList<U, Self::Output> {
let HCons { head, tail } = self;
ConsList::cons(f.call(head), tail.map_to_list(f))
}
}
pub trait ForEach<F>: HList {
fn for_each(self, f: F);
}
impl<F> ForEach<F> for HNil {
fn for_each(self, _: F) {}
}
impl<F: Func<Head, Output = ()>, Head, Tail: ForEach<F>> ForEach<F> for HCons<Head, Tail> {
fn for_each(self, mut f: F) {
let HCons { head, tail } = self;
f.call(head);
tail.for_each(f)
}
}