use crate::{
CloneFunctor, CoMonad, DebugFunctor, EqFunctor, Functor, HKT, NoConstraint, Satisfies,
};
use alloc::boxed::Box;
use core::fmt;
use core::marker::PhantomData;
pub struct Cofree<F, A>
where
F: HKT<Constraint = NoConstraint>,
{
head: A,
tail: F::Type<Box<Cofree<F, A>>>,
}
impl<F, A> Cofree<F, A>
where
F: HKT<Constraint = NoConstraint>,
{
#[inline]
pub fn new(head: A, tail: F::Type<Box<Cofree<F, A>>>) -> Self {
Cofree { head, tail }
}
#[inline]
pub fn head(&self) -> &A {
&self.head
}
#[inline]
pub fn tail(&self) -> &F::Type<Box<Cofree<F, A>>> {
&self.tail
}
#[inline]
pub fn into_parts(self) -> (A, F::Type<Box<Cofree<F, A>>>) {
(self.head, self.tail)
}
#[inline]
pub fn extract(&self) -> A
where
A: Clone,
{
self.head.clone()
}
}
impl<F, A> Cofree<F, A>
where
F: HKT<Constraint = NoConstraint> + Functor<F>,
{
pub fn map<B, Fun>(self, f: Fun) -> Cofree<F, B>
where
Fun: Fn(A) -> B + Clone,
{
let head = f(self.head);
let g = f.clone();
let tail = F::fmap(self.tail, move |boxed: Box<Cofree<F, A>>| {
Box::new((*boxed).map(g.clone()))
});
Cofree { head, tail }
}
pub fn extend<B, K>(self, k: &K) -> Cofree<F, B>
where
K: Fn(&Cofree<F, A>) -> B,
{
let head = k(&self);
let tail = F::fmap(self.tail, |boxed: Box<Cofree<F, A>>| {
Box::new((*boxed).extend(k))
});
Cofree { head, tail }
}
pub fn unfold<X, C>(seed: X, coalg: &C) -> Cofree<F, A>
where
C: Fn(X) -> (A, F::Type<X>),
{
let (a, fx) = coalg(seed);
let tail = F::fmap(fx, |x: X| Box::new(Cofree::unfold(x, coalg)));
Cofree { head: a, tail }
}
pub fn duplicate(self) -> Cofree<F, Cofree<F, A>>
where
F: CloneFunctor,
A: Clone,
{
self.extend(&|w| w.clone())
}
}
impl<F, A> PartialEq for Cofree<F, A>
where
F: EqFunctor,
A: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.head == other.head && F::eq_type(&self.tail, &other.tail)
}
}
impl<F, A> Eq for Cofree<F, A>
where
F: EqFunctor,
A: Eq,
{
}
impl<F, A> fmt::Debug for Cofree<F, A>
where
F: DebugFunctor,
A: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Cofree { head: ")?;
fmt::Debug::fmt(&self.head, f)?;
f.write_str(", tail: ")?;
F::fmt_type(&self.tail, f)?;
f.write_str(" }")
}
}
impl<F, A> Clone for Cofree<F, A>
where
F: CloneFunctor,
A: Clone,
{
fn clone(&self) -> Self {
Cofree {
head: self.head.clone(),
tail: F::clone_type(&self.tail),
}
}
}
pub struct CofreeWitness<F>(PhantomData<F>);
impl<F> HKT for CofreeWitness<F>
where
F: HKT<Constraint = NoConstraint>,
{
type Constraint = NoConstraint;
type Type<T> = Cofree<F, T>;
}
impl<F> Functor<CofreeWitness<F>> for CofreeWitness<F>
where
F: HKT<Constraint = NoConstraint> + Functor<F>,
{
fn fmap<A, B, Func>(m_a: Cofree<F, A>, mut f: Func) -> Cofree<F, B>
where
A: Satisfies<NoConstraint>,
B: Satisfies<NoConstraint>,
Func: FnMut(A) -> B,
{
fn go<F, A, B, Func>(c: Cofree<F, A>, f: &mut Func) -> Cofree<F, B>
where
F: HKT<Constraint = NoConstraint> + Functor<F>,
Func: FnMut(A) -> B,
{
let (head, tail) = c.into_parts();
let new_head = f(head);
let new_tail = F::fmap(tail, |boxed: Box<Cofree<F, A>>| Box::new(go(*boxed, f)));
Cofree::new(new_head, new_tail)
}
go(m_a, &mut f)
}
}
impl<F> CoMonad<CofreeWitness<F>> for CofreeWitness<F>
where
F: HKT<Constraint = NoConstraint> + Functor<F> + CloneFunctor,
{
fn extract<A>(fa: &Cofree<F, A>) -> A
where
A: Satisfies<NoConstraint> + Clone,
{
fa.head().clone()
}
fn extend<A, B, Func>(fa: &Cofree<F, A>, mut f: Func) -> Cofree<F, B>
where
A: Satisfies<NoConstraint> + Clone,
B: Satisfies<NoConstraint>,
Func: FnMut(&Cofree<F, A>) -> B,
{
fn go<F, A, B, Func>(fa: &Cofree<F, A>, f: &mut Func) -> Cofree<F, B>
where
F: HKT<Constraint = NoConstraint> + Functor<F> + CloneFunctor,
A: Clone,
Func: FnMut(&Cofree<F, A>) -> B,
{
let head = f(fa);
let owned: F::Type<Box<Cofree<F, A>>> = F::clone_type(fa.tail());
let tail = F::fmap(owned, |boxed: Box<Cofree<F, A>>| Box::new(go(&boxed, f)));
Cofree::new(head, tail)
}
go(fa, &mut f)
}
}