use core::marker::PhantomData;
use crate::combinator::noop1;
use crate::constant1;
use crate::higher::Higher;
use crate::invariant::Invariant;
#[inline]
pub fn lift<FA, B, F>(mut f: F) -> impl FnMut(FA) -> FA::Target<B>
where
FA: Functor<B>,
F: FnMut(FA::Param) -> B,
{
move |fa| fa.map(&mut f)
}
pub trait Functor<B>: Invariant<B> {
fn map(self, f: impl FnMut(Self::Param) -> B) -> Self::Target<B>;
#[inline]
fn fmap<F>(self, f: F) -> Self::Target<B>
where
F: FnMut(Self::Param) -> B,
Self: Sized,
{
self.map(f)
}
#[inline]
fn fproduct<F>(self, mut f: F) -> Self::Target<(Self::Param, B)>
where
F: FnMut(&Self::Param) -> B,
Self: Functor<(<Self as Higher>::Param, B)> + Sized,
{
self.map(|a| {
let rhs = f(&a);
(a, rhs)
})
}
#[inline]
fn fproduct_left<F>(self, mut f: F) -> Self::Target<(B, Self::Param)>
where
F: FnMut(&Self::Param) -> B,
Self: Functor<(B, <Self as Higher>::Param)> + Sized,
{
self.map(|a| (f(&a), a))
}
#[inline]
fn map_const(self, b: B) -> Self::Target<B>
where
B: Copy,
Self: Functor<B> + Sized,
{
self.map(constant1!(b))
}
#[inline]
fn void(self) -> Self::Target<()>
where
Self: Functor<(), Target<()> = <Self as Higher>::Target<B>> + Sized,
{
self.map(noop1)
}
#[inline]
fn tuple_left(self, b: B) -> Self::Target<(B, Self::Param)>
where
B: Copy,
Self: Functor<(B, <Self as Higher>::Param)> + Sized,
{
self.map(|a| (b, a))
}
#[inline]
fn tuple_right(self, b: B) -> Self::Target<(Self::Param, B)>
where
B: Copy,
Self: Functor<(<Self as Higher>::Param, B)> + Sized,
{
self.map(|a| (a, b))
}
#[inline]
fn unzip<A>(self) -> (Self::Target<A>, Self::Target<B>)
where
Self: Higher<Param = (A, B)> + Functor<A> + Functor<B> + Copy + Sized,
{
(self.map(|x| x.0), self.map(|x| x.1))
}
#[inline]
fn if_f<T, F>(self, mut if_true: T, mut if_false: F) -> Self::Target<B>
where
T: FnMut() -> B,
F: FnMut() -> B,
Self: Functor<B, Param = bool> + Sized,
{
self.map(|x| if x { if_true() } else { if_false() })
}
}
#[macro_export]
macro_rules! functor_iter {
($name:ident) => {
impl<A, B> $crate::functor::Functor<B> for $name<A> {
#[inline]
fn map(self, f: impl FnMut(A) -> B) -> Self::Target<B> {
self.into_iter().map(f).collect::<$name<B>>()
}
}
};
($name:ident, $ct:tt $(+ $dt:tt )*) => {
impl<A, B: $ct $(+ $dt )*> $crate::functor::Functor<B> for $name<A> {
#[inline]
fn map(self, f: impl FnMut(A) -> B) -> Self::Target<B> {
self.into_iter().map(f).collect::<$name<B>>()
}
}
};
}
impl<A, B> Functor<B> for PhantomData<A> {
#[inline]
fn map(self, _f: impl FnMut(A) -> B) -> PhantomData<B> {
PhantomData
}
}
impl<A, B> Functor<B> for Option<A> {
#[inline]
fn map(self, f: impl FnMut(A) -> B) -> Option<B> {
self.map(f)
}
}
impl<A, B, E> Functor<B> for Result<A, E> {
#[inline]
fn map(self, f: impl FnMut(A) -> B) -> Result<B, E> {
self.map(f)
}
}
if_std! {
use std::boxed::Box;
use std::collections::*;
use std::hash::Hash;
use std::vec::Vec;
impl<A, B> Functor<B> for Box<A> {
#[inline]
fn map(self, mut f: impl FnMut(A) -> B) -> Box<B> {
Box::new(f(*self))
}
}
functor_iter!(Vec);
functor_iter!(LinkedList);
functor_iter!(VecDeque);
functor_iter!(BinaryHeap, Ord);
functor_iter!(BTreeSet, Ord);
functor_iter!(HashSet, Eq + Hash);
impl<A, B, K: Eq + Hash> Functor<B> for HashMap<K, A> {
#[inline]
fn map(self, mut f: impl FnMut(A) -> B) -> HashMap<K, B> {
self.into_iter().map(|(k, v)| (k, f(v))).collect()
}
}
}