1#[fp_macros::document_module]
6mod inner {
7 use {
8 crate::{
9 Apply,
10 brands::{
11 TryThunkBrand,
12 TryThunkErrAppliedBrand,
13 TryThunkOkAppliedBrand,
14 },
15 classes::{
16 ApplyFirst,
17 ApplySecond,
18 Bifoldable,
19 Bifunctor,
20 CloneFn,
21 Deferrable,
22 Foldable,
23 FoldableWithIndex,
24 Functor,
25 FunctorWithIndex,
26 LazyConfig,
27 Lift,
28 LiftFn,
29 MonadRec,
30 Monoid,
31 Pointed,
32 Semiapplicative,
33 Semigroup,
34 Semimonad,
35 WithIndex,
36 },
37 impl_kind,
38 kinds::*,
39 types::{
40 ArcTryLazy,
41 Lazy,
42 RcTryLazy,
43 Thunk,
44 TryLazy,
45 TrySendThunk,
46 TryTrampoline,
47 },
48 },
49 core::ops::ControlFlow,
50 fp_macros::*,
51 std::{
52 fmt,
53 sync::Arc,
54 },
55 };
56
57 #[document_type_parameters(
63 "The lifetime of the computation.",
64 "The type of the value produced by the computation on success.",
65 "The type of the error produced by the computation on failure."
66 )]
67 pub struct TryThunk<'a, A, E>(
105 Thunk<'a, Result<A, E>>,
107 );
108
109 #[document_type_parameters(
110 "The lifetime of the computation.",
111 "The type of the success value.",
112 "The type of the error value."
113 )]
114 #[document_parameters("The `TryThunk` instance.")]
115 impl<'a, A: 'a, E: 'a> TryThunk<'a, A, E> {
116 #[document_signature]
118 #[document_parameters("The thunk to wrap.")]
120 #[document_returns("A new `TryThunk` instance.")]
122 #[document_examples]
124 #[inline]
132 pub fn new(f: impl FnOnce() -> Result<A, E> + 'a) -> Self {
133 TryThunk(Thunk::new(f))
134 }
135
136 #[document_signature]
138 #[document_parameters("The value to wrap.")]
140 #[document_returns("A new `TryThunk` instance containing the value.")]
142 #[document_examples]
144 #[inline]
152 pub fn pure(a: A) -> Self {
153 TryThunk(Thunk::pure(Ok(a)))
154 }
155
156 #[document_signature]
158 #[document_parameters("The thunk that returns a `TryThunk`.")]
160 #[document_returns("A new `TryThunk` instance.")]
162 #[document_examples]
164 #[inline]
172 pub fn defer(f: impl FnOnce() -> TryThunk<'a, A, E> + 'a) -> Self {
173 TryThunk(Thunk::defer(move || f().0))
174 }
175
176 #[document_signature]
183 #[document_parameters("The value to wrap.")]
185 #[document_returns("A new `TryThunk` instance containing the value.")]
187 #[document_examples]
189 #[inline]
197 pub fn ok(a: A) -> Self {
198 TryThunk(Thunk::pure(Ok(a)))
199 }
200
201 #[document_signature]
203 #[document_parameters("The error to wrap.")]
205 #[document_returns("A new `TryThunk` instance containing the error.")]
207 #[document_examples]
209 #[inline]
217 pub fn err(e: E) -> Self {
218 TryThunk(Thunk::pure(Err(e)))
219 }
220
221 #[document_signature]
223 #[document_type_parameters("The type of the result of the new computation.")]
225 #[document_parameters("The function to apply to the result of the computation.")]
227 #[document_returns("A new `TryThunk` instance representing the chained computation.")]
229 #[document_examples]
231 #[inline]
239 pub fn bind<B: 'a>(
240 self,
241 f: impl FnOnce(A) -> TryThunk<'a, B, E> + 'a,
242 ) -> TryThunk<'a, B, E> {
243 TryThunk(self.0.bind(|result| match result {
244 Ok(a) => f(a).0,
245 Err(e) => Thunk::pure(Err(e)),
246 }))
247 }
248
249 #[document_signature]
251 #[document_type_parameters("The type of the result of the transformation.")]
253 #[document_parameters("The function to apply to the result of the computation.")]
255 #[document_returns("A new `TryThunk` instance with the transformed result.")]
257 #[document_examples]
259 #[inline]
267 pub fn map<B: 'a>(
268 self,
269 func: impl FnOnce(A) -> B + 'a,
270 ) -> TryThunk<'a, B, E> {
271 TryThunk(self.0.map(|result| result.map(func)))
272 }
273
274 #[document_signature]
276 #[document_type_parameters("The type of the new error.")]
278 #[document_parameters("The function to apply to the error.")]
280 #[document_returns("A new `TryThunk` instance with the transformed error.")]
282 #[document_examples]
284 #[inline]
292 pub fn map_err<E2: 'a>(
293 self,
294 f: impl FnOnce(E) -> E2 + 'a,
295 ) -> TryThunk<'a, A, E2> {
296 TryThunk(self.0.map(|result| result.map_err(f)))
297 }
298
299 #[document_signature]
301 #[document_parameters("The function to apply to the error value.")]
303 #[document_returns("A new `TryThunk` that attempts to recover from failure.")]
305 #[document_examples]
307 #[inline]
315 pub fn catch(
316 self,
317 f: impl FnOnce(E) -> TryThunk<'a, A, E> + 'a,
318 ) -> Self {
319 TryThunk(self.0.bind(|result| match result {
320 Ok(a) => Thunk::pure(Ok(a)),
321 Err(e) => f(e).0,
322 }))
323 }
324
325 #[document_signature]
332 #[document_type_parameters("The error type produced by the recovery computation.")]
334 #[document_parameters("The monadic recovery function applied to the error value.")]
336 #[document_returns(
338 "A new `TryThunk` that either passes through the success value or uses the result of the recovery computation."
339 )]
340 #[document_examples]
342 #[inline]
354 pub fn catch_with<E2: 'a>(
355 self,
356 f: impl FnOnce(E) -> TryThunk<'a, A, E2> + 'a,
357 ) -> TryThunk<'a, A, E2> {
358 TryThunk(Thunk::new(move || match self.evaluate() {
359 Ok(a) => Ok(a),
360 Err(e) => f(e).evaluate(),
361 }))
362 }
363
364 #[document_signature]
366 #[document_type_parameters(
368 "The type of the new success value.",
369 "The type of the new error value."
370 )]
371 #[document_parameters(
373 "The function to apply to the success value.",
374 "The function to apply to the error value."
375 )]
376 #[document_returns("A new `TryThunk` with both sides transformed.")]
378 #[document_examples]
380 pub fn bimap<B: 'a, E2: 'a>(
391 self,
392 f: impl FnOnce(A) -> B + 'a,
393 g: impl FnOnce(E) -> E2 + 'a,
394 ) -> TryThunk<'a, B, E2> {
395 TryThunk(self.0.map(|result| match result {
396 Ok(a) => Ok(f(a)),
397 Err(e) => Err(g(e)),
398 }))
399 }
400
401 #[document_signature]
403 #[document_returns("The result of the computation.")]
405 #[document_examples]
407 #[inline]
415 pub fn evaluate(self) -> Result<A, E> {
416 self.0.evaluate()
417 }
418
419 #[document_signature]
423 #[document_type_parameters(
425 "The type of the second computation's success value.",
426 "The type of the combined result."
427 )]
428 #[document_parameters("The second computation.", "The function to combine the results.")]
430 #[document_returns("A new `TryThunk` producing the combined result.")]
432 #[document_examples]
434 #[inline]
449 pub fn lift2<B: 'a, C: 'a>(
450 self,
451 other: TryThunk<'a, B, E>,
452 f: impl FnOnce(A, B) -> C + 'a,
453 ) -> TryThunk<'a, C, E> {
454 self.bind(move |a| other.map(move |b| f(a, b)))
455 }
456
457 #[document_signature]
461 #[document_type_parameters("The type of the second computation's success value.")]
463 #[document_parameters("The second computation.")]
465 #[document_returns(
467 "A new `TryThunk` that runs both computations and returns the result of the second."
468 )]
469 #[document_examples]
471 #[inline]
486 pub fn then<B: 'a>(
487 self,
488 other: TryThunk<'a, B, E>,
489 ) -> TryThunk<'a, B, E> {
490 self.bind(move |_| other)
491 }
492
493 #[document_signature]
512 #[document_type_parameters("The type of the loop state.")]
514 #[document_parameters(
516 "The step function that produces the next state, the final result, or an error.",
517 "The initial state."
518 )]
519 #[document_returns("A `TryThunk` that, when evaluated, runs the tail-recursive loop.")]
521 #[document_examples]
523 pub fn tail_rec_m<S>(
541 f: impl Fn(S) -> TryThunk<'a, ControlFlow<A, S>, E> + 'a,
542 initial: S,
543 ) -> Self
544 where
545 S: 'a, {
546 TryThunk::new(move || {
547 let mut state = initial;
548 loop {
549 match f(state).evaluate() {
550 Ok(ControlFlow::Continue(next)) => state = next,
551 Ok(ControlFlow::Break(a)) => break Ok(a),
552 Err(e) => break Err(e),
553 }
554 }
555 })
556 }
557
558 #[document_signature]
566 #[document_type_parameters("The type of the loop state.")]
568 #[document_parameters(
570 "The step function that produces the next state, the final result, or an error.",
571 "The initial state."
572 )]
573 #[document_returns("A `TryThunk` that, when evaluated, runs the tail-recursive loop.")]
575 #[document_examples]
577 pub fn arc_tail_rec_m<S>(
604 f: impl Fn(S) -> TryThunk<'a, ControlFlow<A, S>, E> + Send + Sync + 'a,
605 initial: S,
606 ) -> Self
607 where
608 S: 'a, {
609 let f = Arc::new(f);
610 let wrapper = move |s: S| {
611 let f = Arc::clone(&f);
612 f(s)
613 };
614 Self::tail_rec_m(wrapper, initial)
615 }
616
617 #[document_signature]
622 #[document_returns("A memoized `RcTryLazy` wrapping this computation.")]
624 #[document_examples]
626 #[inline]
635 pub fn into_rc_try_lazy(self) -> RcTryLazy<'a, A, E> {
636 RcTryLazy::from(self)
637 }
638
639 #[document_signature]
644 #[document_returns("A thread-safe memoized `ArcTryLazy` wrapping this computation.")]
646 #[document_examples]
648 #[inline]
657 pub fn into_arc_try_lazy(self) -> ArcTryLazy<'a, A, E>
658 where
659 A: Send + Sync + 'a,
660 E: Send + Sync + 'a, {
661 let result = self.evaluate();
662 ArcTryLazy::new(move || result)
663 }
664 }
665
666 #[document_type_parameters(
667 "The lifetime of the computation.",
668 "The type of the computed value.",
669 "The type of the error value."
670 )]
671 #[document_parameters("The `TryThunk` to operate on.")]
672 impl<'a, A: 'a, E: 'a> TryThunk<'a, A, E> {
673 #[document_signature]
680 #[document_parameters(
682 "The closure that might panic.",
683 "The function that converts a panic payload into the error type."
684 )]
685 #[document_returns(
687 "A new `TryThunk` instance where panics are converted to `Err(E)` via the handler."
688 )]
689 #[document_examples]
691 pub fn catch_unwind_with(
707 f: impl FnOnce() -> A + std::panic::UnwindSafe + 'a,
708 handler: impl FnOnce(Box<dyn std::any::Any + Send>) -> E + 'a,
709 ) -> Self {
710 TryThunk::new(move || std::panic::catch_unwind(f).map_err(handler))
711 }
712
713 #[document_signature]
715 #[document_returns("The underlying `Thunk` that produces a `Result`.")]
717 #[document_examples]
719 pub fn into_inner(self) -> Thunk<'a, Result<A, E>> {
728 self.0
729 }
730 }
731
732 #[document_type_parameters(
733 "The lifetime of the computation.",
734 "The type of the computed value."
735 )]
736 impl<'a, A: 'a> TryThunk<'a, A, String> {
737 #[document_signature]
746 #[document_parameters("The closure that might panic.")]
748 #[document_returns(
750 "A new `TryThunk` instance where panics are converted to `Err(String)`."
751 )]
752 #[document_examples]
754 pub fn catch_unwind(f: impl FnOnce() -> A + std::panic::UnwindSafe + 'a) -> Self {
767 Self::catch_unwind_with(f, crate::utils::panic_payload_to_string)
768 }
769 }
770
771 #[document_type_parameters(
772 "The lifetime of the computation.",
773 "The type of the success value.",
774 "The type of the error value.",
775 "The memoization configuration."
776 )]
777 impl<'a, A, E, Config> From<Lazy<'a, A, Config>> for TryThunk<'a, A, E>
778 where
779 A: Clone + 'a,
780 E: 'a,
781 Config: LazyConfig,
782 {
783 #[document_signature]
784 #[document_parameters("The lazy value to convert.")]
785 #[document_returns("A new `TryThunk` instance that wraps the lazy value.")]
786 #[document_examples]
787 fn from(memo: Lazy<'a, A, Config>) -> Self {
795 TryThunk::new(move || Ok(memo.evaluate().clone()))
796 }
797 }
798
799 #[document_type_parameters(
800 "The lifetime of the computation.",
801 "The type of the success value.",
802 "The type of the error value.",
803 "The memoization configuration."
804 )]
805 impl<'a, A, E, Config> From<TryLazy<'a, A, E, Config>> for TryThunk<'a, A, E>
806 where
807 A: Clone + 'a,
808 E: Clone + 'a,
809 Config: LazyConfig,
810 {
811 #[document_signature]
816 #[document_parameters("The fallible lazy value to convert.")]
817 #[document_returns("A new `TryThunk` instance that wraps the fallible lazy value.")]
818 #[document_examples]
819 fn from(memo: TryLazy<'a, A, E, Config>) -> Self {
827 TryThunk::new(move || memo.evaluate().cloned().map_err(Clone::clone))
828 }
829 }
830
831 #[document_type_parameters(
832 "The lifetime of the computation.",
833 "The type of the success value.",
834 "The type of the error value."
835 )]
836 impl<'a, A: 'a, E: 'a> From<Thunk<'a, A>> for TryThunk<'a, A, E> {
837 #[document_signature]
838 #[document_parameters("The thunk to convert.")]
839 #[document_returns("A new `TryThunk` instance that wraps the thunk.")]
840 #[document_examples]
841 fn from(eval: Thunk<'a, A>) -> Self {
849 TryThunk(eval.map(Ok))
850 }
851 }
852
853 #[document_type_parameters(
854 "The lifetime of the computation.",
855 "The type of the success value.",
856 "The type of the error value."
857 )]
858 impl<'a, A: 'a, E: 'a> From<Result<A, E>> for TryThunk<'a, A, E> {
859 #[document_signature]
860 #[document_parameters("The result to convert.")]
861 #[document_returns("A new `TryThunk` instance that produces the result.")]
862 #[document_examples]
863 fn from(result: Result<A, E>) -> Self {
873 TryThunk(Thunk::pure(result))
874 }
875 }
876
877 #[document_type_parameters("The type of the success value.", "The type of the error value.")]
878 impl<A: 'static, E: 'static> From<TryTrampoline<A, E>> for TryThunk<'static, A, E> {
879 #[document_signature]
883 #[document_parameters("The fallible trampoline to convert.")]
884 #[document_returns("A new `TryThunk` instance that evaluates the trampoline.")]
885 #[document_examples]
886 fn from(tramp: TryTrampoline<A, E>) -> Self {
895 TryThunk::new(move || tramp.evaluate())
896 }
897 }
898
899 #[document_type_parameters(
900 "The lifetime of the computation.",
901 "The type of the success value.",
902 "The type of the error value."
903 )]
904 impl<'a, A: 'a, E: 'a> From<TrySendThunk<'a, A, E>> for TryThunk<'a, A, E> {
905 #[document_signature]
912 #[document_parameters("The send try-thunk to convert.")]
913 #[document_returns("A `TryThunk` wrapping the same deferred computation.")]
914 #[document_examples]
915 fn from(send_thunk: TrySendThunk<'a, A, E>) -> Self {
923 TryThunk(Thunk::from(send_thunk.into_inner()))
924 }
925 }
926
927 #[document_type_parameters(
928 "The lifetime of the computation.",
929 "The type of the success value.",
930 "The type of the error value."
931 )]
932 impl<'a, A, E> Deferrable<'a> for TryThunk<'a, A, E>
933 where
934 A: 'a,
935 E: 'a,
936 {
937 #[document_signature]
939 #[document_parameters("A thunk that produces the try thunk.")]
941 #[document_returns("The deferred try thunk.")]
943 #[document_examples]
945 fn defer(f: impl FnOnce() -> Self + 'a) -> Self
958 where
959 Self: Sized, {
960 TryThunk::defer(f)
961 }
962 }
963
964 impl_kind! {
965 #[no_inferable_brand]
966 impl<E: 'static> for TryThunkErrAppliedBrand<E> {
967 #[document_default]
968 type Of<'a, A: 'a>: 'a = TryThunk<'a, A, E>;
969 }
970 }
971
972 #[document_type_parameters("The error type.")]
973 impl<E: 'static> Functor for TryThunkErrAppliedBrand<E> {
974 #[document_signature]
976 #[document_type_parameters(
978 "The lifetime of the computation.",
979 "The type of the value inside the `TryThunk`.",
980 "The type of the result of the transformation."
981 )]
982 #[document_parameters(
984 "The function to apply to the result of the computation.",
985 "The `TryThunk` instance."
986 )]
987 #[document_returns("A new `TryThunk` instance with the transformed result.")]
989 #[document_examples]
990 fn map<'a, A: 'a, B: 'a>(
1003 func: impl Fn(A) -> B + 'a,
1004 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1005 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
1006 fa.map(func)
1007 }
1008 }
1009
1010 #[document_type_parameters("The error type.")]
1011 impl<E: 'static> Pointed for TryThunkErrAppliedBrand<E> {
1012 #[document_signature]
1014 #[document_type_parameters(
1016 "The lifetime of the computation.",
1017 "The type of the value to wrap."
1018 )]
1019 #[document_parameters("The value to wrap.")]
1021 #[document_returns("A new `TryThunk` instance containing the value.")]
1023 #[document_examples]
1025 fn pure<'a, A: 'a>(a: A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>) {
1037 TryThunk::ok(a)
1038 }
1039 }
1040
1041 #[document_type_parameters("The error type.")]
1042 impl<E: 'static> Lift for TryThunkErrAppliedBrand<E> {
1043 #[document_signature]
1045 #[document_type_parameters(
1047 "The lifetime of the computation.",
1048 "The type of the first value.",
1049 "The type of the second value.",
1050 "The type of the result."
1051 )]
1052 #[document_parameters(
1054 "The binary function to apply.",
1055 "The first `TryThunk`.",
1056 "The second `TryThunk`."
1057 )]
1058 #[document_returns(
1060 "A new `TryThunk` instance containing the result of applying the function."
1061 )]
1062 #[document_examples]
1063 fn lift2<'a, A, B, C>(
1081 func: impl Fn(A, B) -> C + 'a,
1082 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1083 fb: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
1084 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>)
1085 where
1086 A: Clone + 'a,
1087 B: Clone + 'a,
1088 C: 'a, {
1089 fa.bind(move |a| fb.map(move |b| func(a, b)))
1090 }
1091 }
1092
1093 #[document_type_parameters("The error type.")]
1094 impl<E: 'static> ApplyFirst for TryThunkErrAppliedBrand<E> {}
1095
1096 #[document_type_parameters("The error type.")]
1097 impl<E: 'static> ApplySecond for TryThunkErrAppliedBrand<E> {}
1098
1099 #[document_type_parameters("The error type.")]
1100 impl<E: 'static> Semiapplicative for TryThunkErrAppliedBrand<E> {
1101 #[document_signature]
1103 #[document_type_parameters(
1105 "The lifetime of the computation.",
1106 "The brand of the cloneable function wrapper.",
1107 "The type of the input.",
1108 "The type of the result."
1109 )]
1110 #[document_parameters(
1112 "The `TryThunk` containing the function.",
1113 "The `TryThunk` containing the value."
1114 )]
1115 #[document_returns(
1117 "A new `TryThunk` instance containing the result of applying the function."
1118 )]
1119 #[document_examples]
1120 fn apply<'a, FnBrand: 'a + CloneFn, A: 'a + Clone, B: 'a>(
1135 ff: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as CloneFn>::Of<'a, A, B>>),
1136 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1137 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
1138 ff.bind(move |f| {
1139 fa.map(
1140 #[expect(clippy::redundant_closure, reason = "Required for move semantics")]
1141 move |a| f(a),
1142 )
1143 })
1144 }
1145 }
1146
1147 #[document_type_parameters("The error type.")]
1148 impl<E: 'static> Semimonad for TryThunkErrAppliedBrand<E> {
1149 #[document_signature]
1151 #[document_type_parameters(
1153 "The lifetime of the computation.",
1154 "The type of the result of the first computation.",
1155 "The type of the result of the new computation."
1156 )]
1157 #[document_parameters(
1159 "The first `TryThunk`.",
1160 "The function to apply to the result of the computation."
1161 )]
1162 #[document_returns("A new `TryThunk` instance representing the chained computation.")]
1164 #[document_examples]
1165 fn bind<'a, A: 'a, B: 'a>(
1180 ma: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1181 func: impl Fn(A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) + 'a,
1182 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
1183 ma.bind(func)
1184 }
1185 }
1186
1187 #[document_type_parameters("The error type.")]
1188 impl<E: 'static> MonadRec for TryThunkErrAppliedBrand<E> {
1189 #[document_signature]
1191 #[document_type_parameters(
1193 "The lifetime of the computation.",
1194 "The type of the initial value and loop state.",
1195 "The type of the result."
1196 )]
1197 #[document_parameters("The step function.", "The initial value.")]
1199 #[document_returns("The result of the computation.")]
1201 #[document_examples]
1203 fn tail_rec_m<'a, A: 'a, B: 'a>(
1226 f: impl Fn(
1227 A,
1228 )
1229 -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, ControlFlow<B, A>>)
1230 + 'a,
1231 a: A,
1232 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
1233 TryThunk::new(move || {
1234 let mut current = a;
1235 loop {
1236 match f(current).evaluate() {
1237 Ok(ControlFlow::Continue(next)) => current = next,
1238 Ok(ControlFlow::Break(res)) => break Ok(res),
1239 Err(e) => break Err(e),
1240 }
1241 }
1242 })
1243 }
1244 }
1245
1246 #[document_type_parameters("The error type.")]
1247 impl<E: 'static> Foldable for TryThunkErrAppliedBrand<E> {
1248 #[document_signature]
1250 #[document_type_parameters(
1252 "The lifetime of the computation.",
1253 "The brand of the cloneable function to use.",
1254 "The type of the elements in the structure.",
1255 "The type of the accumulator."
1256 )]
1257 #[document_parameters(
1259 "The function to apply to each element and the accumulator.",
1260 "The initial value of the accumulator.",
1261 "The `TryThunk` to fold."
1262 )]
1263 #[document_returns("The final accumulator value.")]
1265 #[document_examples]
1266 fn fold_right<'a, FnBrand, A: 'a + Clone, B: 'a>(
1283 func: impl Fn(A, B) -> B + 'a,
1284 initial: B,
1285 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1286 ) -> B
1287 where
1288 FnBrand: CloneFn + 'a, {
1289 match fa.evaluate() {
1290 Ok(a) => func(a, initial),
1291 Err(_) => initial,
1292 }
1293 }
1294
1295 #[document_signature]
1297 #[document_type_parameters(
1299 "The lifetime of the computation.",
1300 "The brand of the cloneable function to use.",
1301 "The type of the elements in the structure.",
1302 "The type of the accumulator."
1303 )]
1304 #[document_parameters(
1306 "The function to apply to the accumulator and each element.",
1307 "The initial value of the accumulator.",
1308 "The `TryThunk` to fold."
1309 )]
1310 #[document_returns("The final accumulator value.")]
1312 #[document_examples]
1313 fn fold_left<'a, FnBrand, A: 'a + Clone, B: 'a>(
1330 func: impl Fn(B, A) -> B + 'a,
1331 initial: B,
1332 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1333 ) -> B
1334 where
1335 FnBrand: CloneFn + 'a, {
1336 match fa.evaluate() {
1337 Ok(a) => func(initial, a),
1338 Err(_) => initial,
1339 }
1340 }
1341
1342 #[document_signature]
1344 #[document_type_parameters(
1346 "The lifetime of the computation.",
1347 "The brand of the cloneable function to use.",
1348 "The type of the elements in the structure.",
1349 "The type of the monoid."
1350 )]
1351 #[document_parameters("The mapping function.", "The TryThunk to fold.")]
1353 #[document_returns("The monoid value.")]
1355 #[document_examples]
1357 fn fold_map<'a, FnBrand, A: 'a + Clone, M>(
1373 func: impl Fn(A) -> M + 'a,
1374 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1375 ) -> M
1376 where
1377 M: Monoid + 'a,
1378 FnBrand: CloneFn + 'a, {
1379 match fa.evaluate() {
1380 Ok(a) => func(a),
1381 Err(_) => M::empty(),
1382 }
1383 }
1384 }
1385
1386 #[document_type_parameters(
1387 "The lifetime of the computation.",
1388 "The success value type.",
1389 "The error value type."
1390 )]
1391 impl<'a, A: Semigroup + 'a, E: 'a> Semigroup for TryThunk<'a, A, E> {
1392 #[document_signature]
1394 #[document_parameters("The first `TryThunk`.", "The second `TryThunk`.")]
1396 #[document_returns("A new `TryThunk` containing the combined result.")]
1398 #[document_examples]
1400 fn append(
1415 a: Self,
1416 b: Self,
1417 ) -> Self {
1418 TryThunk::new(move || {
1419 let a_val = a.evaluate()?;
1420 let b_val = b.evaluate()?;
1421 Ok(Semigroup::append(a_val, b_val))
1422 })
1423 }
1424 }
1425
1426 #[document_type_parameters(
1427 "The lifetime of the computation.",
1428 "The success value type.",
1429 "The error value type."
1430 )]
1431 impl<'a, A: Monoid + 'a, E: 'a> Monoid for TryThunk<'a, A, E> {
1432 #[document_signature]
1434 #[document_returns("A `TryThunk` producing the identity value of `A`.")]
1436 #[document_examples]
1438 fn empty() -> Self {
1449 TryThunk(Thunk::pure(Ok(Monoid::empty())))
1450 }
1451 }
1452
1453 impl_kind! {
1454 for TryThunkBrand {
1461 type Of<'a, E: 'a, A: 'a>: 'a = TryThunk<'a, A, E>;
1462 }
1463 }
1464
1465 impl Bifunctor for TryThunkBrand {
1466 #[document_signature]
1470 #[document_type_parameters(
1472 "The lifetime of the values.",
1473 "The type of the error value.",
1474 "The type of the mapped error value.",
1475 "The type of the success value.",
1476 "The type of the mapped success value."
1477 )]
1478 #[document_parameters(
1480 "The function to apply to the error.",
1481 "The function to apply to the success.",
1482 "The `TryThunk` to map over."
1483 )]
1484 #[document_returns("A new `TryThunk` containing the mapped values.")]
1486 #[document_examples]
1487 fn bimap<'a, A: 'a, B: 'a, C: 'a, D: 'a>(
1508 f: impl Fn(A) -> B + 'a,
1509 g: impl Fn(C) -> D + 'a,
1510 p: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, C>),
1511 ) -> Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, B, D>) {
1512 TryThunk(p.0.map(move |result| match result {
1513 Ok(c) => Ok(g(c)),
1514 Err(a) => Err(f(a)),
1515 }))
1516 }
1517 }
1518
1519 impl Bifoldable for TryThunkBrand {
1520 #[document_signature]
1525 #[document_type_parameters(
1527 "The lifetime of the values.",
1528 "The brand of the cloneable function to use.",
1529 "The error type (first position).",
1530 "The success type (second position).",
1531 "The accumulator type."
1532 )]
1533 #[document_parameters(
1535 "The step function applied to the error value.",
1536 "The step function applied to the success value.",
1537 "The initial accumulator.",
1538 "The `TryThunk` to fold."
1539 )]
1540 #[document_returns("`f(e, z)` for `Err(e)`, or `g(a, z)` for `Ok(a)`.")]
1542 #[document_examples]
1543 fn bi_fold_right<'a, FnBrand: CloneFn + 'a, A: 'a + Clone, B: 'a + Clone, C: 'a>(
1569 f: impl Fn(A, C) -> C + 'a,
1570 g: impl Fn(B, C) -> C + 'a,
1571 z: C,
1572 p: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
1573 ) -> C {
1574 match p.evaluate() {
1575 Err(a) => f(a, z),
1576 Ok(b) => g(b, z),
1577 }
1578 }
1579
1580 #[document_signature]
1585 #[document_type_parameters(
1587 "The lifetime of the values.",
1588 "The brand of the cloneable function to use.",
1589 "The error type (first position).",
1590 "The success type (second position).",
1591 "The accumulator type."
1592 )]
1593 #[document_parameters(
1595 "The step function applied to the error value.",
1596 "The step function applied to the success value.",
1597 "The initial accumulator.",
1598 "The `TryThunk` to fold."
1599 )]
1600 #[document_returns("`f(z, e)` for `Err(e)`, or `g(z, a)` for `Ok(a)`.")]
1602 #[document_examples]
1603 fn bi_fold_left<'a, FnBrand: CloneFn + 'a, A: 'a + Clone, B: 'a + Clone, C: 'a>(
1629 f: impl Fn(C, A) -> C + 'a,
1630 g: impl Fn(C, B) -> C + 'a,
1631 z: C,
1632 p: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
1633 ) -> C {
1634 match p.evaluate() {
1635 Err(a) => f(z, a),
1636 Ok(b) => g(z, b),
1637 }
1638 }
1639
1640 #[document_signature]
1645 #[document_type_parameters(
1647 "The lifetime of the values.",
1648 "The brand of the cloneable function to use.",
1649 "The error type (first position).",
1650 "The success type (second position).",
1651 "The monoid type."
1652 )]
1653 #[document_parameters(
1655 "The function mapping the error to the monoid.",
1656 "The function mapping the success to the monoid.",
1657 "The `TryThunk` to fold."
1658 )]
1659 #[document_returns("`f(e)` for `Err(e)`, or `g(a)` for `Ok(a)`.")]
1661 #[document_examples]
1662 fn bi_fold_map<'a, FnBrand: CloneFn + 'a, A: 'a + Clone, B: 'a + Clone, M>(
1686 f: impl Fn(A) -> M + 'a,
1687 g: impl Fn(B) -> M + 'a,
1688 p: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
1689 ) -> M
1690 where
1691 M: Monoid + 'a, {
1692 match p.evaluate() {
1693 Err(a) => f(a),
1694 Ok(b) => g(b),
1695 }
1696 }
1697 }
1698
1699 impl_kind! {
1700 #[no_inferable_brand]
1701 impl<A: 'static> for TryThunkOkAppliedBrand<A> {
1713 #[document_default]
1714 type Of<'a, E: 'a>: 'a = TryThunk<'a, A, E>;
1715 }
1716 }
1717
1718 #[document_type_parameters("The success type.")]
1719 impl<A: 'static> Functor for TryThunkOkAppliedBrand<A> {
1720 #[document_signature]
1722 #[document_type_parameters(
1724 "The lifetime of the computation.",
1725 "The type of the error value inside the `TryThunk`.",
1726 "The type of the result of the transformation."
1727 )]
1728 #[document_parameters("The function to apply to the error.", "The `TryThunk` instance.")]
1730 #[document_returns("A new `TryThunk` instance with the transformed error.")]
1732 #[document_examples]
1734 fn map<'a, E: 'a, E2: 'a>(
1747 func: impl Fn(E) -> E2 + 'a,
1748 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
1749 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
1750 fa.map_err(func)
1751 }
1752 }
1753
1754 #[document_type_parameters("The success type.")]
1755 impl<A: 'static> Pointed for TryThunkOkAppliedBrand<A> {
1756 #[document_signature]
1758 #[document_type_parameters(
1760 "The lifetime of the computation.",
1761 "The type of the value to wrap."
1762 )]
1763 #[document_parameters("The value to wrap.")]
1765 #[document_returns("A new `TryThunk` instance containing the value as an error.")]
1767 #[document_examples]
1769 fn pure<'a, E: 'a>(e: E) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>) {
1781 TryThunk::err(e)
1782 }
1783 }
1784
1785 #[document_type_parameters("The success type.")]
1786 impl<A: 'static> Lift for TryThunkOkAppliedBrand<A> {
1787 #[document_signature]
1797 #[document_type_parameters(
1799 "The lifetime of the computation.",
1800 "The type of the first error value.",
1801 "The type of the second error value.",
1802 "The type of the result error value."
1803 )]
1804 #[document_parameters(
1806 "The binary function to apply to the errors.",
1807 "The first `TryThunk`.",
1808 "The second `TryThunk`."
1809 )]
1810 #[document_returns(
1812 "A new `TryThunk` instance containing the result of applying the function to the errors."
1813 )]
1814 #[document_examples]
1815 fn lift2<'a, E1, E2, E3>(
1833 func: impl Fn(E1, E2) -> E3 + 'a,
1834 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E1>),
1835 fb: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>),
1836 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E3>)
1837 where
1838 E1: Clone + 'a,
1839 E2: Clone + 'a,
1840 E3: 'a, {
1841 TryThunk::new(move || match fa.evaluate() {
1842 Ok(a) => Ok(a),
1843 Err(e1) => match fb.evaluate() {
1844 Ok(a) => Ok(a),
1845 Err(e2) => Err(func(e1, e2)),
1846 },
1847 })
1848 }
1849 }
1850
1851 #[document_type_parameters("The success type.")]
1852 impl<A: 'static> ApplyFirst for TryThunkOkAppliedBrand<A> {}
1853
1854 #[document_type_parameters("The success type.")]
1855 impl<A: 'static> ApplySecond for TryThunkOkAppliedBrand<A> {}
1856
1857 #[document_type_parameters("The success type.")]
1858 impl<A: 'static> Semiapplicative for TryThunkOkAppliedBrand<A> {
1859 #[document_signature]
1869 #[document_type_parameters(
1871 "The lifetime of the computation.",
1872 "The brand of the cloneable function wrapper.",
1873 "The type of the input error.",
1874 "The type of the result error."
1875 )]
1876 #[document_parameters(
1878 "The `TryThunk` containing the function (in Err).",
1879 "The `TryThunk` containing the value (in Err)."
1880 )]
1881 #[document_returns(
1883 "A new `TryThunk` instance containing the result of applying the function."
1884 )]
1885 #[document_examples]
1886 fn apply<'a, FnBrand: 'a + CloneFn, E1: 'a + Clone, E2: 'a>(
1901 ff: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as CloneFn>::Of<'a, E1, E2>>),
1902 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E1>),
1903 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
1904 TryThunk::new(move || match ff.evaluate() {
1905 Ok(a) => Ok(a),
1906 Err(f) => match fa.evaluate() {
1907 Ok(a) => Ok(a),
1908 Err(e) => Err(f(e)),
1909 },
1910 })
1911 }
1912 }
1913
1914 #[document_type_parameters("The success type.")]
1915 impl<A: 'static> Semimonad for TryThunkOkAppliedBrand<A> {
1916 #[document_signature]
1918 #[document_type_parameters(
1920 "The lifetime of the computation.",
1921 "The type of the result of the first computation (error).",
1922 "The type of the result of the new computation (error)."
1923 )]
1924 #[document_parameters(
1926 "The first `TryThunk`.",
1927 "The function to apply to the error result of the computation."
1928 )]
1929 #[document_returns("A new `TryThunk` instance representing the chained computation.")]
1931 #[document_examples]
1932 fn bind<'a, E1: 'a, E2: 'a>(
1947 ma: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E1>),
1948 func: impl Fn(E1) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) + 'a,
1949 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
1950 TryThunk::new(move || match ma.evaluate() {
1951 Ok(a) => Ok(a),
1952 Err(e) => func(e).evaluate(),
1953 })
1954 }
1955 }
1956
1957 #[document_type_parameters("The success type.")]
1958 impl<A: 'static> MonadRec for TryThunkOkAppliedBrand<A> {
1959 #[document_signature]
1965 #[document_type_parameters(
1967 "The lifetime of the computation.",
1968 "The type of the initial value and loop state.",
1969 "The type of the result."
1970 )]
1971 #[document_parameters("The step function.", "The initial value.")]
1973 #[document_returns("The result of the computation.")]
1975 #[document_examples]
1977 fn tail_rec_m<'a, E: 'a, E2: 'a>(
2000 f: impl Fn(
2001 E,
2002 )
2003 -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, ControlFlow<E2, E>>)
2004 + 'a,
2005 e: E,
2006 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
2007 TryThunk::new(move || {
2008 let mut current = e;
2009 loop {
2010 match f(current).evaluate() {
2011 Err(ControlFlow::Continue(next)) => current = next,
2012 Err(ControlFlow::Break(res)) => break Err(res),
2013 Ok(a) => break Ok(a),
2014 }
2015 }
2016 })
2017 }
2018 }
2019
2020 #[document_type_parameters("The success type.")]
2021 impl<A: 'static> Foldable for TryThunkOkAppliedBrand<A> {
2022 #[document_signature]
2024 #[document_type_parameters(
2026 "The lifetime of the computation.",
2027 "The brand of the cloneable function to use.",
2028 "The type of the elements in the structure.",
2029 "The type of the accumulator."
2030 )]
2031 #[document_parameters(
2033 "The function to apply to each element and the accumulator.",
2034 "The initial value of the accumulator.",
2035 "The `TryThunk` to fold."
2036 )]
2037 #[document_returns("The final accumulator value.")]
2039 #[document_examples]
2040 fn fold_right<'a, FnBrand, E: 'a + Clone, B: 'a>(
2057 func: impl Fn(E, B) -> B + 'a,
2058 initial: B,
2059 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
2060 ) -> B
2061 where
2062 FnBrand: CloneFn + 'a, {
2063 match fa.evaluate() {
2064 Err(e) => func(e, initial),
2065 Ok(_) => initial,
2066 }
2067 }
2068
2069 #[document_signature]
2071 #[document_type_parameters(
2073 "The lifetime of the computation.",
2074 "The brand of the cloneable function to use.",
2075 "The type of the elements in the structure.",
2076 "The type of the accumulator."
2077 )]
2078 #[document_parameters(
2080 "The function to apply to the accumulator and each element.",
2081 "The initial value of the accumulator.",
2082 "The `TryThunk` to fold."
2083 )]
2084 #[document_returns("The final accumulator value.")]
2086 #[document_examples]
2087 fn fold_left<'a, FnBrand, E: 'a + Clone, B: 'a>(
2104 func: impl Fn(B, E) -> B + 'a,
2105 initial: B,
2106 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
2107 ) -> B
2108 where
2109 FnBrand: CloneFn + 'a, {
2110 match fa.evaluate() {
2111 Err(e) => func(initial, e),
2112 Ok(_) => initial,
2113 }
2114 }
2115
2116 #[document_signature]
2118 #[document_type_parameters(
2120 "The lifetime of the computation.",
2121 "The brand of the cloneable function to use.",
2122 "The type of the elements in the structure.",
2123 "The type of the monoid."
2124 )]
2125 #[document_parameters("The mapping function.", "The TryThunk to fold.")]
2127 #[document_returns("The monoid value.")]
2129 #[document_examples]
2131 fn fold_map<'a, FnBrand, E: 'a + Clone, M>(
2147 func: impl Fn(E) -> M + 'a,
2148 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
2149 ) -> M
2150 where
2151 M: Monoid + 'a,
2152 FnBrand: CloneFn + 'a, {
2153 match fa.evaluate() {
2154 Err(e) => func(e),
2155 Ok(_) => M::empty(),
2156 }
2157 }
2158 }
2159
2160 #[document_type_parameters(
2161 "The lifetime of the computation.",
2162 "The type of the success value.",
2163 "The type of the error value."
2164 )]
2165 #[document_parameters("The try-thunk to format.")]
2166 impl<'a, A, E> fmt::Debug for TryThunk<'a, A, E> {
2167 #[document_signature]
2169 #[document_parameters("The formatter.")]
2170 #[document_returns("The formatting result.")]
2171 #[document_examples]
2172 fn fmt(
2179 &self,
2180 f: &mut fmt::Formatter<'_>,
2181 ) -> fmt::Result {
2182 f.write_str("TryThunk(<unevaluated>)")
2183 }
2184 }
2185
2186 #[document_type_parameters("The error type.")]
2187 impl<E: 'static> WithIndex for TryThunkErrAppliedBrand<E> {
2188 type Index = ();
2189 }
2190
2191 #[document_type_parameters("The error type.")]
2192 impl<E: 'static> FunctorWithIndex for TryThunkErrAppliedBrand<E> {
2193 #[document_signature]
2195 #[document_type_parameters(
2196 "The lifetime of the computation.",
2197 "The type of the success value inside the `TryThunk`.",
2198 "The type of the result of applying the function."
2199 )]
2200 #[document_parameters(
2201 "The function to apply to the value and its index.",
2202 "The `TryThunk` to map over."
2203 )]
2204 #[document_returns(
2205 "A new `TryThunk` containing the result of applying the function, or the original error."
2206 )]
2207 #[document_examples]
2208 fn map_with_index<'a, A: 'a, B: 'a>(
2221 f: impl Fn((), A) -> B + 'a,
2222 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
2223 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
2224 fa.map(move |a| f((), a))
2225 }
2226 }
2227
2228 #[document_type_parameters("The error type.")]
2229 impl<E: 'static> FoldableWithIndex for TryThunkErrAppliedBrand<E> {
2230 #[document_signature]
2232 #[document_type_parameters(
2233 "The lifetime of the computation.",
2234 "The brand of the cloneable function to use.",
2235 "The type of the success value inside the `TryThunk`.",
2236 "The monoid type."
2237 )]
2238 #[document_parameters(
2239 "The function to apply to the value and its index.",
2240 "The `TryThunk` to fold."
2241 )]
2242 #[document_returns("The monoid value.")]
2243 #[document_examples]
2244 fn fold_map_with_index<'a, FnBrand, A: 'a + Clone, R: Monoid>(
2261 f: impl Fn((), A) -> R + 'a,
2262 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
2263 ) -> R
2264 where
2265 FnBrand: LiftFn + 'a, {
2266 match fa.evaluate() {
2267 Ok(a) => f((), a),
2268 Err(_) => R::empty(),
2269 }
2270 }
2271 }
2272
2273 #[document_type_parameters("The success type.")]
2274 impl<A: 'static> WithIndex for TryThunkOkAppliedBrand<A> {
2275 type Index = ();
2276 }
2277
2278 #[document_type_parameters("The success type.")]
2279 impl<A: 'static> FunctorWithIndex for TryThunkOkAppliedBrand<A> {
2280 #[document_signature]
2282 #[document_type_parameters(
2283 "The lifetime of the computation.",
2284 "The type of the error value inside the `TryThunk`.",
2285 "The type of the result of applying the function."
2286 )]
2287 #[document_parameters(
2288 "The function to apply to the error and its index.",
2289 "The `TryThunk` to map over."
2290 )]
2291 #[document_returns(
2292 "A new `TryThunk` containing the original success or the transformed error."
2293 )]
2294 #[document_examples]
2295 fn map_with_index<'a, E: 'a, E2: 'a>(
2308 f: impl Fn((), E) -> E2 + 'a,
2309 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
2310 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
2311 fa.map_err(move |e| f((), e))
2312 }
2313 }
2314
2315 #[document_type_parameters("The success type.")]
2316 impl<A: 'static> FoldableWithIndex for TryThunkOkAppliedBrand<A> {
2317 #[document_signature]
2319 #[document_type_parameters(
2320 "The lifetime of the computation.",
2321 "The brand of the cloneable function to use.",
2322 "The type of the error value inside the `TryThunk`.",
2323 "The monoid type."
2324 )]
2325 #[document_parameters(
2326 "The function to apply to the error and its index.",
2327 "The `TryThunk` to fold."
2328 )]
2329 #[document_returns("The monoid value.")]
2330 #[document_examples]
2331 fn fold_map_with_index<'a, FnBrand, E: 'a + Clone, R: Monoid>(
2348 f: impl Fn((), E) -> R + 'a,
2349 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
2350 ) -> R
2351 where
2352 FnBrand: LiftFn + 'a, {
2353 match fa.evaluate() {
2354 Err(e) => f((), e),
2355 Ok(_) => R::empty(),
2356 }
2357 }
2358 }
2359}
2360pub use inner::*;
2361
2362#[cfg(test)]
2363#[expect(
2364 clippy::unwrap_used,
2365 clippy::panic,
2366 reason = "Tests use panicking operations for brevity and clarity"
2367)]
2368mod tests {
2369 use {
2370 super::*,
2371 crate::types::Thunk,
2372 quickcheck_macros::quickcheck,
2373 };
2374
2375 #[test]
2379 fn test_success() {
2380 let try_thunk: TryThunk<i32, ()> = TryThunk::ok(42);
2381 assert_eq!(try_thunk.evaluate(), Ok(42));
2382 }
2383
2384 #[test]
2386 fn test_pure() {
2387 let try_thunk: TryThunk<i32, ()> = TryThunk::pure(42);
2388 assert_eq!(try_thunk.evaluate(), Ok(42));
2389 }
2390
2391 #[test]
2395 fn test_failure() {
2396 let try_thunk: TryThunk<i32, &str> = TryThunk::err("error");
2397 assert_eq!(try_thunk.evaluate(), Err("error"));
2398 }
2399
2400 #[test]
2404 fn test_map() {
2405 let try_thunk: TryThunk<i32, ()> = TryThunk::ok(21).map(|x| x * 2);
2406 assert_eq!(try_thunk.evaluate(), Ok(42));
2407 }
2408
2409 #[test]
2413 fn test_map_err() {
2414 let try_thunk: TryThunk<i32, i32> = TryThunk::err(21).map_err(|x| x * 2);
2415 assert_eq!(try_thunk.evaluate(), Err(42));
2416 }
2417
2418 #[test]
2422 fn test_bind() {
2423 let try_thunk: TryThunk<i32, ()> = TryThunk::ok(21).bind(|x| TryThunk::ok(x * 2));
2424 assert_eq!(try_thunk.evaluate(), Ok(42));
2425 }
2426
2427 #[test]
2431 fn test_borrowing() {
2432 let x = 42;
2433 let try_thunk: TryThunk<&i32, ()> = TryThunk::new(|| Ok(&x));
2434 assert_eq!(try_thunk.evaluate(), Ok(&42));
2435 }
2436
2437 #[test]
2441 fn test_bind_failure() {
2442 let try_thunk = TryThunk::<i32, &str>::err("error").bind(|x| TryThunk::ok(x * 2));
2443 assert_eq!(try_thunk.evaluate(), Err("error"));
2444 }
2445
2446 #[test]
2450 fn test_map_failure() {
2451 let try_thunk = TryThunk::<i32, &str>::err("error").map(|x| x * 2);
2452 assert_eq!(try_thunk.evaluate(), Err("error"));
2453 }
2454
2455 #[test]
2459 fn test_map_err_success() {
2460 let try_thunk = TryThunk::<i32, &str>::pure(42).map_err(|_| "new error");
2461 assert_eq!(try_thunk.evaluate(), Ok(42));
2462 }
2463
2464 #[test]
2466 fn test_try_thunk_from_memo() {
2467 use crate::types::RcLazy;
2468 let memo = RcLazy::new(|| 42);
2469 let try_thunk: TryThunk<i32, ()> = TryThunk::from(memo);
2470 assert_eq!(try_thunk.evaluate(), Ok(42));
2471 }
2472
2473 #[test]
2475 fn test_try_thunk_from_try_memo() {
2476 use crate::types::RcTryLazy;
2477 let memo = RcTryLazy::new(|| Ok(42));
2478 let try_thunk: TryThunk<i32, ()> = TryThunk::from(memo);
2479 assert_eq!(try_thunk.evaluate(), Ok(42));
2480 }
2481
2482 #[test]
2486 fn test_try_thunk_from_eval() {
2487 let eval = Thunk::pure(42);
2488 let try_thunk: TryThunk<i32, ()> = TryThunk::from(eval);
2489 assert_eq!(try_thunk.evaluate(), Ok(42));
2490 }
2491
2492 #[test]
2494 fn test_defer() {
2495 let try_thunk: TryThunk<i32, ()> = TryThunk::defer(|| TryThunk::ok(42));
2496 assert_eq!(try_thunk.evaluate(), Ok(42));
2497 }
2498
2499 #[test]
2503 fn test_catch() {
2504 let try_thunk: TryThunk<i32, &str> = TryThunk::err("error").catch(|_| TryThunk::ok(42));
2505 assert_eq!(try_thunk.evaluate(), Ok(42));
2506 }
2507
2508 #[test]
2512 fn test_catch_with() {
2513 let recovered: TryThunk<i32, i32> =
2514 TryThunk::<i32, &str>::err("error").catch_with(|_| TryThunk::err(42));
2515 assert_eq!(recovered.evaluate(), Err(42));
2516
2517 let ok: TryThunk<i32, i32> = TryThunk::<i32, &str>::ok(1).catch_with(|_| TryThunk::err(42));
2518 assert_eq!(ok.evaluate(), Ok(1));
2519 }
2520
2521 #[test]
2523 fn test_try_thunk_with_err_brand() {
2524 use crate::{
2525 brands::*,
2526 functions::*,
2527 };
2528
2529 let try_thunk: TryThunk<i32, ()> = TryThunk::ok(10);
2531 let mapped = explicit::map::<TryThunkErrAppliedBrand<()>, _, _, _, _>(|x| x * 2, try_thunk);
2532 assert_eq!(mapped.evaluate(), Ok(20));
2533
2534 let try_thunk: TryThunk<i32, ()> = pure::<TryThunkErrAppliedBrand<()>, _>(42);
2536 assert_eq!(try_thunk.evaluate(), Ok(42));
2537
2538 let try_thunk: TryThunk<i32, ()> = TryThunk::ok(10);
2540 let bound = explicit::bind::<TryThunkErrAppliedBrand<()>, _, _, _, _>(try_thunk, |x| {
2541 pure::<TryThunkErrAppliedBrand<()>, _>(x * 2)
2542 });
2543 assert_eq!(bound.evaluate(), Ok(20));
2544
2545 let try_thunk: TryThunk<i32, ()> = TryThunk::ok(10);
2547 let folded = explicit::fold_right::<RcFnBrand, TryThunkErrAppliedBrand<()>, _, _, _, _>(
2548 |x, acc| x + acc,
2549 5,
2550 try_thunk,
2551 );
2552 assert_eq!(folded, 15);
2553 }
2554
2555 #[test]
2557 fn test_bifunctor() {
2558 use crate::{
2559 brands::*,
2560 classes::bifunctor::*,
2561 };
2562
2563 let x: TryThunk<i32, i32> = TryThunk::ok(5);
2564 assert_eq!(bimap::<TryThunkBrand, _, _, _, _>(|e| e + 1, |s| s * 2, x).evaluate(), Ok(10));
2565
2566 let y: TryThunk<i32, i32> = TryThunk::err(5);
2567 assert_eq!(bimap::<TryThunkBrand, _, _, _, _>(|e| e + 1, |s| s * 2, y).evaluate(), Err(6));
2568 }
2569
2570 #[test]
2574 fn test_bifoldable_right() {
2575 use crate::{
2576 brands::*,
2577 functions::*,
2578 };
2579
2580 assert_eq!(
2582 explicit::bi_fold_right::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
2583 (|e: i32, acc| acc - e, |s: i32, acc| acc + s),
2584 10,
2585 TryThunk::err(3),
2586 ),
2587 7
2588 );
2589
2590 assert_eq!(
2592 explicit::bi_fold_right::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
2593 (|e: i32, acc| acc - e, |s: i32, acc| acc + s),
2594 10,
2595 TryThunk::ok(5),
2596 ),
2597 15
2598 );
2599 }
2600
2601 #[test]
2605 fn test_bifoldable_left() {
2606 use crate::{
2607 brands::*,
2608 functions::*,
2609 };
2610
2611 assert_eq!(
2612 explicit::bi_fold_left::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
2613 (|acc, e: i32| acc - e, |acc, s: i32| acc + s),
2614 10,
2615 TryThunk::err(3),
2616 ),
2617 7
2618 );
2619
2620 assert_eq!(
2621 explicit::bi_fold_left::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
2622 (|acc, e: i32| acc - e, |acc, s: i32| acc + s),
2623 10,
2624 TryThunk::ok(5),
2625 ),
2626 15
2627 );
2628 }
2629
2630 #[test]
2634 fn test_bifoldable_map() {
2635 use crate::{
2636 brands::*,
2637 functions::*,
2638 };
2639
2640 assert_eq!(
2641 explicit::bi_fold_map::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
2642 (|e: i32| e.to_string(), |s: i32| s.to_string()),
2643 TryThunk::err(3),
2644 ),
2645 "3".to_string()
2646 );
2647
2648 assert_eq!(
2649 explicit::bi_fold_map::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
2650 (|e: i32| e.to_string(), |s: i32| s.to_string()),
2651 TryThunk::ok(5),
2652 ),
2653 "5".to_string()
2654 );
2655 }
2656
2657 #[test]
2661 fn test_monad_rec_ok_applied() {
2662 use {
2663 crate::{
2664 brands::*,
2665 functions::*,
2666 },
2667 core::ops::ControlFlow,
2668 };
2669
2670 let result = tail_rec_m::<TryThunkOkAppliedBrand<i32>, _, _>(
2671 |x| {
2672 pure::<TryThunkOkAppliedBrand<i32>, _>(
2673 if x < 100 { ControlFlow::Continue(x + 1) } else { ControlFlow::Break(x) },
2674 )
2675 },
2676 0,
2677 );
2678 assert_eq!(result.evaluate(), Err(100));
2679 }
2680
2681 #[test]
2685 fn test_monad_rec_ok_applied_short_circuit() {
2686 use {
2687 crate::{
2688 brands::*,
2689 functions::*,
2690 },
2691 core::ops::ControlFlow,
2692 };
2693
2694 let result = tail_rec_m::<TryThunkOkAppliedBrand<i32>, _, _>(
2695 |x: i32| {
2696 if x == 5 {
2697 TryThunk::ok(42)
2698 } else {
2699 pure::<TryThunkOkAppliedBrand<i32>, _>(ControlFlow::<i32, i32>::Continue(x + 1))
2700 }
2701 },
2702 0,
2703 );
2704 assert_eq!(result.evaluate(), Ok(42));
2705 }
2706
2707 #[test]
2711 fn test_catch_unwind() {
2712 let thunk = TryThunk::<i32, String>::catch_unwind(|| {
2713 if true {
2714 panic!("oops")
2715 }
2716 42
2717 });
2718 assert_eq!(thunk.evaluate(), Err("oops".to_string()));
2719 }
2720
2721 #[test]
2725 fn test_catch_unwind_success() {
2726 let thunk = TryThunk::<i32, String>::catch_unwind(|| 42);
2727 assert_eq!(thunk.evaluate(), Ok(42));
2728 }
2729
2730 #[test]
2732 fn test_try_thunk_with_ok_brand() {
2733 use crate::{
2734 brands::*,
2735 functions::*,
2736 };
2737
2738 let try_thunk: TryThunk<i32, i32> = TryThunk::err(10);
2740 let mapped = explicit::map::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(|x| x * 2, try_thunk);
2741 assert_eq!(mapped.evaluate(), Err(20));
2742
2743 let try_thunk: TryThunk<i32, i32> = pure::<TryThunkOkAppliedBrand<i32>, _>(42);
2745 assert_eq!(try_thunk.evaluate(), Err(42));
2746
2747 let try_thunk: TryThunk<i32, i32> = TryThunk::err(10);
2749 let bound = explicit::bind::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(try_thunk, |x| {
2750 pure::<TryThunkOkAppliedBrand<i32>, _>(x * 2)
2751 });
2752 assert_eq!(bound.evaluate(), Err(20));
2753
2754 let try_thunk: TryThunk<i32, i32> = TryThunk::err(10);
2756 let folded = explicit::fold_right::<RcFnBrand, TryThunkOkAppliedBrand<i32>, _, _, _, _>(
2757 |x, acc| x + acc,
2758 5,
2759 try_thunk,
2760 );
2761 assert_eq!(folded, 15);
2762 }
2763
2764 #[test]
2768 fn test_try_thunk_from_result_ok() {
2769 let try_thunk: TryThunk<i32, String> = TryThunk::from(Ok(42));
2770 assert_eq!(try_thunk.evaluate(), Ok(42));
2771 }
2772
2773 #[test]
2777 fn test_try_thunk_from_result_err() {
2778 let try_thunk: TryThunk<i32, String> = TryThunk::from(Err("error".to_string()));
2779 assert_eq!(try_thunk.evaluate(), Err("error".to_string()));
2780 }
2781
2782 #[test]
2786 fn test_try_thunk_from_try_send_thunk_ok() {
2787 use crate::types::TrySendThunk;
2788 let send: TrySendThunk<i32, ()> = TrySendThunk::pure(42);
2789 let thunk: TryThunk<i32, ()> = TryThunk::from(send);
2790 assert_eq!(thunk.evaluate(), Ok(42));
2791 }
2792
2793 #[test]
2797 fn test_try_thunk_from_try_send_thunk_err() {
2798 use crate::types::TrySendThunk;
2799 let send: TrySendThunk<i32, String> = TrySendThunk::err("fail".to_string());
2800 let thunk: TryThunk<i32, String> = TryThunk::from(send);
2801 assert_eq!(thunk.evaluate(), Err("fail".to_string()));
2802 }
2803
2804 #[quickcheck]
2810 fn functor_identity(x: i32) -> bool {
2811 use crate::{
2812 brands::*,
2813 functions::*,
2814 };
2815 let t: TryThunk<i32, i32> = TryThunk::ok(x);
2816 explicit::map::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(|a| a, t).evaluate() == Ok(x)
2817 }
2818
2819 #[quickcheck]
2821 fn functor_composition(x: i32) -> bool {
2822 use crate::{
2823 brands::*,
2824 functions::*,
2825 };
2826 let f = |a: i32| a.wrapping_add(1);
2827 let g = |a: i32| a.wrapping_mul(2);
2828 let lhs = explicit::map::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(
2829 move |a| f(g(a)),
2830 TryThunk::ok(x),
2831 )
2832 .evaluate();
2833 let rhs = explicit::map::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(
2834 f,
2835 explicit::map::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(g, TryThunk::ok(x)),
2836 )
2837 .evaluate();
2838 lhs == rhs
2839 }
2840
2841 #[quickcheck]
2845 fn monad_left_identity(a: i32) -> bool {
2846 use crate::{
2847 brands::*,
2848 functions::*,
2849 };
2850 let f = |x: i32| pure::<TryThunkErrAppliedBrand<i32>, _>(x.wrapping_mul(2));
2851 let lhs = explicit::bind::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(
2852 pure::<TryThunkErrAppliedBrand<i32>, _>(a),
2853 f,
2854 )
2855 .evaluate();
2856 let rhs = f(a).evaluate();
2857 lhs == rhs
2858 }
2859
2860 #[quickcheck]
2862 fn monad_right_identity(x: i32) -> bool {
2863 use crate::{
2864 brands::*,
2865 functions::*,
2866 };
2867 let lhs = explicit::bind::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(
2868 pure::<TryThunkErrAppliedBrand<i32>, _>(x),
2869 pure::<TryThunkErrAppliedBrand<i32>, _>,
2870 )
2871 .evaluate();
2872 lhs == Ok(x)
2873 }
2874
2875 #[quickcheck]
2877 fn monad_associativity(x: i32) -> bool {
2878 use crate::{
2879 brands::*,
2880 functions::*,
2881 };
2882 let f = |a: i32| pure::<TryThunkErrAppliedBrand<i32>, _>(a.wrapping_add(1));
2883 let g = |a: i32| pure::<TryThunkErrAppliedBrand<i32>, _>(a.wrapping_mul(3));
2884 let m: TryThunk<i32, i32> = TryThunk::ok(x);
2885 let m2: TryThunk<i32, i32> = TryThunk::ok(x);
2886 let lhs = explicit::bind::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(
2887 explicit::bind::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(m, f),
2888 g,
2889 )
2890 .evaluate();
2891 let rhs = explicit::bind::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(m2, move |a| {
2892 explicit::bind::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(f(a), g)
2893 })
2894 .evaluate();
2895 lhs == rhs
2896 }
2897
2898 #[quickcheck]
2900 fn error_short_circuit(e: i32) -> bool {
2901 let t: TryThunk<i32, i32> = TryThunk::err(e);
2902 t.bind(|x| TryThunk::ok(x.wrapping_add(1))).evaluate() == Err(e)
2903 }
2904
2905 #[test]
2909 fn test_lift2_ok_ok() {
2910 let t1: TryThunk<i32, String> = TryThunk::ok(10);
2911 let t2: TryThunk<i32, String> = TryThunk::ok(20);
2912 let t3 = t1.lift2(t2, |a, b| a + b);
2913 assert_eq!(t3.evaluate(), Ok(30));
2914 }
2915
2916 #[test]
2920 fn test_lift2_err_ok() {
2921 let t1: TryThunk<i32, String> = TryThunk::err("first".to_string());
2922 let t2: TryThunk<i32, String> = TryThunk::ok(20);
2923 let t3 = t1.lift2(t2, |a, b| a + b);
2924 assert_eq!(t3.evaluate(), Err("first".to_string()));
2925 }
2926
2927 #[test]
2931 fn test_lift2_ok_err() {
2932 let t1: TryThunk<i32, String> = TryThunk::ok(10);
2933 let t2: TryThunk<i32, String> = TryThunk::err("second".to_string());
2934 let t3 = t1.lift2(t2, |a, b| a + b);
2935 assert_eq!(t3.evaluate(), Err("second".to_string()));
2936 }
2937
2938 #[test]
2942 fn test_then_ok_ok() {
2943 let t1: TryThunk<i32, String> = TryThunk::ok(10);
2944 let t2: TryThunk<i32, String> = TryThunk::ok(20);
2945 let t3 = t1.then(t2);
2946 assert_eq!(t3.evaluate(), Ok(20));
2947 }
2948
2949 #[test]
2953 fn test_then_err_ok() {
2954 let t1: TryThunk<i32, String> = TryThunk::err("first".to_string());
2955 let t2: TryThunk<i32, String> = TryThunk::ok(20);
2956 let t3 = t1.then(t2);
2957 assert_eq!(t3.evaluate(), Err("first".to_string()));
2958 }
2959
2960 #[test]
2964 fn test_then_ok_err() {
2965 let t1: TryThunk<i32, String> = TryThunk::ok(10);
2966 let t2: TryThunk<i32, String> = TryThunk::err("second".to_string());
2967 let t3 = t1.then(t2);
2968 assert_eq!(t3.evaluate(), Err("second".to_string()));
2969 }
2970
2971 #[test]
2975 fn test_into_rc_try_lazy() {
2976 let thunk: TryThunk<i32, ()> = TryThunk::ok(42);
2977 let lazy = thunk.into_rc_try_lazy();
2978 assert_eq!(lazy.evaluate(), Ok(&42));
2979 }
2980
2981 #[test]
2985 fn test_into_rc_try_lazy_caching() {
2986 use std::{
2987 cell::RefCell,
2988 rc::Rc,
2989 };
2990
2991 let counter = Rc::new(RefCell::new(0));
2992 let counter_clone = counter.clone();
2993 let thunk: TryThunk<i32, ()> = TryThunk::new(move || {
2994 *counter_clone.borrow_mut() += 1;
2995 Ok(42)
2996 });
2997 let lazy = thunk.into_rc_try_lazy();
2998
2999 assert_eq!(*counter.borrow(), 0);
3000 assert_eq!(lazy.evaluate(), Ok(&42));
3001 assert_eq!(*counter.borrow(), 1);
3002 assert_eq!(lazy.evaluate(), Ok(&42));
3003 assert_eq!(*counter.borrow(), 1);
3004 }
3005
3006 #[test]
3010 fn test_into_arc_try_lazy() {
3011 let thunk: TryThunk<i32, ()> = TryThunk::ok(42);
3012 let lazy = thunk.into_arc_try_lazy();
3013 assert_eq!(lazy.evaluate(), Ok(&42));
3014 }
3015
3016 #[test]
3020 fn test_into_arc_try_lazy_send_sync() {
3021 use std::thread;
3022
3023 let thunk: TryThunk<i32, String> = TryThunk::ok(42);
3024 let lazy = thunk.into_arc_try_lazy();
3025 let lazy_clone = lazy.clone();
3026
3027 let handle = thread::spawn(move || {
3028 assert_eq!(lazy_clone.evaluate(), Ok(&42));
3029 });
3030
3031 assert_eq!(lazy.evaluate(), Ok(&42));
3032 handle.join().unwrap();
3033 }
3034
3035 #[test]
3039 fn test_catch_unwind_with_panic() {
3040 let thunk = TryThunk::<i32, i32>::catch_unwind_with(
3041 || {
3042 if true {
3043 panic!("oops")
3044 }
3045 42
3046 },
3047 |_payload| -1,
3048 );
3049 assert_eq!(thunk.evaluate(), Err(-1));
3050 }
3051
3052 #[test]
3056 fn test_catch_unwind_with_success() {
3057 let thunk = TryThunk::<i32, i32>::catch_unwind_with(|| 42, |_payload| -1);
3058 assert_eq!(thunk.evaluate(), Ok(42));
3059 }
3060
3061 #[quickcheck]
3065 fn bifunctor_identity_ok(x: i32) -> bool {
3066 use crate::{
3067 brands::*,
3068 classes::bifunctor::*,
3069 };
3070 let t: TryThunk<i32, i32> = TryThunk::ok(x);
3071 bimap::<TryThunkBrand, _, _, _, _>(|e| e, |a| a, t).evaluate() == Ok(x)
3072 }
3073
3074 #[quickcheck]
3076 fn bifunctor_identity_err(e: i32) -> bool {
3077 use crate::{
3078 brands::*,
3079 classes::bifunctor::*,
3080 };
3081 let t: TryThunk<i32, i32> = TryThunk::err(e);
3082 bimap::<TryThunkBrand, _, _, _, _>(|e| e, |a| a, t).evaluate() == Err(e)
3083 }
3084
3085 #[quickcheck]
3087 fn bifunctor_composition_ok(x: i32) -> bool {
3088 use crate::{
3089 brands::*,
3090 classes::bifunctor::*,
3091 };
3092 let f1 = |a: i32| a.wrapping_add(1);
3093 let f2 = |a: i32| a.wrapping_mul(2);
3094 let g1 = |a: i32| a.wrapping_add(10);
3095 let g2 = |a: i32| a.wrapping_mul(3);
3096
3097 let lhs = bimap::<TryThunkBrand, _, _, _, _>(
3098 move |e| f1(f2(e)),
3099 move |a| g1(g2(a)),
3100 TryThunk::ok(x),
3101 )
3102 .evaluate();
3103 let rhs = bimap::<TryThunkBrand, _, _, _, _>(
3104 f1,
3105 g1,
3106 bimap::<TryThunkBrand, _, _, _, _>(f2, g2, TryThunk::ok(x)),
3107 )
3108 .evaluate();
3109 lhs == rhs
3110 }
3111
3112 #[quickcheck]
3114 fn bifunctor_composition_err(e: i32) -> bool {
3115 use crate::{
3116 brands::*,
3117 classes::bifunctor::*,
3118 };
3119 let f1 = |a: i32| a.wrapping_add(1);
3120 let f2 = |a: i32| a.wrapping_mul(2);
3121 let g1 = |a: i32| a.wrapping_add(10);
3122 let g2 = |a: i32| a.wrapping_mul(3);
3123
3124 let lhs = bimap::<TryThunkBrand, _, _, _, _>(
3125 move |e| f1(f2(e)),
3126 move |a| g1(g2(a)),
3127 TryThunk::err(e),
3128 )
3129 .evaluate();
3130 let rhs = bimap::<TryThunkBrand, _, _, _, _>(
3131 f1,
3132 g1,
3133 bimap::<TryThunkBrand, _, _, _, _>(f2, g2, TryThunk::err(e)),
3134 )
3135 .evaluate();
3136 lhs == rhs
3137 }
3138
3139 #[quickcheck]
3143 fn error_monad_left_identity(a: i32) -> bool {
3144 use crate::{
3145 brands::*,
3146 functions::*,
3147 };
3148 let f = |x: i32| pure::<TryThunkOkAppliedBrand<i32>, _>(x.wrapping_mul(2));
3149 let lhs = explicit::bind::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(
3150 pure::<TryThunkOkAppliedBrand<i32>, _>(a),
3151 f,
3152 )
3153 .evaluate();
3154 let rhs = f(a).evaluate();
3155 lhs == rhs
3156 }
3157
3158 #[quickcheck]
3160 fn error_monad_right_identity(x: i32) -> bool {
3161 use crate::{
3162 brands::*,
3163 functions::*,
3164 };
3165 let lhs = explicit::bind::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(
3166 pure::<TryThunkOkAppliedBrand<i32>, _>(x),
3167 pure::<TryThunkOkAppliedBrand<i32>, _>,
3168 )
3169 .evaluate();
3170 lhs == Err(x)
3171 }
3172
3173 #[quickcheck]
3175 fn error_monad_associativity(x: i32) -> bool {
3176 use crate::{
3177 brands::*,
3178 functions::*,
3179 };
3180 let f = |a: i32| pure::<TryThunkOkAppliedBrand<i32>, _>(a.wrapping_add(1));
3181 let g = |a: i32| pure::<TryThunkOkAppliedBrand<i32>, _>(a.wrapping_mul(3));
3182 let m: TryThunk<i32, i32> = TryThunk::err(x);
3183 let m2: TryThunk<i32, i32> = TryThunk::err(x);
3184 let lhs = explicit::bind::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(
3185 explicit::bind::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(m, f),
3186 g,
3187 )
3188 .evaluate();
3189 let rhs = explicit::bind::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(m2, move |a| {
3190 explicit::bind::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(f(a), g)
3191 })
3192 .evaluate();
3193 lhs == rhs
3194 }
3195
3196 #[quickcheck]
3200 fn try_thunk_semigroup_associativity(
3201 a: String,
3202 b: String,
3203 c: String,
3204 ) -> bool {
3205 use crate::classes::semigroup::append;
3206
3207 let ta: TryThunk<String, ()> = TryThunk::ok(a.clone());
3208 let tb: TryThunk<String, ()> = TryThunk::ok(b.clone());
3209 let tc: TryThunk<String, ()> = TryThunk::ok(c.clone());
3210 let ta2: TryThunk<String, ()> = TryThunk::ok(a);
3211 let tb2: TryThunk<String, ()> = TryThunk::ok(b);
3212 let tc2: TryThunk<String, ()> = TryThunk::ok(c);
3213 let lhs = append(append(ta, tb), tc).evaluate();
3214 let rhs = append(ta2, append(tb2, tc2)).evaluate();
3215 lhs == rhs
3216 }
3217
3218 #[quickcheck]
3220 fn try_thunk_monoid_left_identity(x: String) -> bool {
3221 use crate::classes::{
3222 monoid::empty,
3223 semigroup::append,
3224 };
3225
3226 let a: TryThunk<String, ()> = TryThunk::ok(x.clone());
3227 let lhs: TryThunk<String, ()> = append(empty(), a);
3228 lhs.evaluate() == Ok(x)
3229 }
3230
3231 #[quickcheck]
3233 fn try_thunk_monoid_right_identity(x: String) -> bool {
3234 use crate::classes::{
3235 monoid::empty,
3236 semigroup::append,
3237 };
3238
3239 let a: TryThunk<String, ()> = TryThunk::ok(x.clone());
3240 let rhs: TryThunk<String, ()> = append(a, empty());
3241 rhs.evaluate() == Ok(x)
3242 }
3243
3244 #[test]
3249 fn test_into_arc_try_lazy_thread_safety() {
3250 use std::{
3251 sync::{
3252 Arc,
3253 atomic::{
3254 AtomicUsize,
3255 Ordering,
3256 },
3257 },
3258 thread,
3259 };
3260
3261 let counter = Arc::new(AtomicUsize::new(0));
3262 let counter_clone = Arc::clone(&counter);
3263 let thunk: TryThunk<i32, String> = TryThunk::new(move || {
3264 counter_clone.fetch_add(1, Ordering::SeqCst);
3265 Ok(42)
3266 });
3267 let lazy = thunk.into_arc_try_lazy();
3268
3269 let handles: Vec<_> = (0 .. 8)
3270 .map(|_| {
3271 let lazy_clone = lazy.clone();
3272 thread::spawn(move || {
3273 assert_eq!(lazy_clone.evaluate(), Ok(&42));
3274 })
3275 })
3276 .collect();
3277
3278 assert_eq!(lazy.evaluate(), Ok(&42));
3279 for h in handles {
3280 h.join().unwrap();
3281 }
3282
3283 assert_eq!(counter.load(Ordering::SeqCst), 1);
3285 }
3286
3287 #[test]
3291 fn test_semigroup_append_first_err_short_circuits() {
3292 use {
3293 crate::classes::semigroup::append,
3294 std::cell::Cell,
3295 };
3296
3297 let counter = Cell::new(0u32);
3298 let t1: TryThunk<String, &str> = TryThunk::err("first failed");
3299 let t2: TryThunk<String, &str> = TryThunk::new(|| {
3300 counter.set(counter.get() + 1);
3301 Ok("second".to_string())
3302 });
3303 let result = append(t1, t2);
3304 assert_eq!(result.evaluate(), Err("first failed"));
3305 assert_eq!(counter.get(), 0, "second operand should not have been evaluated");
3306 }
3307
3308 #[test]
3313 fn test_semigroup_append_second_err_propagates() {
3314 use crate::classes::semigroup::append;
3315
3316 let t1: TryThunk<String, &str> = TryThunk::pure("hello".to_string());
3317 let t2: TryThunk<String, &str> = TryThunk::err("second failed");
3318 let result = append(t1, t2);
3319 assert_eq!(result.evaluate(), Err("second failed"));
3320 }
3321
3322 #[test]
3326 fn test_tail_rec_m_success() {
3327 use core::ops::ControlFlow;
3328 let result: TryThunk<i32, ()> = TryThunk::tail_rec_m(
3329 |x| {
3330 TryThunk::ok(
3331 if x < 1000 { ControlFlow::Continue(x + 1) } else { ControlFlow::Break(x) },
3332 )
3333 },
3334 0,
3335 );
3336 assert_eq!(result.evaluate(), Ok(1000));
3337 }
3338
3339 #[test]
3344 fn test_tail_rec_m_early_error() {
3345 use core::ops::ControlFlow;
3346 let result: TryThunk<i32, &str> = TryThunk::tail_rec_m(
3347 |x| {
3348 if x == 5 {
3349 TryThunk::err("stopped at 5")
3350 } else {
3351 TryThunk::ok(ControlFlow::Continue(x + 1))
3352 }
3353 },
3354 0,
3355 );
3356 assert_eq!(result.evaluate(), Err("stopped at 5"));
3357 }
3358
3359 #[test]
3363 fn test_tail_rec_m_stack_safety() {
3364 use core::ops::ControlFlow;
3365 let iterations: i64 = 100_000;
3366 let result: TryThunk<i64, ()> = TryThunk::tail_rec_m(
3367 |acc| {
3368 TryThunk::ok(
3369 if acc < iterations {
3370 ControlFlow::Continue(acc + 1)
3371 } else {
3372 ControlFlow::Break(acc)
3373 },
3374 )
3375 },
3376 0i64,
3377 );
3378 assert_eq!(result.evaluate(), Ok(iterations));
3379 }
3380
3381 #[test]
3387 fn test_arc_tail_rec_m_success() {
3388 use {
3389 core::ops::ControlFlow,
3390 std::sync::{
3391 Arc,
3392 atomic::{
3393 AtomicUsize,
3394 Ordering,
3395 },
3396 },
3397 };
3398 let counter = Arc::new(AtomicUsize::new(0));
3399 let counter_clone = Arc::clone(&counter);
3400 let result: TryThunk<i32, ()> = TryThunk::arc_tail_rec_m(
3401 move |x| {
3402 counter_clone.fetch_add(1, Ordering::SeqCst);
3403 TryThunk::ok(
3404 if x < 100 { ControlFlow::Continue(x + 1) } else { ControlFlow::Break(x) },
3405 )
3406 },
3407 0,
3408 );
3409 assert_eq!(result.evaluate(), Ok(100));
3410 assert_eq!(counter.load(Ordering::SeqCst), 101);
3411 }
3412
3413 #[test]
3417 fn test_arc_tail_rec_m_early_error() {
3418 use core::ops::ControlFlow;
3419 let result: TryThunk<i32, &str> = TryThunk::arc_tail_rec_m(
3420 |x| {
3421 if x == 5 {
3422 TryThunk::err("stopped at 5")
3423 } else {
3424 TryThunk::ok(ControlFlow::Continue(x + 1))
3425 }
3426 },
3427 0,
3428 );
3429 assert_eq!(result.evaluate(), Err("stopped at 5"));
3430 }
3431
3432 #[test]
3436 fn test_arc_tail_rec_m_stack_safety() {
3437 use core::ops::ControlFlow;
3438 let iterations: i64 = 100_000;
3439 let result: TryThunk<i64, ()> = TryThunk::arc_tail_rec_m(
3440 |acc| {
3441 TryThunk::ok(
3442 if acc < iterations {
3443 ControlFlow::Continue(acc + 1)
3444 } else {
3445 ControlFlow::Break(acc)
3446 },
3447 )
3448 },
3449 0i64,
3450 );
3451 assert_eq!(result.evaluate(), Ok(iterations));
3452 }
3453}