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()
}
}
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> {
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> {
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)
}
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))
}
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());
}