1use super::Ability;
2
3#[derive(Clone)]
4pub struct Fx<'f, S: Clone, V: Clone>(Eff<'f, S, V>);
5
6#[derive(Clone)]
7enum Eff<'f, S: Clone, V: Clone> {
8 Immediate(S, V),
9 Pending(Ability<'f, S, S, V>),
10}
11
12impl<V: Clone> Fx<'_, (), V> {
13 pub fn eval(self) -> V {
14 let mut e = self;
15 loop {
16 match e.0 {
17 Eff::Immediate((), v) => return v,
18 Eff::Pending(f) => e = f.apply(()),
19 }
20 }
21 }
22}
23
24impl<'f, S: Clone, V: Clone> Fx<'f, S, V> {
25 pub fn immediate(s: S, value: V) -> Self {
26 Fx(Eff::Immediate(s, value))
27 }
28
29 pub fn pending<F>(f: F) -> Self
30 where
31 F: FnOnce(S) -> Self + Clone + 'f,
32 {
33 Fx(Eff::Pending(Ability::new(f)))
34 }
35
36 pub fn adapt<T, U, C, F>(self, cmap: C, fmap: F) -> Fx<'f, T, U>
37 where
38 T: Clone,
39 S: Clone,
40 U: Clone,
41 C: FnOnce(T) -> S + Clone + 'f,
42 F: FnOnce(T, S, V) -> Fx<'f, T, U> + Clone + 'f,
43 {
44 Fx::pending(|t: T| match self.0 {
45 Eff::Immediate(s, v) => fmap(t, s, v),
46 Eff::Pending(f) => f.apply(cmap.clone()(t)).adapt(cmap, fmap),
47 })
48 }
49}