1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use std::mem::transmute;
use std::marker::PhantomData;
use std::collections::VecDeque;

use super::ConduitM;

struct FnTake<'a, A, B>(Box<FnMut(A) -> Option<B> + 'a>);

impl<'a, A, B> FnTake<'a, A, B> {
    fn new<F: FnOnce(A) -> B + 'a>(f: F) -> Self {
        let mut opt = Some(f);
        FnTake(Box::new(move |a| {
            opt.take().map(|f| f(a))
        }))
    }
    fn run(mut self, a: A) -> B {
        self.0(a).unwrap()
    }
}

/// The Kleisli arrow from `A` to `ConduitM<I, O, B>`.
pub struct Kleisli<'a, A, I, O, B> {
    phan: PhantomData<(A, B)>,
    deque: VecDeque<FnTake<'a, Box<()>, ConduitM<'a, I, O, ()>>>
}

unsafe fn fn_transmute<'a, I, O, A, B, F: 'a + FnOnce(Box<A>) -> ConduitM<'a, I, O, B>>(f: F)
    -> FnTake<'a, Box<()>, ConduitM<'a, I, O, ()>> {
    FnTake::new(move |ptr| transmute(f(transmute::<Box<()>, Box<A>>(ptr))))
}

impl<'a, I, O, A> Kleisli<'a, A, I, O, A> {
    /// Creates the identity arrow.
    ///
    /// # Example
    ///
    /// ```rust
    /// use plumbum::Kleisli;
    ///
    /// let k: Kleisli<i32, (), (), i32> = Kleisli::new();
    /// assert_eq!(k.run(42), 42.into());
    /// ```
    pub fn new() -> Kleisli<'a, A, I, O, A> {
        Kleisli { phan: PhantomData, deque: VecDeque::new() }
    }
}

pub fn append_boxed<'a, I, O, A, B, C, F>
    (mut k: Kleisli<'a, A, I, O, B>, f: F) -> Kleisli<'a, A, I, O, C>
    where F: 'a + FnOnce(Box<B>) -> ConduitM<'a, I, O, C> {
    k.deque.push_back(unsafe { fn_transmute(f) });
    Kleisli { phan: PhantomData, deque: k.deque }
}

impl<'a, I, O, A, B> Kleisli<'a, A, I, O, B> {

    /// Wraps the given function into an arrow.
    ///
    /// # Example
    ///
    /// ```rust
    /// use plumbum::Kleisli;
    ///
    /// let k: Kleisli<i32, (), (), i32> = Kleisli::from(|x: i32| (x + 1).into());
    /// assert_eq!(k.run(42), 43.into());
    /// ```
    pub fn from<F>(f: F) -> Kleisli<'a, A, I, O, B>
        where F: 'a + FnOnce(A) -> ConduitM<'a, I, O, B> {
        Kleisli::new().append(f)
    }

    /// Appends the given function to the tail of the arrow.
    /// This corresponds to closure composition at the codomain (post-composition).
    ///
    /// # Example
    ///
    /// ```rust
    /// use plumbum::Kleisli;
    ///
    /// let k: Kleisli<i32, (), (), i32> = Kleisli::from(|x: i32| (x + 1).into())
    ///                                    .append(|x| (x * 2).into());
    /// assert_eq!(k.run(42), 86.into());
    /// ```
    pub fn append<F, C>(self, f: F) -> Kleisli<'a, A, I, O, C>
        where F: 'a + FnOnce(B) -> ConduitM<'a, I, O, C> {
        append_boxed(self, move |b| f(*b))
    }

    /// Given an input, runs the arrow to completion and return
    /// the resulting program.
    pub fn run(mut self, a: A) -> ConduitM<'a, I, O, B> where I: 'static, O: 'static {
        unsafe {
            let mut r = transmute::<ConduitM<'a, I, O, A>, ConduitM<'a, I, O, ()>>(a.into());
            loop {
                match self.deque.pop_front() {
                    None => return transmute(r),
                    Some(f) => r = r.and_then_boxed(move |a| f.run(a))
                }
            }
        }
    }

}

#[test]
fn kleisli_run_plus_one() {
    let k: Kleisli<i32, (), (), i32> = Kleisli::from(|a: i32| (a + 1).into());
    assert_eq!(k.run(42), 43.into());
}

#[test]
fn kleisli_run_to_string() {
    let k: Kleisli<i32, (), (), String> =
        Kleisli::from(|a: i32| (a.to_string()).into());
    assert_eq!(k.run(42), "42".to_string().into());
}