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
use std::mem::transmute;
use std::marker::PhantomData;
use std::collections::VecDeque;

use super::instr::Instr;
use super::{Program, point};

/// The Kleisli arrow from `A` to `Program<I, B>`.
pub struct Kleisli<'a, I: Instr<Return=A>, A, B> {
    phan: PhantomData<(A, B)>,
    deque: VecDeque<Box<Fn(Box<()>) -> Program<'a, I, ()> + 'a>>
}

unsafe fn fn_transmute<'a, I: Instr, A, B, F: 'a + Fn(Box<A>) -> Program<'a, I, B>>(f: F)
    -> Box<Fn(Box<()>) -> Program<'a, I, ()> + 'a> {
    Box::new(move |ptr| transmute(f(transmute::<Box<()>, Box<A>>(ptr))))
}

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

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

impl<'a, I: 'a + Instr<Return=A>, A, B> Kleisli<'a, I, A, B> {

    /// Appends the given function to the tail of the arrow.
    /// This corresponds to closure composition at the codomain (post-composition).
    ///
    /// # Example
    ///
    /// ```rust
    /// use operational::{Kleisli, point};
    /// use operational::instr::identity;
    ///
    /// let k = Kleisli::new().append(|x| point(x + 1));
    /// assert_eq!(k.run(42), identity(43));
    /// ```
    pub fn append<F, C>(self, f: F) -> Kleisli<'a, I, A, C>
        where F: 'a + Fn(B) -> Program<'a, I, 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) -> Program<'a, I, B> {
        unsafe {
            let mut r = transmute::<Program<'a, I, A>, Program<'a, I, ()>>(point(a));
            loop {
                match self.deque.pop_front() {
                    None => return transmute(r),
                    Some(f) => r = r.and_then_boxed(move |a| (*f)(a))
                }
            }
        }
    }

}

#[test]
fn kleisli_run_plus_one() {
    use super::instr::Identity;
    let k: Kleisli<Identity<i32>, _, _> = Kleisli::new().append(|a| point(a + 1));
    assert_eq!(k.run(42), point(43));
}

#[test]
fn kleisli_run_to_string() {
    use super::instr::Identity;
    let k: Kleisli<Identity<i32>, _, _> = Kleisli::new().append(|a: i32| point(a.to_string()));
    assert_eq!(k.run(42), point("42".to_string()));
}