// pub mod r#async;
pub mod blocking;
use core::{future::Future, ops::ControlFlow};
use crate::{higher_ranked_trait, higher_ranked_type, hkt::Marker};
// 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 Ss: Send + Sync {}
impl<T: Send + Sync> Ss for T {}
/// Type class for type erased higher-ranked effective types.
#[allow(non_snake_case)]
mod Erased {
use super::*;
/// For all lifetimes `'lt`.
///
/// You should never need to interact with this trait directly.
/// Instead use the [`ErasedEffective`] type alias.
///
/// The `Output` generic type is what the effective will output.
/// The `E` generic type is the effect this effective is for.
/// The `Bound` generic is for higher-ranked implied bounds.
/// See TODO for more info.
pub trait ForLt<'lt, Output: Ss + 'lt, E: Effect, Bound: 'lt> {
/// The effective type with the lifetime `'lt` given.
type Effective: Effective<'lt, Output = Output, Effect = E>;
}
/// A type class for higher-ranked types with an effective concrete type.
///
/// You should never need to interact with this trait directly.
/// Instead use the [`ErasedEffective`] type alias.
///
/// The `Output` generic type is what the effective will output.
/// The `E` generic type is the effect this effective is for.
pub trait Hkt<Output: Ss, E: Effect> {
/// Accessor to inject arbitrary higher-ranked implied bounds.
/// See TODO for more info.
type Hrt<Bound>: for<'a> ForLt<'a, Output, E, &'a (Output, Bound)>;
}
}
/// Type alias to get the concrete [`Effect::Erased`] type.
///
/// The `'lt` lifetime is the minimum lifetime of the returned effective.
/// `Output` is the output type of the effective.
/// `E` is the effect to pull the [`Effect::Erased`] from.
///
/// The `Bound` generic shouldn't be needed or used in most code.
/// It is only needed for adding higher-ranked implied bounds.
/// See TODO for details.
#[rustfmt::skip]
pub type ErasedEffective<'lt, Output, E, Bound = ()> = <
<
<E as Effect>::Erased<Output>
as
Erased::Hkt<Output, E>
>::Hrt<Bound>
as
Erased::ForLt<'lt, Output, E, &'lt (Output, Bound)>
>::Effective;
pub trait Effect: Join<Effect = Self> + TryJoin<Effect = Self> + Ss + Sized + 'static {
type Erased<T: Ss>: Erased::Hkt<T, Self>;
fn ready<'a, T: Ss + 'a>(value: T) -> ErasedEffective<'a, T, Self>;
fn from_future<'a, F: Ss + 'a>(future: F) -> ErasedEffective<'a, F::Output, Self>
where
F: Future,
F::Output: Ss + 'a;
}
pub fn all_ctx<T>(x: T) -> (T, ()) {
(x, ())
}
pub fn merge_ctx<C, T>(ctx: C, x: T) -> (C, T) {
(ctx, x)
}
pub trait ShortCircuit {
type Normal: Ss;
type Short: Ss;
type NormalWith<T: Ss>: ShortCircuit<Normal = T, Short = Self::Short> + Ss;
fn map<F, R: Ss>(self, f: F) -> Self::NormalWith<R>
where
F: FnOnce(Self::Normal) -> R;
fn or_else<F>(self, f: F) -> Self
where
F: FnOnce(Self::Short) -> Self;
}
impl<T: Ss> ShortCircuit for Option<T> {
type Normal = T;
type Short = ();
type NormalWith<U: Ss> = Option<U>;
fn map<F, R: Ss>(self, f: F) -> Self::NormalWith<R>
where
F: FnOnce(Self::Normal) -> R,
{
Option::map(self, f)
}
fn or_else<F>(self, f: F) -> Self
where
F: FnOnce(Self::Short) -> Self,
{
Option::or_else(self, || f(()))
}
}
impl<T: Ss, E: Ss> ShortCircuit for Result<T, E> {
type Normal = T;
type Short = E;
type NormalWith<U: Ss> = Result<U, E>;
fn map<F, R: Ss>(self, f: F) -> Self::NormalWith<R>
where
F: FnOnce(Self::Normal) -> R,
{
Result::map(self, f)
}
fn or_else<F>(self, f: F) -> Self
where
F: FnOnce(Self::Short) -> Self,
{
Result::or_else(self, f)
}
}
pub trait EffectiveExt<'lt>: Effective<'lt> {
fn map_normal<'wrap, F: Ss, R: Ss>(
self,
f: F,
) -> ErasedEffective<'wrap, <Self::Output as ShortCircuit>::NormalWith<R>, Self::Effect>
where
F: FnOnce(<Self::Output as ShortCircuit>::Normal) -> R,
Self::Output: ShortCircuit,
'lt: 'wrap;
fn or_else<'wrap, F: Ss>(self, f: F) -> ErasedEffective<'wrap, Self::Output, Self::Effect>
where
F: FnOnce(<Self::Output as ShortCircuit>::Short) -> Self::Output,
Self::Output: ShortCircuit,
'lt: 'wrap;
fn demo(self)
where
Self: Effective<'lt, Output = (i32, i32)>;
fn as_ctx_or_else<'ctx: 'lt, 'wrap, T: Ss, U: Ss, F: Ss>(
self,
f: F,
) -> ErasedEffective<'wrap, (T, Option<U>), Self::Effect>
where
Self: Effective<'lt, Output = (T, Option<U>)>,
F: for<'b> FnOnce(&'b mut T) -> ErasedEffective<'b, Option<U>, Self::Effect, &'ctx ()>,
'lt: 'wrap;
}
impl<'lt, T: Effective<'lt>> EffectiveExt<'lt> for T {
fn map_normal<'wrap, F: Ss, R: Ss>(
self,
f: F,
) -> ErasedEffective<'wrap, <Self::Output as ShortCircuit>::NormalWith<R>, Self::Effect>
where
F: FnOnce(<Self::Output as ShortCircuit>::Normal) -> R,
Self::Output: ShortCircuit,
'lt: 'wrap,
{
self.map(move |value| value.map(f))
}
fn or_else<'wrap, F: Ss>(self, f: F) -> ErasedEffective<'wrap, Self::Output, Self::Effect>
where
F: FnOnce(<Self::Output as ShortCircuit>::Short) -> Self::Output,
Self::Output: ShortCircuit,
'lt: 'wrap,
{
self.map(move |value| value.or_else(f))
}
fn demo(self)
where
Self: Effective<'lt, Output = (i32, i32)>,
{
self.map(|(a, b)| a + b);
}
fn as_ctx_or_else<'ctx: 'lt, 'wrap, N: Ss, U: Ss, F: Ss>(
self,
f: F,
) -> ErasedEffective<'wrap, (N, Option<U>), Self::Effect>
where
Self: Effective<'lt, Output = (N, Option<U>)>,
F: for<'b> FnOnce(&'b mut N) -> ErasedEffective<'b, Option<U>, Self::Effect, &'ctx ()>,
'lt: 'wrap,
{
self.as_ctx(
// Replace everything in Effective with just the loop construct which can skip
// the as_ctx body.
all_ctx,
|(ctx, value), _| {
if value.is_some() {
<Self::Effect as Effect>::ready(None).into_erased()
} else {
f(ctx)
}
},
|(ctx, a), b| match (a, b) {
(None, None) => (ctx, None),
(Some(v), None) | (None, Some(v)) => (ctx, Some(v)),
(Some(_), Some(_)) => unreachable!(),
},
)
}
}
pub trait Effective<'lt>: Ss + 'lt {
fn into_erased<B>(self) -> ErasedEffective<'lt, Self::Output, Self::Effect, B>;
/// The effect the effective belongs to.
type Effect: Effect;
/// The type of the effective's output value.
type Output: Ss + 'lt;
/// Future that resolves to the same value as the effective.
type IntoFuture: Ss + Future<Output = Self::Output>;
/// Convert the effective into a general future for use in async.
fn into_future(self) -> Self::IntoFuture;
fn map<'wrap, T: Ss, F: Ss>(self, cb: F) -> ErasedEffective<'wrap, T, Self::Effect>
where
F: FnOnce(Self::Output) -> T,
'lt: 'wrap;
fn then<'wrap, T: Ss, F: Ss>(self, cb: F) -> ErasedEffective<'wrap, T, Self::Effect>
where
F: FnOnce(Self::Output) -> ErasedEffective<'wrap, T, Self::Effect>,
'lt: 'wrap;
fn as_ctx<'ctx: 'lt, 'wrap, T: Ss, S: Ss, C: Ss, U: Ss, F: Ss, A: Ss, R: Ss>(
self,
split: S,
cb: F,
after: A,
) -> ErasedEffective<'wrap, R, Self::Effect>
where
S: FnOnce(Self::Output) -> (C, U),
F: for<'b> FnOnce(&'b mut C, U) -> ErasedEffective<'b, T, Self::Effect, &'ctx ()>,
A: FnOnce(C, T) -> R,
'lt: 'wrap;
fn r#loop<'ctx: 'lt, 'wrap, T: Ss, F: Ss>(
self,
cb: F,
) -> ErasedEffective<'wrap, (Self::Output, T), Self::Effect>
where
F: for<'b> FnMut(
&'b mut Self::Output,
) -> ErasedEffective<'b, ControlFlow<T>, Self::Effect, &'ctx ()>,
'lt: 'wrap;
}
pub trait TryEffective<'lt>: Effective<'lt, Output = Result<Self::Ok, Self::Err>> {
type Ok: Ss + 'lt;
type Err: Ss + 'lt;
}
impl<'lt, T, Ok, Err> TryEffective<'lt> for T
where
T: Effective<'lt, Output = Result<Ok, Err>>,
Ok: Ss + 'lt,
Err: Ss + 'lt,
{
type Ok = Ok;
type Err = Err;
}
pub trait Join {
type Effect: Effect;
type Two<'a, T0: Ss + 'a, T1: Ss + 'a>: Effective<
'a,
Output = (T0::Output, T1::Output),
Effect = Self::Effect,
>
where
T0: Effective<'a, Effect = Self::Effect>,
T1: Effective<'a, Effect = Self::Effect>;
type Three<'a, T0: Ss + 'a, T1: Ss + 'a, T2: Ss + 'a>: Effective<
'a,
Output = (T0::Output, T1::Output, T2::Output),
Effect = Self::Effect,
>
where
T0: Effective<'a, Effect = Self::Effect>,
T1: Effective<'a, Effect = Self::Effect>,
T2: Effective<'a, Effect = Self::Effect>;
fn two<'a, T0: Ss + 'a, T1: Ss + 'a>(effectives: (T0, T1)) -> Self::Two<'a, T0, T1>
where
T0: Effective<'a, Effect = Self::Effect>,
T1: Effective<'a, Effect = Self::Effect>;
fn three<'a, T0: Ss + 'a, T1: Ss + 'a, T2: Ss + 'a>(
effectives: (T0, T1, T2),
) -> Self::Three<'a, T0, T1, T2>
where
T0: Effective<'a, Effect = Self::Effect>,
T1: Effective<'a, Effect = Self::Effect>,
T2: Effective<'a, Effect = Self::Effect>;
}
pub trait TryJoin {
type Effect: Effect;
type Two<'a, T0: Ss + 'a, T1: Ss + 'a>: Effective<
'a,
Output = Result<(T0::Ok, T1::Ok), T0::Err>,
Effect = Self::Effect,
>
where
T0: TryEffective<'a, Effect = Self::Effect>,
T1: TryEffective<'a, Err = T0::Err, Effect = Self::Effect>;
type Three<'a, T0: Ss + 'a, T1: Ss + 'a, T2: Ss + 'a>: Effective<
'a,
Output = Result<(T0::Ok, T1::Ok, T2::Ok), T0::Err>,
Effect = Self::Effect,
>
where
T0: TryEffective<'a, Effect = Self::Effect>,
T1: TryEffective<'a, Err = T0::Err, Effect = Self::Effect>,
T2: TryEffective<'a, Err = T0::Err, Effect = Self::Effect>;
fn two<'a, T0: Ss + 'a, T1: Ss + 'a, F0: Ss + 'a, F1: Ss + 'a>(
cb: (F0, F1),
) -> Self::Two<'a, T0, T1>
where
T0: TryEffective<'a, Effect = Self::Effect>,
T1: TryEffective<'a, Err = T0::Err, Effect = Self::Effect>,
F0: FnOnce() -> T0,
F1: FnOnce() -> T1;
fn three<'a, T0: Ss + 'a, T1: Ss + 'a, T2: Ss + 'a, F0: Ss + 'a, F1: Ss + 'a, F2: Ss + 'a>(
cb: (F0, F1, F2),
) -> Self::Three<'a, T0, T1, T2>
where
T0: TryEffective<'a, Effect = Self::Effect>,
T1: TryEffective<'a, Err = T0::Err, Effect = Self::Effect>,
T2: TryEffective<'a, Err = T0::Err, Effect = Self::Effect>,
F0: FnOnce() -> T0,
F1: FnOnce() -> T1,
F2: FnOnce() -> T2;
}
pub fn join<'lt, E: Effect, T: Joinable<'lt, E>>(x: T) -> T::Output {
x.join()
}
pub trait Joinable<'lt, E: Effect> {
type Output: Effective<'lt, Effect = E>;
fn join(self) -> Self::Output;
}
impl<'lt, E: Effect> Joinable<'lt, E> for () {
type Output = ErasedEffective<'lt, (), E>;
fn join(self) -> Self::Output {
E::ready(())
}
}
impl<'lt, E: Effect, T0> Joinable<'lt, E> for (T0,)
where
T0: Effective<'lt, Effect = E>,
{
type Output = T0;
fn join(self) -> Self::Output {
self.0
}
}
impl<'lt, E: Effect, T0, T1> Joinable<'lt, E> for (T0, T1)
where
T0: Effective<'lt, Effect = E>,
T1: Effective<'lt, Effect = E>,
{
type Output = <E as Join>::Two<'lt, T0, T1>;
fn join(self) -> Self::Output {
<E as Join>::two(self)
}
}
impl<'lt, E: Effect, T0, T1, T2> Joinable<'lt, E> for (T0, T1, T2)
where
T0: Effective<'lt, Effect = E>,
T1: Effective<'lt, Effect = E>,
T2: Effective<'lt, Effect = E>,
{
type Output = <E as Join>::Three<'lt, T0, T1, T2>;
fn join(self) -> Self::Output {
<E as Join>::three(self)
}
}
pub fn try_join<'lt, E: Effect, T: TryJoinable<'lt, E>>(x: T) -> T::Output {
x.join()
}
pub trait TryJoinable<'lt, E: Effect> {
type Output: TryEffective<'lt, Effect = E>;
fn join(self) -> Self::Output;
}
impl<'lt, E: Effect> TryJoinable<'lt, E> for () {
type Output = ErasedEffective<'lt, Result<(), ()>, E>;
fn join(self) -> Self::Output {
E::ready(Ok(()))
}
}
impl<'lt, E: Effect, F0, T0> TryJoinable<'lt, E> for (F0,)
where
F0: FnOnce() -> T0,
T0: TryEffective<'lt, Effect = E>,
{
type Output = T0;
fn join(self) -> Self::Output {
self.0()
}
}
impl<'lt, E: Effect, F0, F1, T0, T1> TryJoinable<'lt, E> for (F0, F1)
where
F0: FnOnce() -> T0 + Ss + 'lt,
F1: FnOnce() -> T1 + Ss + 'lt,
T0: TryEffective<'lt, Effect = E>,
T1: TryEffective<'lt, Err = T0::Err, Effect = E>,
{
type Output = <E as TryJoin>::Two<'lt, T0, T1>;
fn join(self) -> Self::Output {
<E as TryJoin>::two(self)
}
}