use alloc::boxed::Box;
use core::marker::PhantomData;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ArrowVal<V> {
Leaf(V),
Pair(Box<ArrowVal<V>>, Box<ArrowVal<V>>),
InL(Box<ArrowVal<V>>),
InR(Box<ArrowVal<V>>),
}
impl<V> ArrowVal<V> {
#[inline]
pub fn pair(left: ArrowVal<V>, right: ArrowVal<V>) -> Self {
ArrowVal::Pair(Box::new(left), Box::new(right))
}
#[inline]
pub fn inl(value: ArrowVal<V>) -> Self {
ArrowVal::InL(Box::new(value))
}
#[inline]
pub fn inr(value: ArrowVal<V>) -> Self {
ArrowVal::InR(Box::new(value))
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ArrowCore<G> {
Id,
Gen(G),
Compose(Box<ArrowCore<G>>, Box<ArrowCore<G>>),
First(Box<ArrowCore<G>>),
Second(Box<ArrowCore<G>>),
Split(Box<ArrowCore<G>>, Box<ArrowCore<G>>),
Fanout(Box<ArrowCore<G>>, Box<ArrowCore<G>>),
Left(Box<ArrowCore<G>>),
Right(Box<ArrowCore<G>>),
Choice(Box<ArrowCore<G>>, Box<ArrowCore<G>>),
Fanin(Box<ArrowCore<G>>, Box<ArrowCore<G>>),
}
impl<G> ArrowCore<G> {
pub fn interpret<V, I>(&self, interp: &I, input: ArrowVal<V>) -> ArrowVal<V>
where
I: Fn(&G, V) -> V,
V: Clone,
{
match self {
ArrowCore::Id => input,
ArrowCore::Gen(g) => match input {
ArrowVal::Leaf(v) => ArrowVal::Leaf(interp(g, v)),
other @ (ArrowVal::Pair(..) | ArrowVal::InL(_) | ArrowVal::InR(_)) => other,
},
ArrowCore::Compose(f, g) => g.interpret(interp, f.interpret(interp, input)),
ArrowCore::First(f) => match input {
ArrowVal::Pair(a, b) => ArrowVal::Pair(Box::new(f.interpret(interp, *a)), b),
other @ (ArrowVal::Leaf(_) | ArrowVal::InL(_) | ArrowVal::InR(_)) => other,
},
ArrowCore::Second(g) => match input {
ArrowVal::Pair(a, b) => ArrowVal::Pair(a, Box::new(g.interpret(interp, *b))),
other @ (ArrowVal::Leaf(_) | ArrowVal::InL(_) | ArrowVal::InR(_)) => other,
},
ArrowCore::Split(f, g) => match input {
ArrowVal::Pair(a, b) => ArrowVal::Pair(
Box::new(f.interpret(interp, *a)),
Box::new(g.interpret(interp, *b)),
),
other @ (ArrowVal::Leaf(_) | ArrowVal::InL(_) | ArrowVal::InR(_)) => other,
},
ArrowCore::Fanout(f, g) => ArrowVal::Pair(
Box::new(f.interpret(interp, input.clone())),
Box::new(g.interpret(interp, input)),
),
ArrowCore::Left(f) => match input {
ArrowVal::InL(a) => ArrowVal::InL(Box::new(f.interpret(interp, *a))),
other @ (ArrowVal::Leaf(_) | ArrowVal::Pair(..) | ArrowVal::InR(_)) => other,
},
ArrowCore::Right(g) => match input {
ArrowVal::InR(b) => ArrowVal::InR(Box::new(g.interpret(interp, *b))),
other @ (ArrowVal::Leaf(_) | ArrowVal::Pair(..) | ArrowVal::InL(_)) => other,
},
ArrowCore::Choice(f, g) => match input {
ArrowVal::InL(a) => ArrowVal::InL(Box::new(f.interpret(interp, *a))),
ArrowVal::InR(b) => ArrowVal::InR(Box::new(g.interpret(interp, *b))),
other @ (ArrowVal::Leaf(_) | ArrowVal::Pair(..)) => other,
},
ArrowCore::Fanin(f, g) => match input {
ArrowVal::InL(a) => f.interpret(interp, *a),
ArrowVal::InR(b) => g.interpret(interp, *b),
other @ (ArrowVal::Leaf(_) | ArrowVal::Pair(..)) => other,
},
}
}
}
pub struct ArrowTerm<In, Out, G> {
core: ArrowCore<G>,
_wiring: PhantomData<fn(In) -> Out>,
}
impl<In, Out, G> ArrowTerm<In, Out, G> {
#[inline]
const fn wrap(core: ArrowCore<G>) -> Self {
ArrowTerm {
core,
_wiring: PhantomData,
}
}
#[inline]
pub const fn generator(g: G) -> Self {
ArrowTerm::wrap(ArrowCore::Gen(g))
}
#[inline]
pub const fn core(&self) -> &ArrowCore<G> {
&self.core
}
#[inline]
pub fn into_core(self) -> ArrowCore<G> {
self.core
}
}
impl<A, G> ArrowTerm<A, A, G> {
#[inline]
pub const fn id() -> Self {
ArrowTerm::wrap(ArrowCore::Id)
}
}
impl<In, Out, G> ArrowTerm<In, Out, G> {
#[inline]
pub fn compose<Next>(self, next: ArrowTerm<Out, Next, G>) -> ArrowTerm<In, Next, G> {
ArrowTerm::wrap(ArrowCore::Compose(Box::new(self.core), Box::new(next.core)))
}
#[inline]
pub fn first<C>(self) -> ArrowTerm<(In, C), (Out, C), G> {
ArrowTerm::wrap(ArrowCore::First(Box::new(self.core)))
}
#[inline]
pub fn second<C>(self) -> ArrowTerm<(C, In), (C, Out), G> {
ArrowTerm::wrap(ArrowCore::Second(Box::new(self.core)))
}
#[inline]
pub fn split<I2, O2>(self, g: ArrowTerm<I2, O2, G>) -> ArrowTerm<(In, I2), (Out, O2), G> {
ArrowTerm::wrap(ArrowCore::Split(Box::new(self.core), Box::new(g.core)))
}
#[inline]
pub fn fanout<O2>(self, g: ArrowTerm<In, O2, G>) -> ArrowTerm<In, (Out, O2), G> {
ArrowTerm::wrap(ArrowCore::Fanout(Box::new(self.core), Box::new(g.core)))
}
#[inline]
pub fn left<C>(self) -> ArrowTerm<crate::Either<In, C>, crate::Either<Out, C>, G> {
ArrowTerm::wrap(ArrowCore::Left(Box::new(self.core)))
}
#[inline]
pub fn right<C>(self) -> ArrowTerm<crate::Either<C, In>, crate::Either<C, Out>, G> {
ArrowTerm::wrap(ArrowCore::Right(Box::new(self.core)))
}
#[inline]
pub fn choice<I2, O2>(
self,
g: ArrowTerm<I2, O2, G>,
) -> ArrowTerm<crate::Either<In, I2>, crate::Either<Out, O2>, G> {
ArrowTerm::wrap(ArrowCore::Choice(Box::new(self.core), Box::new(g.core)))
}
#[inline]
pub fn fanin<I2>(self, g: ArrowTerm<I2, Out, G>) -> ArrowTerm<crate::Either<In, I2>, Out, G> {
ArrowTerm::wrap(ArrowCore::Fanin(Box::new(self.core), Box::new(g.core)))
}
}