use core::marker::PhantomData;
use crate::prelude::*;
pub trait BifunctorOnce<F, A, B>
where F: HKT2<T<A, B> = Self>
{
fn bimap1<A2, B2, FA, FB>(self, fa: FA, fb: FB) -> F::T<A2, B2>
where FA: F1Once<A, Ret = A2>,
FB: F1Once<B, Ret = B2>;
fn lmap1<A2, FA>(self, fa: FA) -> F::T<A2, B>
where Self: Sized,
FA: F1Once<A, Ret = A2>
{
self.bimap1(fa, |b| b)
}
fn rmap1<B2, FB>(self, fb: FB) -> F::T<A, B2>
where Self: Sized,
FB: F1Once<B, Ret = B2>
{
self.bimap1(|a| a, fb)
}
}
pub trait Bifunctor<F, A, B>
where F: HKT2<T<A, B> = Self>
{
fn bimap<A2, B2, FA, FB>(self, fa: FA, fb: FB) -> F::T<A2, B2>
where FA: F1<A, Ret = A2>,
FB: F1<B, Ret = B2>;
fn lmap<A2, FA>(self, fa: FA) -> F::T<A2, B>
where Self: Sized,
FA: F1<A, Ret = A2>
{
self.bimap(fa, |b| b)
}
fn rmap<B2, FB>(self, fb: FB) -> F::T<A, B2>
where Self: Sized,
FB: F1<B, Ret = B2>
{
self.bimap(|a| a, fb)
}
#[allow(clippy::self_named_constructors)]
fn join(self) -> Join<F, Self, A>
where Self: Sized,
F: HKT2<T<A, A> = Self>
{
Join::join(self)
}
}
pub struct Join<M, T, A>(T, PhantomData<(M, A)>);
impl<M, T, A> Join<M, T, A> {
pub fn join(t: T) -> Join<M, T, A>
where M: HKT2<T<A, A> = T>
{
Join(t, PhantomData)
}
pub fn unjoin(self) -> T {
self.0
}
}
pub struct JoinHKT<M>(PhantomData<M>);
impl<M> JoinHKT<M> {
fn join<A>(t: M::T<A, A>) -> <Self as HKT1>::T<A>
where M: HKT2
{
Join::join(t)
}
}
impl<M> HKT1 for JoinHKT<M> where M: HKT2
{
type T<A> = Join<M, <M as HKT2>::T<A, A>, A>;
}
impl<M, T, A> Functor<JoinHKT<M>, A> for Join<M, T, A>
where JoinHKT<M>: HKT1<T<A> = Self>,
M: HKT2 + HKT2<T<A, A> = T>,
T: Bifunctor<M, A, A>
{
fn fmap<AB, B>(self, f: AB) -> <JoinHKT<M> as HKT1>::T<B>
where AB: F1<A, Ret = B>
{
JoinHKT::<M>::join::<B>(self.0.bimap(|a| f.call(a), |b| f.call(b)))
}
}