Diffstat (limited to 'src/effect.rs')
-rw-r--r--src/effect.rs473
1 files changed, 351 insertions, 122 deletions
diff --git a/src/effect.rs b/src/effect.rs
index 0f85ee3..458ed8a 100644
--- a/src/effect.rs
+++ b/src/effect.rs
@@ -3,7 +3,7 @@ pub mod blocking;
use core::{future::Future, ops::ControlFlow};
-use crate::{higher_ranked_trait, higher_ranked_type, hkt::Marker};
+use crate::{higher_ranked_trait, higher_ranked_type, hkt::Marker, 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.
@@ -106,164 +106,364 @@ pub trait Effect: Join<Effect = Self> + TryJoin<Effect = Self> + Ss + Sized + 's
F::Output: Ss + 'a;
}
-pub fn all_ctx<T>(x: T) -> (T, ()) {
- (x, ())
+pub trait ReadyExt<'lt, Eff: Effective<'lt, Output = Self>>: Ss + Sized {
+ fn ready(self) -> Eff;
}
-pub fn merge_ctx<C, T>(ctx: C, x: T) -> (C, T) {
- (ctx, x)
+impl<'lt, Eff: Effective<'lt, Output = T>, T: Ss> ReadyExt<'lt, Eff> for T {
+ fn ready(self) -> Eff {
+ Eff::ready(self)
+ }
+}
+
+pub trait ConvertShort<T> {
+ fn convert_short(short: Self) -> T;
}
-pub trait ShortCircuit {
- type Normal: Ss;
+pub trait FromShort<S = <Self as ShortCircuit>::Short> {
+ fn from_short(short: S) -> Self;
+}
- type Short: Ss;
+impl<T, S> FromShort<S> for T
+where
+ S: ConvertShort<T>,
+{
+ fn from_short(short: S) -> Self {
+ S::convert_short(short)
+ }
+}
- type NormalWith<T: Ss>: ShortCircuit<Normal = T, Short = Self::Short> + Ss;
+pub trait ShortCircuit: FromShort {
+ type Output;
+ type Short;
- fn map<F, R: Ss>(self, f: F) -> Self::NormalWith<R>
- where
- F: FnOnce(Self::Normal) -> R;
+ fn from_output(output: Self::Output) -> Self;
+ fn branch(self) -> ControlFlow<Self::Short, Self::Output>;
+}
- fn or_else<F>(self, f: F) -> Self
- where
- F: FnOnce(Self::Short) -> Self;
+pub fn effective_from_short<'lt, Eff, S>(short: S) -> Eff
+where
+ Eff: Effective<'lt>,
+ Eff::Output: FromShort<S>,
+{
+ Eff::ready(FromShort::from_short(short))
+}
+
+impl<T> ConvertShort<Option<T>> for Option<Never> {
+ fn convert_short(short: Option<Never>) -> Option<T> {
+ match short {
+ None => None,
+ Some(_) => unreachable!(),
+ }
+ }
}
-impl<T: Ss> ShortCircuit for Option<T> {
- type Normal = T;
+impl<T> ShortCircuit for Option<T> {
+ type Output = T;
- type Short = ();
+ type Short = Option<Never>;
- type NormalWith<U: Ss> = Option<U>;
+ fn branch(self) -> ControlFlow<Self::Short, Self::Output> {
+ match self {
+ Some(v) => ControlFlow::Continue(v),
+ None => ControlFlow::Break(None),
+ }
+ }
- fn map<F, R: Ss>(self, f: F) -> Self::NormalWith<R>
- where
- F: FnOnce(Self::Normal) -> R,
- {
- Option::map(self, f)
+ fn from_output(output: Self::Output) -> Self {
+ Some(output)
}
+}
- fn or_else<F>(self, f: F) -> Self
- where
- F: FnOnce(Self::Short) -> Self,
- {
- Option::or_else(self, || f(()))
+impl<T, E> ConvertShort<Result<T, E>> for Result<Never, E> {
+ fn convert_short(short: Result<Never, E>) -> Result<T, E> {
+ match short {
+ Err(err) => Err(err),
+ Ok(_) => unreachable!(),
+ }
}
}
-impl<T: Ss, E: Ss> ShortCircuit for Result<T, E> {
- type Normal = T;
+impl<T, E> ShortCircuit for Result<T, E> {
+ type Output = T;
+
+ type Short = Result<Never, E>;
+
+ fn branch(self) -> ControlFlow<Self::Short, Self::Output> {
+ match self {
+ Ok(v) => ControlFlow::Continue(v),
+ Err(err) => ControlFlow::Break(Err(err)),
+ }
+ }
+
+ fn from_output(output: Self::Output) -> Self {
+ Ok(output)
+ }
+}
- type Short = E;
+#[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;
- type NormalWith<U: Ss> = Result<U, E>;
+pub trait EffectExt: Effect {
+ fn repeat_map<'ctx, 'wrap, I: Ss + 'wrap, U: Ss, F: Ss>(
+ input: I,
+ f: F,
+ ) -> ErasedEffective<'wrap, U, Self>
+ where
+ F: for<'temp> FnMut(&'temp mut I) -> ErasedEffective<'temp, ControlFlow<U>, Self, &'ctx ()>,
+ 'ctx: 'wrap,
+ {
+ Self::ready(input).repeat_map(f)
+ }
- fn map<F, R: Ss>(self, f: F) -> Self::NormalWith<R>
+ #[inline(always)]
+ fn as_ctx<'ctx, 'wrap, I: Ss + 'wrap, U: Ss, F: Ss>(
+ input: I,
+ f: F,
+ ) -> ErasedEffective<'wrap, (I, U), Self>
where
- F: FnOnce(Self::Normal) -> R,
+ F: for<'temp> FnOnce(&'temp mut I) -> ErasedEffective<'temp, U, Self, &'ctx ()>,
+ 'ctx: 'wrap,
{
- Result::map(self, f)
+ Self::ready(input).as_ctx(f)
}
- fn or_else<F>(self, f: F) -> Self
+ fn as_ctx_map<'ctx, 'wrap, I: Ss + 'wrap, U: Ss, F: Ss>(
+ input: I,
+ f: F,
+ ) -> ErasedEffective<'wrap, U, Self>
where
- F: FnOnce(Self::Short) -> Self,
+ F: for<'temp> FnOnce(&'temp mut I) -> ErasedEffective<'temp, U, Self, &'ctx ()>,
+ 'ctx: 'wrap,
{
- Result::or_else(self, f)
+ Self::ready(input).as_ctx_map(f)
}
}
+impl<T: Effect> EffectExt for T {}
+
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>
+ fn remove_ctx<'wrap, Ctx: Ss, T: Ss>(self) -> ErasedEffective<'wrap, T, Self::Effect>
where
- F: FnOnce(<Self::Output as ShortCircuit>::Normal) -> R,
- Self::Output: ShortCircuit,
- 'lt: 'wrap;
+ Self: Effective<'lt, Output = (Ctx, T)>,
+ 'lt: 'wrap,
+ {
+ self.map(|(_, value)| value)
+ }
- fn or_else<'wrap, F: Ss>(self, f: F) -> ErasedEffective<'wrap, Self::Output, Self::Effect>
+ fn map<'wrap, U: Ss, F: Ss>(self, f: F) -> ErasedEffective<'wrap, U, Self::Effect>
where
- F: FnOnce(<Self::Output as ShortCircuit>::Short) -> Self::Output,
- Self::Output: ShortCircuit,
- 'lt: 'wrap;
+ F: FnOnce(Self::Output) -> U,
+ 'lt: 'wrap,
+ {
+ self.r#do(
+ |v| ((), ControlFlow::<_, Never>::Break(f(v))),
+ |_, _| unreachable!(),
+ |_, _: Never| unreachable!(),
+ |_, _: &mut Never| unreachable!(),
+ |_, _, _: Never| unreachable!(),
+ |_, _, v| v,
+ )
+ }
- fn demo(self)
+ fn then<'wrap, U: Ss, F: Ss>(self, f: F) -> ErasedEffective<'wrap, U, Self::Effect>
where
- Self: Effective<'lt, Output = (i32, i32)>;
+ F: FnOnce(Self::Output) -> ErasedEffective<'wrap, U, Self::Effect>,
+ 'lt: 'wrap,
+ {
+ self.r#do(
+ |v| ((), ControlFlow::Continue(v)),
+ |_, v| f(v).cast(),
+ |_, v| ControlFlow::<_, Never>::Break(v),
+ |_, _| unreachable!(),
+ |_, _, _: Never| unreachable!(),
+ |_, _, v| v,
+ )
+ }
- fn as_ctx_or_else<'ctx: 'lt, 'wrap, T: Ss, U: Ss, F: Ss>(
- self,
- f: F,
- ) -> ErasedEffective<'wrap, (T, Option<U>), Self::Effect>
+ fn or_else<'wrap, U: Ss, F: Ss>(self, f: F) -> ErasedEffective<'wrap, 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;
-}
+ Self: Effective<'lt, Output = Option<U>>,
+ F: FnOnce() -> ErasedEffective<'wrap, Option<U>, Self::Effect>,
+ '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,
+ )
+ }
-impl<'lt, T: Effective<'lt>> EffectiveExt<'lt> for T {
- fn map_normal<'wrap, F: Ss, R: Ss>(
+ #[inline(always)]
+ fn as_ctx<'ctx: 'lt, 'wrap, U: Ss, F: Ss>(
self,
f: F,
- ) -> ErasedEffective<'wrap, <Self::Output as ShortCircuit>::NormalWith<R>, Self::Effect>
+ ) -> ErasedEffective<'wrap, (Self::Output, U), Self::Effect>
where
- F: FnOnce(<Self::Output as ShortCircuit>::Normal) -> R,
- Self::Output: ShortCircuit,
+ F: for<'temp> FnOnce(
+ &'temp mut Self::Output,
+ ) -> ErasedEffective<'temp, U, Self::Effect, &'ctx ()>,
'lt: 'wrap,
{
- self.map(move |value| value.map(f))
+ 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),
+ )
}
- fn or_else<'wrap, F: Ss>(self, f: F) -> ErasedEffective<'wrap, Self::Output, Self::Effect>
+ fn as_ctx_map<'ctx: 'lt, 'wrap, U: Ss, F: Ss>(
+ self,
+ f: F,
+ ) -> ErasedEffective<'wrap, U, Self::Effect>
where
- F: FnOnce(<Self::Output as ShortCircuit>::Short) -> Self::Output,
- Self::Output: ShortCircuit,
+ F: for<'temp> FnOnce(
+ &'temp mut Self::Output,
+ ) -> ErasedEffective<'temp, U, Self::Effect, &'ctx ()>,
'lt: 'wrap,
{
- self.map(move |value| value.or_else(f))
+ self.r#do(
+ |ctx| (ctx, ControlFlow::Continue(())),
+ |ctx, _| f(ctx).cast(),
+ |_, v| ControlFlow::<_, Never>::Break(v),
+ |_, _| unreachable!(),
+ |_, _, _: Never| unreachable!(),
+ |_, _, v| v,
+ )
}
- fn demo(self)
+ fn as_ctx_or_else<'ctx: 'lt, 'wrap, Ctx: Ss, U: Ss, F: Ss>(
+ self,
+ f: F,
+ ) -> ErasedEffective<'wrap, (Ctx, Option<U>), Self::Effect>
where
- Self: Effective<'lt, Output = (i32, i32)>,
+ Self: Effective<'lt, Output = (Ctx, Option<U>)>,
+ F: for<'temp> FnOnce(
+ &'temp mut Ctx,
+ ) -> ErasedEffective<'temp, Option<U>, Self::Effect, &'ctx ()>,
+ 'lt: 'wrap,
{
- self.map(|(a, b)| a + b);
+ 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 as_ctx_or_else<'ctx: 'lt, 'wrap, N: Ss, U: Ss, F: Ss>(
+ fn as_ctx_or_else_map<'ctx: 'lt, 'wrap, Ctx: Ss, U: Ss, F: Ss>(
self,
f: F,
- ) -> ErasedEffective<'wrap, (N, Option<U>), Self::Effect>
+ ) -> ErasedEffective<'wrap, 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 ()>,
+ Self: Effective<'lt, Output = (Ctx, Option<U>)>,
+ F: for<'temp> FnOnce(
+ &'temp mut Ctx,
+ ) -> ErasedEffective<'temp, 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!(),
+ 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: 'lt, 'wrap, U: Ss, F: Ss>(
+ self,
+ mut f: F,
+ ) -> ErasedEffective<'wrap, (Self::Output, U), Self::Effect>
+ where
+ F: for<'temp> FnMut(
+ &'temp mut Self::Output,
+ )
+ -> ErasedEffective<'temp, ControlFlow<U>, Self::Effect, &'ctx ()>,
+ 'lt: 'wrap,
+ {
+ self.r#do(
+ |ctx| (ctx, ControlFlow::Continue(())),
+ |_, _| <Self::Effect as Effect>::ready(()).cast(),
+ |_, _| ControlFlow::Continue(()),
+ |ctx, _| f(ctx).cast(),
+ |_, _, v| v,
+ |ctx, _, v| (ctx, v),
+ )
+ }
+
+ fn repeat_map<'ctx: 'lt, 'wrap, U: Ss, F: Ss>(
+ self,
+ mut f: F,
+ ) -> ErasedEffective<'wrap, U, Self::Effect>
+ where
+ F: for<'temp> FnMut(
+ &'temp mut Self::Output,
+ )
+ -> ErasedEffective<'temp, ControlFlow<U>, Self::Effect, &'ctx ()>,
+ 'lt: 'wrap,
+ {
+ self.r#do(
+ |ctx| (ctx, ControlFlow::Continue(())),
+ |_, _| <Self::Effect as Effect>::ready(()).cast(),
+ |_, _| ControlFlow::Continue(()),
+ |ctx, _| f(ctx).cast(),
+ |_, _, v| v,
+ |_, _, v| v,
)
}
}
-pub trait Effective<'lt>: Ss + 'lt {
- fn into_erased<B>(self) -> ErasedEffective<'lt, Self::Output, Self::Effect, B>;
+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;
@@ -277,36 +477,49 @@ pub trait Effective<'lt>: Ss + 'lt {
/// 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>(
+ fn ready(value: Self::Output) -> Self;
+
+ fn r#do<
+ 'ctx: 'lt,
+ 'wrap,
+ Pre: Ss,
+ Ctx: Ss,
+ Owned: Ss,
+ First: Ss,
+ FirstOutput: Ss,
+ FirstPost: Ss,
+ Done: Ss,
+ Extra: Ss,
+ Repeat: Ss,
+ RepeatOutput: Ss,
+ RepeatPost: Ss,
+ Post: Ss,
+ Return: Ss,
+ >(
self,
- cb: F,
- ) -> ErasedEffective<'wrap, (Self::Output, T), Self::Effect>
+ pre: Pre,
+ first: First,
+ first_post: FirstPost,
+ repeat: Repeat,
+ repeat_post: RepeatPost,
+ post: Post,
+ ) -> ErasedEffective<'wrap, Return, Self::Effect>
where
- F: for<'b> FnMut(
- &'b mut Self::Output,
- ) -> ErasedEffective<'b, ControlFlow<T>, Self::Effect, &'ctx ()>,
+ Pre: FnOnce(Self::Output) -> (Ctx, ControlFlow<Done, Owned>),
+ First: for<'temp> FnOnce(
+ &'temp mut Ctx,
+ Owned,
+ )
+ -> ErasedEffective<'temp, FirstOutput, Self::Effect, &'wrap ()>,
+ FirstPost: for<'temp> FnOnce(&'temp mut Ctx, FirstOutput) -> ControlFlow<Done, Extra>,
+ Repeat: for<'temp> FnMut(
+ &'temp mut Ctx,
+ &'temp mut Extra,
+ )
+ -> ErasedEffective<'temp, RepeatOutput, Self::Effect, &'wrap ()>,
+ RepeatPost:
+ for<'temp> FnMut(&'temp mut Ctx, &'temp mut Extra, RepeatOutput) -> ControlFlow<Done>,
+ Post: FnOnce(Ctx, Option<Extra>, Done) -> Return,
'lt: 'wrap;
}
@@ -502,3 +715,19 @@ where
<E as TryJoin>::two(self)
}
}
+
+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>,
+{
+ type Output = <E as TryJoin>::Three<'lt, T0, T1, T2>;
+
+ fn join(self) -> Self::Output {
+ <E as TryJoin>::three(self)
+ }
+}