diff --git a/src/func/classes.rs b/src/func/classes.rs
index c81f35b..14678ff 100644
--- a/src/func/classes.rs
+++ b/src/func/classes.rs
@@ -20,6 +20,7 @@
use crate::func::*;
pub mod composition;
+pub mod effect;
pub mod future;
pub mod lazy;
pub mod option;
diff --git a/src/func/classes/effect.rs b/src/func/classes/effect.rs
new file mode 100644
index 0000000..760a431
--- /dev/null
+++ b/src/func/classes/effect.rs
@@ -0,0 +1,185 @@
+use crate::func::*;
+
+pub trait Effect {
+ fn pure() -> Self;
+
+ fn seq(lhs: Self, rhs: Self) -> Self;
+
+ fn after(self, effect: Self) -> Self;
+}
+
+#[derive(Clone)]
+pub struct WithEffect {
+ value: A,
+ effect: E,
+}
+
+impl WithEffect {
+ fn after(self, effect: E) -> Self {
+ WithEffect {
+ value: self.value,
+ effect: self.effect.after(effect),
+ }
+ }
+}
+
+#[derive(SharedFunctor, CovariantFunctor)]
+pub struct EffectClass(E);
+
+impl WeakFunctor for EffectClass {
+ type F<'a, A: 'a> = WithEffect
+ where
+ Self: 'a;
+}
+
+impl Functor for EffectClass {
+ fn fmap<'a, A: 'a, B: 'a>(f: impl 'a + FnOnce(A) -> B, fa: Self::F<'a, A>) -> Self::F<'a, B>
+ where
+ Self: 'a,
+ {
+ WithEffect {
+ value: f(fa.value),
+ effect: fa.effect,
+ }
+ }
+
+ fn replace<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, b: B) -> Self::F<'a, B>
+ where
+ Self: 'a,
+ {
+ drop(fa.value);
+ WithEffect {
+ value: b,
+ effect: fa.effect,
+ }
+ }
+}
+
+impl Pure for EffectClass {
+ fn pure<'a, A: 'a>(a: A) -> Self::F<'a, A>
+ where
+ Self: 'a,
+ {
+ WithEffect {
+ value: a,
+ effect: E::pure(),
+ }
+ }
+}
+
+impl ApplicativeSeq for EffectClass {
+ fn seq<'a, A: 'a, B: 'a>(
+ ff: Self::F<'a, impl 'a + FnOnce(A) -> B>,
+ fa: Self::F<'a, A>,
+ ) -> Self::F<'a, B>
+ where
+ Self: 'a,
+ {
+ WithEffect {
+ value: (ff.value)(fa.value),
+ effect: E::seq(ff.effect, fa.effect),
+ }
+ }
+}
+
+impl ApplicativeLA2 for EffectClass {
+ fn la2<'a, A: 'a, B: 'a, C: 'a>(
+ f: impl 'a + FnOnce(A, B) -> C,
+ fa: Self::F<'a, A>,
+ fb: Self::F<'a, B>,
+ ) -> Self::F<'a, C>
+ where
+ Self: 'a,
+ {
+ WithEffect {
+ value: f(fa.value, fb.value),
+ effect: E::seq(fa.effect, fb.effect),
+ }
+ }
+}
+
+impl ApplicativeTuple for EffectClass {
+ fn tuple<'a, A: 'a, B: 'a>((fa, fb): (Self::F<'a, A>, Self::F<'a, B>)) -> Self::F<'a, (A, B)>
+ where
+ Self: 'a,
+ {
+ WithEffect {
+ value: (fa.value, fb.value),
+ effect: E::seq(fa.effect, fb.effect),
+ }
+ }
+}
+
+impl Applicative for EffectClass {
+ fn discard_first<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, fb: Self::F<'a, B>) -> Self::F<'a, B>
+ where
+ Self: 'a,
+ {
+ drop(fa.value);
+ WithEffect {
+ value: fb.value,
+ effect: E::seq(fa.effect, fb.effect),
+ }
+ }
+
+ fn discard_second<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, fb: Self::F<'a, B>) -> Self::F<'a, A>
+ where
+ Self: 'a,
+ {
+ drop(fb.value);
+ WithEffect {
+ value: fa.value,
+ effect: E::seq(fa.effect, fb.effect),
+ }
+ }
+}
+
+impl Monad for EffectClass {
+ fn bind<'a, A: 'a, B: 'a>(
+ fa: Self::F<'a, A>,
+ f: impl 'a + FnOnce(A) -> Self::F<'a, B>,
+ ) -> Self::F<'a, B>
+ where
+ Self: 'a,
+ {
+ f(fa.value).after(fa.effect)
+ }
+
+ fn iterate<'a, B: 'a>(mut f: impl Iterative<'a, T = Self, B = B>) -> Self::F<'a, B>
+ where
+ Self: 'a,
+ {
+ let mut effect = E::pure();
+ loop {
+ let fa = f.next();
+ effect = fa.effect.after(effect);
+ match fa.value {
+ ControlFlow::Continue(next_f) => f = next_f,
+ ControlFlow::Break(b) => return WithEffect { value: b, effect },
+ }
+ }
+ }
+
+ fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A>
+ where
+ Self::F<'a, A>: 'a,
+ Self: 'a,
+ {
+ ffa.value.after(ffa.effect)
+ }
+}
+
+impl LocalFunctor for EffectClass {
+ fn stuff<'a, A: 'a, T: 'a + Pure>(fa: Self::F<'a, T::F<'a, A>>) -> T::F<'a, Self::F<'a, A>>
+ where
+ Self: 'a,
+ {
+ T::fmap(
+ |a| WithEffect {
+ value: a,
+ effect: fa.effect,
+ },
+ fa.value,
+ )
+ }
+}