use crate::arrow::arrow_term::{ArrowCore, ArrowVal};
use crate::{Functor, HKT, Monad, NoConstraint, Pure};
use alloc::boxed::Box;
impl<G> ArrowCore<G> {
pub fn interpret_kleisli<M, V, Phi>(
&self,
phi: &Phi,
input: ArrowVal<V>,
) -> M::Type<ArrowVal<V>>
where
M: Monad<M> + HKT<Constraint = NoConstraint>,
Phi: Fn(&G, V) -> M::Type<V>,
V: Clone,
{
match self {
ArrowCore::Id => <M as Pure<M>>::pure(input),
ArrowCore::Gen(g) => match input {
ArrowVal::Leaf(v) => <M as Functor<M>>::fmap(phi(g, v), ArrowVal::Leaf),
other => <M as Pure<M>>::pure(other),
},
ArrowCore::Compose(f, h) => {
<M as Monad<M>>::bind(f.interpret_kleisli::<M, V, Phi>(phi, input), move |mid| {
h.interpret_kleisli::<M, V, Phi>(phi, mid)
})
}
ArrowCore::First(f) => match input {
ArrowVal::Pair(a, b) => {
<M as Monad<M>>::bind(f.interpret_kleisli::<M, V, Phi>(phi, *a), move |a2| {
<M as Pure<M>>::pure(ArrowVal::Pair(Box::new(a2), b.clone()))
})
}
other => <M as Pure<M>>::pure(other),
},
ArrowCore::Second(h) => match input {
ArrowVal::Pair(a, b) => {
<M as Monad<M>>::bind(h.interpret_kleisli::<M, V, Phi>(phi, *b), move |b2| {
<M as Pure<M>>::pure(ArrowVal::Pair(a.clone(), Box::new(b2)))
})
}
other => <M as Pure<M>>::pure(other),
},
ArrowCore::Split(f, h) => match input {
ArrowVal::Pair(a, b) => {
let b = *b;
<M as Monad<M>>::bind(f.interpret_kleisli::<M, V, Phi>(phi, *a), move |a2| {
<M as Monad<M>>::bind(
h.interpret_kleisli::<M, V, Phi>(phi, b.clone()),
move |b2| {
<M as Pure<M>>::pure(ArrowVal::Pair(
Box::new(a2.clone()),
Box::new(b2),
))
},
)
})
}
other => <M as Pure<M>>::pure(other),
},
ArrowCore::Fanout(f, h) => {
let copy = input.clone();
<M as Monad<M>>::bind(f.interpret_kleisli::<M, V, Phi>(phi, input), move |a2| {
<M as Monad<M>>::bind(
h.interpret_kleisli::<M, V, Phi>(phi, copy.clone()),
move |b2| {
<M as Pure<M>>::pure(ArrowVal::Pair(Box::new(a2.clone()), Box::new(b2)))
},
)
})
}
ArrowCore::Left(f) => match input {
ArrowVal::InL(a) => <M as Functor<M>>::fmap(
f.interpret_kleisli::<M, V, Phi>(phi, *a),
ArrowVal::inl,
),
other @ (ArrowVal::Leaf(_) | ArrowVal::Pair(..) | ArrowVal::InR(_)) => {
<M as Pure<M>>::pure(other)
}
},
ArrowCore::Right(h) => match input {
ArrowVal::InR(b) => <M as Functor<M>>::fmap(
h.interpret_kleisli::<M, V, Phi>(phi, *b),
ArrowVal::inr,
),
other @ (ArrowVal::Leaf(_) | ArrowVal::Pair(..) | ArrowVal::InL(_)) => {
<M as Pure<M>>::pure(other)
}
},
ArrowCore::Choice(f, h) => match input {
ArrowVal::InL(a) => <M as Functor<M>>::fmap(
f.interpret_kleisli::<M, V, Phi>(phi, *a),
ArrowVal::inl,
),
ArrowVal::InR(b) => <M as Functor<M>>::fmap(
h.interpret_kleisli::<M, V, Phi>(phi, *b),
ArrowVal::inr,
),
other @ (ArrowVal::Leaf(_) | ArrowVal::Pair(..)) => <M as Pure<M>>::pure(other),
},
ArrowCore::Fanin(f, h) => match input {
ArrowVal::InL(a) => f.interpret_kleisli::<M, V, Phi>(phi, *a),
ArrowVal::InR(b) => h.interpret_kleisli::<M, V, Phi>(phi, *b),
other @ (ArrowVal::Leaf(_) | ArrowVal::Pair(..)) => <M as Pure<M>>::pure(other),
},
}
}
}