extern crate alloc;
use alloc::boxed::Box;
use core::marker::PhantomData;
use super::algebraic::EffectusAlgebraicus;
use super::row_v2::{EffectId, EffectRow, EffectSet, assert_has_effect_type};
pub struct Eff<R: EffectRow, A> {
run: Box<dyn FnOnce() -> EffResult<R, A> + Send>,
}
pub enum EffResult<R: EffectRow, A> {
Purus(A),
Suspensus(EffSuspension<R, A>),
}
pub struct EffSuspension<R: EffectRow, A> {
operation: Box<dyn core::any::Any + Send>,
continuation: Box<dyn core::any::Any + Send>,
_phantom: PhantomData<(R, A)>,
}
impl<R: EffectRow, A: 'static + Send> Eff<R, A> {
#[inline]
pub fn purus(value: A) -> Self {
Eff {
run: Box::new(move || EffResult::Purus(value)),
}
}
#[inline]
pub fn map<B, F>(self, f: F) -> Eff<R, B>
where
B: 'static + Send,
F: FnOnce(A) -> B + Send + 'static,
{
Eff {
run: Box::new(move || {
match (self.run)() {
EffResult::Purus(a) => EffResult::Purus(f(a)),
EffResult::Suspensus(susp) => {
EffResult::Suspensus(EffSuspension {
operation: susp.operation,
continuation: susp.continuation,
_phantom: PhantomData,
})
}
}
}),
}
}
#[inline]
pub fn flat_map<B, F>(self, f: F) -> Eff<R, B>
where
B: 'static + Send,
F: FnOnce(A) -> Eff<R, B> + Send + 'static,
{
Eff {
run: Box::new(move || match (self.run)() {
EffResult::Purus(a) => (f(a).run)(),
EffResult::Suspensus(susp) => EffResult::Suspensus(EffSuspension {
operation: susp.operation,
continuation: susp.continuation,
_phantom: PhantomData,
}),
}),
}
}
#[inline]
pub fn map2<B, C, F>(self, other: Eff<R, B>, f: F) -> Eff<R, C>
where
B: 'static + Send,
C: 'static + Send,
F: FnOnce(A, B) -> C + Send + 'static,
{
self.flat_map(move |a| other.map(move |b| f(a, b)))
}
}
#[inline]
pub fn run_purus<A: 'static + Send>(eff: Eff<EffectSet<0>, A>) -> A {
match (eff.run)() {
EffResult::Purus(a) => a,
EffResult::Suspensus(_) => {
panic!("Pure computation suspended on effect - this is a bug")
}
}
}
pub trait Membrum<R: EffectRow>: EffectusAlgebraicus {}
impl<E, R> Membrum<R> for E
where
E: EffectusAlgebraicus + EffectId,
R: EffectRow,
{
}
#[inline]
pub fn send<E, R>(op: E) -> Eff<R, E::Result>
where
E: EffectusAlgebraicus + EffectId + Send + 'static,
E::Result: Send + 'static,
R: EffectRow,
{
assert_has_effect_type::<R, E>();
Eff {
run: Box::new(move || {
EffResult::Suspensus(EffSuspension {
operation: Box::new(op),
continuation: Box::new(()),
_phantom: PhantomData,
})
}),
}
}
pub type ENil = EffectSet<0>;
#[inline]
pub fn pure_eff<R: EffectRow, A: 'static + Send>(a: A) -> Eff<R, A> {
Eff::purus(a)
}
#[inline]
pub fn then<R: EffectRow, A: 'static + Send, B: 'static + Send>(
first: Eff<R, A>,
second: Eff<R, B>,
) -> Eff<R, B> {
first.flat_map(move |_| second)
}
pub fn sequence_eff<R: EffectRow, A: 'static + Send + Clone>(
effs: alloc::vec::Vec<Eff<R, A>>,
) -> Eff<R, alloc::vec::Vec<A>> {
effs.into_iter()
.fold(Eff::purus(alloc::vec::Vec::new()), |acc, eff| {
acc.flat_map(move |mut vec| {
eff.map(move |a| {
vec.push(a);
vec
})
})
})
}
pub fn traverse_eff<R, A, B, F>(items: alloc::vec::Vec<A>, f: F) -> Eff<R, alloc::vec::Vec<B>>
where
R: EffectRow,
A: 'static + Send,
B: 'static + Send + Clone,
F: Fn(A) -> Eff<R, B> + Clone + Send + 'static,
{
items
.into_iter()
.fold(Eff::purus(alloc::vec::Vec::new()), move |acc, a| {
let f = f.clone();
acc.flat_map(move |mut vec| {
f(a).map(move |b| {
vec.push(b);
vec
})
})
})
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn test_eff_purus() {
let eff: Eff<ENil, i32> = Eff::purus(42);
let result = run_purus(eff);
assert_eq!(result, 42);
}
#[test]
fn test_eff_map() {
let eff: Eff<ENil, i32> = Eff::purus(21);
let doubled = eff.map(|x| x * 2);
let result = run_purus(doubled);
assert_eq!(result, 42);
}
#[test]
fn test_eff_flat_map() {
let eff: Eff<ENil, i32> = Eff::purus(20);
let result = eff.flat_map(|x| Eff::purus(x + 22));
assert_eq!(run_purus(result), 42);
}
#[test]
fn test_eff_map2() {
let eff1: Eff<ENil, i32> = Eff::purus(20);
let eff2: Eff<ENil, i32> = Eff::purus(22);
let sum = eff1.map2(eff2, |a, b| a + b);
assert_eq!(run_purus(sum), 42);
}
#[test]
fn test_pure_eff() {
let eff: Eff<ENil, &str> = pure_eff("hello");
assert_eq!(run_purus(eff), "hello");
}
#[test]
fn test_then() {
let first: Eff<ENil, i32> = Eff::purus(1);
let second: Eff<ENil, &str> = Eff::purus("result");
let result = then(first, second);
assert_eq!(run_purus(result), "result");
}
#[test]
fn test_sequence_eff() {
let effs: alloc::vec::Vec<Eff<ENil, i32>> =
vec![Eff::purus(1), Eff::purus(2), Eff::purus(3)];
let sequenced = sequence_eff(effs);
assert_eq!(run_purus(sequenced), vec![1, 2, 3]);
}
#[test]
fn test_traverse_eff() {
let items = vec![1, 2, 3];
let result = traverse_eff(items, |x| Eff::<ENil, _>::purus(x * 2));
assert_eq!(run_purus(result), vec![2, 4, 6]);
}
}