use crate::cons::types::hcons::HNilSeed;
use crate::cons::{Cons, ConsCell, Extend, Nil};
pub trait MapFn<Input> {
type Output;
fn map(input: Input) -> Self::Output;
}
pub trait MapFnsInto<F, NilSeed>: ConsCell {
type Output: ConsCell;
fn map_recursive_into(self) -> Self::Output;
}
impl<F, C: Cons, NilSeed> MapFnsInto<F, NilSeed> for C
where
F: MapFn<C::Head>,
C::Tail: MapFnsInto<F, NilSeed>,
<C::Tail as MapFnsInto<F, NilSeed>>::Output: Extend<<F as MapFn<C::Head>>::Output>,
{
type Output = <<C::Tail as MapFnsInto<F, NilSeed>>::Output as Extend<
<F as MapFn<C::Head>>::Output,
>>::Extension;
#[inline(always)]
fn map_recursive_into(self) -> Self::Output {
let (head, tail) = self.split();
Self::Output::new(
<F as MapFn<C::Head>>::map(head),
<C::Tail as MapFnsInto<F, NilSeed>>::map_recursive_into(tail),
)
}
}
impl<F, InNilSeed, OutNilSeed> MapFnsInto<F, OutNilSeed> for Nil<InNilSeed> {
type Output = Nil<OutNilSeed>;
#[inline(always)]
fn map_recursive_into(self) -> Self::Output {
Self::Output::new()
}
}
pub trait MapFns<F>: ConsCell {
type Output: ConsCell;
fn map_recursive(self) -> Self::Output;
}
impl<F, C: MapFnsInto<F, HNilSeed>> MapFns<F> for C {
type Output = <C as MapFnsInto<F, HNilSeed>>::Output;
#[inline(always)]
fn map_recursive(self) -> Self::Output {
self.map_recursive_into()
}
}
pub trait Map: ConsCell {
#[inline(always)]
fn map<F>(self) -> <Self as MapFns<F>>::Output
where
Self: MapFns<F>,
{
<Self as MapFns<F>>::map_recursive(self)
}
#[inline(always)]
fn map_into<F, NilSeed>(self) -> <Self as MapFnsInto<F, NilSeed>>::Output
where
Self: MapFnsInto<F, NilSeed>,
{
<Self as MapFnsInto<F, NilSeed>>::map_recursive_into(self)
}
}
impl<C: ConsCell> Map for C {}
#[cfg(test)]
mod tests {
use super::*;
use crate::cons::types::hcons::HNil;
struct ToUsize {}
impl<T: Into<usize>> MapFn<T> for ToUsize {
type Output = usize;
fn map(input: T) -> usize {
input.into()
}
}
struct DoubleUsize {}
impl MapFn<usize> for DoubleUsize {
type Output = usize;
fn map(input: usize) -> Self::Output {
input * 2
}
}
#[test]
fn it_works() {
let x = HNil::new().extend(1u8).extend(2u16);
let y = x.map::<ToUsize>().map::<DoubleUsize>();
assert_eq!(y.head(), &4usize);
assert_eq!(y.tail().head(), &2usize);
}
fn map_to_usize<C: Cons>(c: C) -> <C as MapFns<ToUsize>>::Output
where
C: MapFns<ToUsize>,
{
c.map::<ToUsize>()
}
#[test]
fn it_works_generic() {
let x = HNil::new().extend(1u8).extend(2u16);
let y = map_to_usize(x);
assert_eq!(y.head(), &2usize);
assert_eq!(y.tail().head(), &1usize);
}
fn map_to_usize_then_double<C: Cons>(
c: C,
) -> <<C as MapFns<ToUsize>>::Output as MapFns<DoubleUsize>>::Output
where
C: MapFns<ToUsize>,
<C as MapFns<ToUsize>>::Output: MapFns<DoubleUsize>,
{
c.map::<ToUsize>().map::<DoubleUsize>()
}
#[test]
fn it_works_generic_chained() {
let x = HNil::new().extend(1u8).extend(2u16);
let y = map_to_usize_then_double(x);
assert_eq!(y.head(), &4usize);
assert_eq!(y.tail().head(), &2usize);
}
}