pub mod blocking;
use core::{future::Future, ops::ControlFlow};
// Goal: code using the module shouldn't care if the underlying environment is async or not.
// That is the API should allow yielding without forcing it.
//
// Escaping the effect wrapper requires knowing the concrete context or transforming in the more
// general form which is a Future.
// Async operations cannot be made blocking without issues.
//
// Async and blocking can perform loops. In async the loop will yield between iterations.
// The effective value becomes the loop context. This operation can encode arbitrary folds which
// gives a fully powerful loop construct. The callback effective is allowed to reference the
// context, but the returned value is not.
//
// Async and blocking can perform maps. This is a blocking transform of the value from one form to
// another.
//
// A map that returns an effective for the callback is a then.
//
// The special operation is as_ctx which allows a effective value to be referenced temporarily
// during the operation. This is basically a loop that happens onces.
//
// All effectives can be converted to the effect's erased effective.
//
// An effective can be made from a value.
//
// An effective can be made from a future.
//
// Multiple effectives can be joined together.
//
// E::join((
// || A,
// || B
// ))
pub trait Effect:
Join<Effect = Self> + TryJoin<Effect = Self> + Send + Sync + Sized + 'static
{
type Erased<T>: Effective<Output = T, Effect = Self>;
type Ready<T>: Effective<Output = T, Effect = Self>;
fn ready<T>(value: T) -> Self::Ready<T>;
type FromFuture<F>: Effective<Output = F::Output, Effect = Self>
where
F: Future;
fn from_future<F>() -> Self::FromFuture<F>
where
F: Future;
}
pub trait EffectiveForLt<'a, Output: 'a, E: Effect, Bound> {
type T: Effective<Output = Output, Effect = E>;
}
pub trait EffectiveHrt<Output, E: Effect>: for<'a> EffectiveForLt<'a, Output, E, &'a Output> {}
pub type EffectiveT<'a, V, Output, E> = <V as EffectiveForLt<'a, Output, E, &'a Output>>::T;
pub trait Effective: Into<<Self::Effect as Effect>::Erased<Self::Output>> {
/// The effect the effective belongs to.
type Effect: Effect;
/// The type of the effective's output value.
type Output;
/// Future that resolves to the same value as the effective.
type IntoFuture: Future<Output = Self::Output>;
/// Convert the effective into a general future for use in async.
fn into_future(self) -> Self::IntoFuture;
/// Effective performing a loop.
type Loop<T, V, F>: Effective<Output = (Self::Output, T), Effect = Self::Effect>
where
F: for<'a> FnMut(&'a mut Self::Output) -> EffectiveT<'a, V, ControlFlow<T>, Self::Effect>,
V: EffectiveHrt<ControlFlow<T>, Self::Effect>;
/// Perform a loop with the effective value as context.
///
/// When the callback returns break then the loop will end and the effective
/// will resolve to the break value and the context.
fn r#loop<T, V, F>(self, cb: F) -> Self::Loop<T, V, F>
where
F: for<'a> FnMut(&'a mut Self::Output) -> EffectiveT<'a, V, ControlFlow<T>, Self::Effect>,
V: EffectiveHrt<ControlFlow<T>, Self::Effect>;
type Map<T, F>: Effective<Output = T, Effect = Self::Effect>
where
F: FnOnce(Self::Output) -> T;
fn map<T, F>(self, cb: F) -> Self::Map<T, F>
where
F: FnOnce(Self::Output) -> T;
type Then<T, V, F>: Effective<Output = T, Effect = Self::Effect>
where
F: FnOnce(Self::Output) -> V,
V: Effective<Output = T, Effect = Self::Effect>;
fn then<T, V, F>(self, cb: F) -> Self::Then<T, V, F>
where
F: FnOnce(Self::Output) -> V,
V: Effective<Output = T, Effect = Self::Effect>;
type AsCtx<T, V, F>: Effective<Output = (Self::Output, T), Effect = Self::Effect>
where
F: for<'a> FnOnce(&'a mut Self::Output) -> EffectiveT<'a, V, T, Self::Effect>,
V: EffectiveHrt<T, Self::Effect>;
fn as_ctx<T, V, F>(self, cb: F) -> Self::AsCtx<T, V, F>
where
F: for<'a> FnOnce(&'a mut Self::Output) -> EffectiveT<'a, V, T, Self::Effect>,
V: EffectiveHrt<T, Self::Effect>;
}
pub trait Join {
type Effect: Effect;
type Two<T0, T1>: Effective<Output = (T0::Output, T1::Output), Effect = Self::Effect>
where
T0: Effective<Effect = Self::Effect>,
T1: Effective<Effect = Self::Effect>;
type Three<T0, T1, T2>: Effective<
Output = (T0::Output, T1::Output, T2::Output),
Effect = Self::Effect,
>
where
T0: Effective<Effect = Self::Effect>,
T1: Effective<Effect = Self::Effect>,
T2: Effective<Effect = Self::Effect>;
fn two<T0, T1, F0, F1>(cb: (F0, F1)) -> Self::Two<T0, T1>
where
T0: Effective<Effect = Self::Effect>,
T1: Effective<Effect = Self::Effect>,
F0: FnOnce() -> T0,
F1: FnOnce() -> T1;
fn three<T0, T1, T2, F0, F1, F2>(effectives: (F0, F1, F2)) -> Self::Three<T0, T1, T2>
where
T0: Effective<Effect = Self::Effect>,
T1: Effective<Effect = Self::Effect>,
T2: Effective<Effect = Self::Effect>,
F0: FnOnce() -> T0,
F1: FnOnce() -> T1,
F2: FnOnce() -> T2;
}
pub trait TryJoin {
type Effect: Effect;
type Two<Err, V0, V1, T0, T1>: Effective<Output = Result<(T0, T1), Err>, Effect = Self::Effect>
where
V0: Effective<Output = Result<T0, Err>, Effect = Self::Effect>,
V1: Effective<Output = Result<T1, Err>, Effect = Self::Effect>;
type Three<Err, V0, V1, V2, T0, T1, T2>: Effective<
Output = Result<(T0, T1, T2), Err>,
Effect = Self::Effect,
>
where
V0: Effective<Output = Result<T0, Err>, Effect = Self::Effect>,
V1: Effective<Output = Result<T1, Err>, Effect = Self::Effect>,
V2: Effective<Output = Result<T2, Err>, Effect = Self::Effect>;
fn two<Err, T0, T1, V0, V1, F0, F1>(cb: (F0, F1)) -> Self::Two<Err, V0, V1, T0, T1>
where
V0: Effective<Output = Result<T0, Err>, Effect = Self::Effect>,
V1: Effective<Output = Result<T1, Err>, Effect = Self::Effect>,
F0: FnOnce() -> T0,
F1: FnOnce() -> T1;
fn three<Err, T0, T1, T2, V0, V1, V2, F0, F1, F2>(
cb: (F0, F1, F2),
) -> Self::Three<Err, V0, V1, V2, T0, T1, T2>
where
V0: Effective<Output = Result<T0, Err>, Effect = Self::Effect>,
V1: Effective<Output = Result<T1, Err>, Effect = Self::Effect>,
V2: Effective<Output = Result<T2, Err>, Effect = Self::Effect>,
F0: FnOnce() -> T0,
F1: FnOnce() -> T1,
F2: FnOnce() -> T2;
}