pub mod r#async; pub mod blocking; use core::{future::Future, ops::ControlFlow}; use crate::never::Never; // 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 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 { /// Accessor to inject arbitrary higher-ranked implied bounds. /// See TODO for more info. type Hrt: 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 = ()> = < < ::Erased as Erased::Hkt >::Hrt as Erased::ForLt<'lt, Output, E, &'lt (Output, Bound)> >::Effective; pub trait BlockOn: Ss + Sized + 'static { fn block_on(future: F) -> F::Output where F: core::future::Future + Ss, ::Output: Ss; } pub trait Effect: Join + TryJoin + Ss + Sized + 'static { type BlockOn: BlockOn; type Erased: Erased::Hkt; fn ready<'a, T: Ss + 'a>(value: T) -> ErasedEffective<'a, T, Self>; fn from_future<'a, F>(future: F) -> ErasedEffective<'a, F::Output, Self> where F: Ss + 'a + Future, F::Output: Ss + 'a; } pub trait ReadyExt<'lt, Eff: Effective<'lt, Output = Self>>: Ss + Sized { fn ready(self) -> Eff; } impl<'lt, Eff: Effective<'lt, Output = T>, T: Ss> ReadyExt<'lt, Eff> for T { fn ready(self) -> Eff { Eff::ready(self) } } pub trait ConvertShort { fn convert_short(short: Self) -> T; } pub trait FromShort::Short> { fn from_short(short: S) -> Self; } impl FromShort for T where S: ConvertShort, { fn from_short(short: S) -> Self { S::convert_short(short) } } pub trait ShortCircuit: FromShort { type Output; type Short; fn from_output(output: Self::Output) -> Self; fn branch(self) -> ControlFlow; } pub fn effective_from_short<'lt, Eff, S>(short: S) -> Eff where Eff: Effective<'lt>, Eff::Output: FromShort, { Eff::ready(FromShort::from_short(short)) } impl ConvertShort> for Option { fn convert_short(short: Option) -> Option { match short { None => None, Some(_) => unreachable!(), } } } impl ShortCircuit for Option { type Output = T; type Short = Option; fn branch(self) -> ControlFlow { match self { Some(v) => ControlFlow::Continue(v), None => ControlFlow::Break(None), } } fn from_output(output: Self::Output) -> Self { Some(output) } } impl ConvertShort> for Result { fn convert_short(short: Result) -> Result { match short { Err(err) => Err(err), Ok(_) => unreachable!(), } } } impl ShortCircuit for Result { type Output = T; type Short = Result; fn branch(self) -> ControlFlow { match self { Ok(v) => ControlFlow::Continue(v), Err(err) => ControlFlow::Break(Err(err)), } } fn from_output(output: Self::Output) -> Self { Ok(output) } } pub trait ResultErrorExt { fn into_error(self) -> E; } impl ResultErrorExt for Result { fn into_error(self) -> E { match self { Ok(_) => unreachable!(), Err(err) => err, } } } #[macro_export] macro_rules! tri { ($expr:expr $(,)?) => { match $crate::effect::ShortCircuit::branch($expr) { ::core::ops::ControlFlow::Continue(val) => val, ::core::ops::ControlFlow::Break(short) => { return $crate::effect::effective_from_short(short); } } }; } pub use tri; pub trait EffectExt: Effect { fn repeat_map<'ctx, 'wrap, I, U, F>(input: I, f: F) -> ErasedEffective<'wrap, U, Self> where F: 'wrap + for<'temp> FnMut(&'temp mut I) -> ErasedEffective<'temp, ControlFlow, Self, &'ctx ()>, I: Ss + 'wrap, U: Ss, F: Ss, 'ctx: 'wrap, { Self::ready(input).repeat_map(f) } #[inline(always)] fn as_ctx<'ctx, 'wrap, I, U, F>(input: I, f: F) -> ErasedEffective<'wrap, (I, U), Self> where F: 'wrap + for<'temp> FnOnce(&'temp mut I) -> ErasedEffective<'temp, U, Self, &'ctx ()>, I: Ss + 'wrap, U: Ss, F: Ss, 'ctx: 'wrap, { Self::ready(input).as_ctx(f) } fn as_ctx_map<'ctx, 'wrap, I, U, F>(input: I, f: F) -> ErasedEffective<'wrap, U, Self> where F: 'wrap + for<'temp> FnOnce(&'temp mut I) -> ErasedEffective<'temp, U, Self, &'ctx ()>, I: Ss + 'wrap, U: Ss, F: Ss, 'ctx: 'wrap, { Self::ready(input).as_ctx_map(f) } } impl EffectExt for T {} pub trait EffectiveExt<'lt>: Effective<'lt> { fn remove_ctx<'wrap, Ctx: Ss, T: Ss>(self) -> ErasedEffective<'wrap, T, Self::Effect> where Self: Effective<'lt, Output = (Ctx, T)>, 'lt: 'wrap, { self.map(|(_, value)| value) } fn remove_value<'wrap, Ctx: Ss, T: Ss>(self) -> ErasedEffective<'wrap, Ctx, Self::Effect> where Self: Effective<'lt, Output = (Ctx, T)>, 'lt: 'wrap, { self.map(|(ctx, _)| ctx) } fn map<'wrap, U, F>(self, f: F) -> ErasedEffective<'wrap, U, Self::Effect> where F: 'wrap + FnOnce(Self::Output) -> U, U: Ss, F: Ss, 'lt: 'wrap, { self.r#do( |v| ((), ControlFlow::<_, Never>::Break(f(v))), |_, _| unreachable!(), |_, _: Never| unreachable!(), |_, _: &mut Never| unreachable!(), |_, _, _: Never| unreachable!(), |_, _, v| v, ) } fn then<'wrap, U, F>(self, f: F) -> ErasedEffective<'wrap, U, Self::Effect> where F: 'wrap + FnOnce(Self::Output) -> ErasedEffective<'wrap, U, Self::Effect>, U: Ss, F: Ss, 'lt: 'wrap, { self.r#do( |v| ((), ControlFlow::Continue(v)), |_, v| f(v).cast(), |_, v| ControlFlow::<_, Never>::Break(v), |_, _| unreachable!(), |_, _, _: Never| unreachable!(), |_, _, v| v, ) } fn or_else<'wrap, U, F>(self, f: F) -> ErasedEffective<'wrap, Option, Self::Effect> where Self: Effective<'lt, Output = Option>, F: 'wrap + FnOnce() -> ErasedEffective<'wrap, Option, Self::Effect>, U: Ss, F: Ss, 'lt: 'wrap, { self.r#do( |v| { ( (), match v { Some(v) => ControlFlow::Break(Some(v)), None => ControlFlow::Continue(()), }, ) }, |_, _| f().cast(), |_, v| ControlFlow::<_, Never>::Break(v), |_, _| unreachable!(), |_, _, _: Never| unreachable!(), |_, _, v| v, ) } #[allow(clippy::wrong_self_convention)] #[inline(always)] fn as_ctx<'ctx, 'wrap, U, F>( self, f: F, ) -> ErasedEffective<'wrap, (Self::Output, U), Self::Effect> where F: 'wrap + for<'temp> FnOnce( &'temp mut Self::Output, ) -> ErasedEffective<'temp, U, Self::Effect, &'ctx ()>, U: Ss, F: Ss, 'ctx: 'lt, 'lt: 'wrap, { self.r#do( |ctx| (ctx, ControlFlow::Continue(())), #[inline(always)] |ctx, _| f(ctx).cast(), |_, v| ControlFlow::<_, Never>::Break(v), |_, _| unreachable!(), |_, _, _: Never| unreachable!(), |ctx, _, v| (ctx, v), ) } #[allow(clippy::wrong_self_convention)] fn as_ctx_map<'ctx, 'wrap, U, F>(self, f: F) -> ErasedEffective<'wrap, U, Self::Effect> where F: 'wrap + for<'temp> FnOnce( &'temp mut Self::Output, ) -> ErasedEffective<'temp, U, Self::Effect, &'ctx ()>, 'ctx: 'lt, U: Ss, F: Ss, 'lt: 'wrap, { self.r#do( |ctx| (ctx, ControlFlow::Continue(())), |ctx, _| f(ctx).cast(), |_, v| ControlFlow::<_, Never>::Break(v), |_, _| unreachable!(), |_, _, _: Never| unreachable!(), |_, _, v| v, ) } #[allow(clippy::wrong_self_convention)] fn as_ctx_or_else<'ctx, 'wrap, Ctx, U, F>( self, f: F, ) -> ErasedEffective<'wrap, (Ctx, Option), Self::Effect> where Self: Effective<'lt, Output = (Ctx, Option)>, F: 'wrap + for<'temp> FnOnce( &'temp mut Ctx, ) -> ErasedEffective<'temp, Option, Self::Effect, &'ctx ()>, 'ctx: 'lt, Ctx: Ss, U: Ss, F: Ss, 'lt: 'wrap, { self.r#do( |(ctx, v)| { ( ctx, match v { Some(v) => ControlFlow::Break(Some(v)), None => ControlFlow::Continue(()), }, ) }, |ctx, ()| f(ctx).cast(), |_, v| ControlFlow::<_, Never>::Break(v), |_, _| unreachable!(), |_, _, _: Never| unreachable!(), |ctx, _, v| (ctx, v), ) } fn or_else_update<'ctx, 'wrap, Ctx, U, F>( self, f: F, ) -> ErasedEffective<'wrap, (Ctx, U), Self::Effect> where Self: Effective<'lt, Output = (Ctx, U)>, F: 'wrap + for<'temp> FnOnce( &'temp mut Ctx, U::Short, ) -> ErasedEffective<'temp, U, Self::Effect, &'ctx ()>, U: ShortCircuit, U::Short: Ss, Ctx: Ss, U: Ss, F: Ss, 'ctx: 'lt, 'lt: 'wrap, { self.r#do( |(ctx, v)| { ( ctx, // The inverting of the control flow is because on breaks we want to run the // function not skip it. match U::branch(v) { ControlFlow::Continue(v) => ControlFlow::Break(U::from_output(v)), ControlFlow::Break(v) => ControlFlow::Continue(v), }, ) }, |ctx, v| f(ctx, v).cast(), |_, v| ControlFlow::<_, Never>::Break(v), |_, _| unreachable!(), |_, _, _: Never| unreachable!(), |ctx, _, v| (ctx, v), ) } #[allow(clippy::wrong_self_convention)] fn as_ctx_or_else_map<'ctx, 'wrap, Ctx, U, F>( self, f: F, ) -> ErasedEffective<'wrap, Option, Self::Effect> where Self: Effective<'lt, Output = (Ctx, Option)>, F: 'wrap + for<'temp> FnOnce( &'temp mut Ctx, ) -> ErasedEffective<'temp, Option, Self::Effect, &'ctx ()>, 'ctx: 'lt, Ctx: Ss, U: Ss, F: Ss, 'lt: 'wrap, { self.r#do( |(ctx, v)| { ( ctx, match v { Some(v) => ControlFlow::Break(Some(v)), None => ControlFlow::Continue(()), }, ) }, |ctx, ()| f(ctx).cast(), |_, v| ControlFlow::<_, Never>::Break(v), |_, _| unreachable!(), |_, _, _: Never| unreachable!(), |_, _, v| v, ) } fn repeat<'ctx, 'wrap, U, F>( self, mut f: F, ) -> ErasedEffective<'wrap, (Self::Output, U), Self::Effect> where F: 'wrap + for<'temp> FnMut( &'temp mut Self::Output, ) -> ErasedEffective<'temp, ControlFlow, Self::Effect, &'ctx ()>, 'ctx: 'lt, U: Ss, F: Ss, 'lt: 'wrap, { self.r#do( |ctx| (ctx, ControlFlow::Continue(())), |_, _| ::ready(()).cast(), |_, _| ControlFlow::Continue(()), move |ctx, _| f(ctx).cast(), |_, _, v| v, |ctx, _, v| (ctx, v), ) } fn repeat_map<'ctx, 'wrap, U, F>(self, mut f: F) -> ErasedEffective<'wrap, U, Self::Effect> where F: 'wrap + for<'temp> FnMut( &'temp mut Self::Output, ) -> ErasedEffective<'temp, ControlFlow, Self::Effect, &'ctx ()>, 'ctx: 'lt, U: Ss, F: Ss, 'lt: 'wrap, { self.r#do( |ctx| (ctx, ControlFlow::Continue(())), |_, _| ::ready(()).cast(), |_, _| ControlFlow::Continue(()), move |ctx, _| f(ctx).cast(), |_, _, v| v, |_, _, v| v, ) } } impl<'lt, T: Effective<'lt>> EffectiveExt<'lt> for T {} pub trait Effective<'lt>: Sized + Ss + 'lt { fn cast<'wrap, B>(self) -> ErasedEffective<'wrap, Self::Output, Self::Effect, B> where 'lt: 'wrap; /// 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; /// Convert the effective into a general future for use in async. fn into_future(self) -> Self::IntoFuture; fn ready(value: Self::Output) -> Self; fn r#do< 'ctx: 'lt, 'wrap, Pre, Ctx, Owned, First, FirstOutput, FirstPost, Done, Extra, Repeat, RepeatOutput, RepeatPost, Post, Return, >( self, pre: Pre, first: First, first_post: FirstPost, repeat: Repeat, repeat_post: RepeatPost, post: Post, ) -> ErasedEffective<'wrap, Return, Self::Effect> where Pre: Ss + 'wrap + FnOnce(Self::Output) -> (Ctx, ControlFlow), First: Ss + 'wrap + for<'temp> FnOnce( &'temp mut Ctx, Owned, ) -> ErasedEffective<'temp, FirstOutput, Self::Effect, &'wrap ()>, FirstPost: Ss + 'wrap + for<'temp> FnOnce(&'temp mut Ctx, FirstOutput) -> ControlFlow, Repeat: Ss + 'wrap + for<'temp> FnMut( &'temp mut Ctx, &'temp mut Extra, ) -> ErasedEffective<'temp, RepeatOutput, Self::Effect, &'wrap ()>, RepeatPost: Ss + 'wrap + for<'temp> FnMut(&'temp mut Ctx, &'temp mut Extra, RepeatOutput) -> ControlFlow, Post: Ss + 'wrap + FnOnce(Ctx, Option, Done) -> Return, Return: Ss, RepeatOutput: Ss, FirstOutput: Ss, Owned: Ss, Done: Ss, Ctx: Ss, Extra: Ss, 'lt: 'wrap; } pub trait TryEffective<'lt>: Effective<'lt, Output = Result> { type Ok: Ss + 'lt; type Err: Ss + 'lt; } impl<'lt, T, Ok, Err> TryEffective<'lt> for T where T: Effective<'lt, Output = Result>, Ok: Ss + 'lt, Err: Ss + 'lt, { type Ok = Ok; type Err = Err; } pub trait Join { type Effect: Effect; fn two<'a, T0, T1>( effectives: (T0, T1), ) -> ErasedEffective<'a, (T0::Output, T1::Output), Self::Effect> where T0: Ss + 'a + Effective<'a, Effect = Self::Effect>, T1: Ss + 'a + Effective<'a, Effect = Self::Effect>; fn three<'a, T0, T1, T2>( effectives: (T0, T1, T2), ) -> ErasedEffective<'a, (T0::Output, T1::Output, T2::Output), Self::Effect> where T0: Ss + 'a + Effective<'a, Effect = Self::Effect>, T1: Ss + 'a + Effective<'a, Effect = Self::Effect>, T2: Ss + 'a + Effective<'a, Effect = Self::Effect>; } pub trait TryJoin { type Effect: Effect; fn two<'a, T0, T1, F0, F1>( cb: (F0, F1), ) -> ErasedEffective<'a, Result<(T0::Ok, T1::Ok), T0::Err>, Self::Effect> where T0: Ss + 'a + TryEffective<'a, Effect = Self::Effect>, T1: Ss + 'a + TryEffective<'a, Err = T0::Err, Effect = Self::Effect>, F0: Ss + 'a + FnOnce() -> T0, F1: Ss + 'a + FnOnce() -> T1; fn three<'a, T0, T1, T2, F0, F1, F2>( cb: (F0, F1, F2), ) -> ErasedEffective<'a, Result<(T0::Ok, T1::Ok, T2::Ok), T0::Err>, Self::Effect> where T0: Ss + 'a + TryEffective<'a, Effect = Self::Effect>, T1: Ss + 'a + TryEffective<'a, Err = T0::Err, Effect = Self::Effect>, T2: Ss + 'a + TryEffective<'a, Err = T0::Err, Effect = Self::Effect>, F0: Ss + 'a + FnOnce() -> T0, F1: Ss + 'a + FnOnce() -> T1, F2: Ss + 'a + FnOnce() -> T2; } pub fn join<'lt, E: Effect, T: Joinable<'lt, E>>(x: T) -> ErasedEffective<'lt, T::Output, E> { x.join() } pub trait Joinable<'lt, E: Effect> { type Output: Ss; fn join(self) -> ErasedEffective<'lt, Self::Output, E>; } impl<'lt, E: Effect> Joinable<'lt, E> for () { type Output = (); fn join(self) -> ErasedEffective<'lt, (), E> { E::ready(()) } } impl<'lt, E: Effect, T0> Joinable<'lt, E> for (T0,) where T0: Effective<'lt, Effect = E>, { type Output = (T0::Output,); fn join(self) -> ErasedEffective<'lt, (T0::Output,), E> { self.0.map(|x| (x,)) } } 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 = (T0::Output, T1::Output); fn join(self) -> ErasedEffective<'lt, Self::Output, E> { ::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 = (T0::Output, T1::Output, T2::Output); fn join(self) -> ErasedEffective<'lt, Self::Output, E> { ::three(self) } } pub fn try_join<'lt, E: Effect, T: TryJoinable<'lt, E>>( x: T, ) -> ErasedEffective<'lt, Result, E> { x.join() } pub trait TryJoinable<'lt, E: Effect> { type Ok: Ss; type Err: Ss; fn join(self) -> ErasedEffective<'lt, Result, E>; } impl<'lt, E: Effect> TryJoinable<'lt, E> for () { fn join(self) -> ErasedEffective<'lt, Result, E> { E::ready(Ok(())) } type Ok = (); type Err = Never; } impl<'lt, E: Effect, F0, T0> TryJoinable<'lt, E> for (F0,) where F0: FnOnce() -> T0, T0: TryEffective<'lt, Effect = E>, { fn join(self) -> ErasedEffective<'lt, Result, E> { self.0().map(|x| x.map(|x| (x,))) } type Ok = (T0::Ok,); type Err = T0::Err; } 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>, { fn join(self) -> ErasedEffective<'lt, Result, E> { ::two(self) } type Ok = (T0::Ok, T1::Ok); type Err = T0::Err; } impl<'lt, E: Effect, F0, F1, F2, T0, T1, T2> TryJoinable<'lt, E> for (F0, F1, F2) where F0: FnOnce() -> T0 + Ss + 'lt, F1: FnOnce() -> T1 + Ss + 'lt, F2: FnOnce() -> T2 + Ss + 'lt, T0: TryEffective<'lt, Effect = E>, T1: TryEffective<'lt, Err = T0::Err, Effect = E>, T2: TryEffective<'lt, Err = T0::Err, Effect = E>, { fn join(self) -> ErasedEffective<'lt, Result, E> { ::three(self) } type Ok = (T0::Ok, T1::Ok, T2::Ok); type Err = T0::Err; }