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
pub trait EnvIO {
    type Out;

    fn run(self) -> Self::Out;

    fn flat_map<OutEnv, F>(self, f: F) -> FlatMap<Self, F>
    where Self: Sized, OutEnv: EnvIO, F: Fn(Self::Out) -> OutEnv {
        FlatMap { envio: self, f }
    }
}

pub struct Effect<Out, F: Fn() -> Out> {
    effect: F
}

impl<Out0, F: Fn() -> Out0> EnvIO for Effect<Out0, F> {
    type Out = Out0;

    fn run(self) -> Self::Out {
        (self.effect)()
    }
}

pub struct FlatMap<Env, F> {
    envio: Env,
    f: F
}

impl<Env: EnvIO, OutEnv: EnvIO, F> EnvIO for FlatMap<Env, F> where F: Fn(Env::Out) -> OutEnv {
    type Out = OutEnv::Out;

    fn run(self) -> OutEnv::Out {
        (self.f)(self.envio.run()).run()
    }
}

pub trait IntoEnvIO {
    type Out;

    type IntoEnv: EnvIO<Out=Self::Out>;

    fn effect(self) -> Self::IntoEnv;
}

impl<T, F: Fn() -> T> IntoEnvIO for F {
    type Out = T;
    type IntoEnv = Effect<T, F>;

    fn effect(self) -> Self::IntoEnv {
        Effect { effect: self }
    }
}

#[macro_export]
macro_rules! eff {
    ($e:expr) => {
        (move || $e).effect()
    }
}