pub trait Apply {
type In;
type Out;
fn apply(&self, a: Self::In) -> Self::Out;
}
pub trait ApplyDrop {
type In;
type Out;
fn apply_drop(self, a: Self::In) -> Self::Out;
}
pub struct FBox<FIn, FOut> {
f: Box<Fn(FIn) -> FOut>
}
impl<FIn, FOut> Apply for FBox<FIn, FOut> {
type In = FIn;
type Out = FOut;
fn apply(&self, a: FIn) -> FOut {
(self.f)(a)
}
}
impl<FIn, FOut> ApplyDrop for FBox<FIn, FOut> {
type In = FIn;
type Out = FOut;
fn apply_drop(self, a: FIn) -> FOut {
(self.f)(a)
}
}
impl<FIn: 'static, FOut: 'static> FBox<FIn, FOut> {
pub fn new(f: impl Fn(FIn) -> FOut + 'static) -> FBox<FIn, FOut> {
FBox { f: Box::new(f) }
}
pub fn compose<GIn: 'static>(self, g: impl Fn(GIn) -> FIn + 'static) -> FBox<GIn, FOut> {
FBox::new(move |x| (self.f)(g(x)))
}
pub fn and_then<GOut: 'static>(self, g: impl Fn(FOut) -> GOut + 'static) -> FBox<FIn, GOut> {
FBox::new(move |x| g((self.f)(x)))
}
pub fn compose_b<GIn: 'static>(self, other: FBox<GIn, FIn>) -> FBox<GIn, FOut> {
FBox::new(move |x| (self.f)((other.f)(x)))
}
pub fn and_then_b<GOut: 'static>(self, other: FBox<FOut, GOut>) -> FBox<FIn, GOut> {
FBox::new(move |x| (other.f)((self.f)(x)))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn apply_and_apply_drop() {
let fb1 = FBox::new(|x| x + 1);
assert_eq!(
fb1.apply(3),
fb1.apply_drop(3)
);
}
}