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 #[multi_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>(
1136 ff: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as CloneFn>::Of<'a, A, B>>),
1137 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1138 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
1139 ff.bind(move |f| {
1140 fa.map(
1141 #[expect(clippy::redundant_closure, reason = "Required for move semantics")]
1142 move |a| f(a),
1143 )
1144 })
1145 }
1146 }
1147
1148 #[document_type_parameters("The error type.")]
1149 impl<E: 'static> Semimonad for TryThunkErrAppliedBrand<E> {
1150 #[document_signature]
1152 #[document_type_parameters(
1154 "The lifetime of the computation.",
1155 "The type of the result of the first computation.",
1156 "The type of the result of the new computation."
1157 )]
1158 #[document_parameters(
1160 "The first `TryThunk`.",
1161 "The function to apply to the result of the computation."
1162 )]
1163 #[document_returns("A new `TryThunk` instance representing the chained computation.")]
1165 #[document_examples]
1166 fn bind<'a, A: 'a, B: 'a>(
1181 ma: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1182 func: impl Fn(A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) + 'a,
1183 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
1184 ma.bind(func)
1185 }
1186 }
1187
1188 #[document_type_parameters("The error type.")]
1189 impl<E: 'static> MonadRec for TryThunkErrAppliedBrand<E> {
1190 #[document_signature]
1192 #[document_type_parameters(
1194 "The lifetime of the computation.",
1195 "The type of the initial value and loop state.",
1196 "The type of the result."
1197 )]
1198 #[document_parameters("The step function.", "The initial value.")]
1200 #[document_returns("The result of the computation.")]
1202 #[document_examples]
1204 fn tail_rec_m<'a, A: 'a, B: 'a>(
1227 f: impl Fn(
1228 A,
1229 )
1230 -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, ControlFlow<B, A>>)
1231 + 'a,
1232 a: A,
1233 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
1234 TryThunk::new(move || {
1235 let mut current = a;
1236 loop {
1237 match f(current).evaluate() {
1238 Ok(ControlFlow::Continue(next)) => current = next,
1239 Ok(ControlFlow::Break(res)) => break Ok(res),
1240 Err(e) => break Err(e),
1241 }
1242 }
1243 })
1244 }
1245 }
1246
1247 #[document_type_parameters("The error type.")]
1248 impl<E: 'static> Foldable for TryThunkErrAppliedBrand<E> {
1249 #[document_signature]
1251 #[document_type_parameters(
1253 "The lifetime of the computation.",
1254 "The brand of the cloneable function to use.",
1255 "The type of the elements in the structure.",
1256 "The type of the accumulator."
1257 )]
1258 #[document_parameters(
1260 "The function to apply to each element and the accumulator.",
1261 "The initial value of the accumulator.",
1262 "The `TryThunk` to fold."
1263 )]
1264 #[document_returns("The final accumulator value.")]
1266 #[document_examples]
1267 fn fold_right<'a, FnBrand, A: 'a + Clone, B: 'a>(
1284 func: impl Fn(A, B) -> B + 'a,
1285 initial: B,
1286 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1287 ) -> B
1288 where
1289 FnBrand: CloneFn + 'a, {
1290 match fa.evaluate() {
1291 Ok(a) => func(a, initial),
1292 Err(_) => initial,
1293 }
1294 }
1295
1296 #[document_signature]
1298 #[document_type_parameters(
1300 "The lifetime of the computation.",
1301 "The brand of the cloneable function to use.",
1302 "The type of the elements in the structure.",
1303 "The type of the accumulator."
1304 )]
1305 #[document_parameters(
1307 "The function to apply to the accumulator and each element.",
1308 "The initial value of the accumulator.",
1309 "The `TryThunk` to fold."
1310 )]
1311 #[document_returns("The final accumulator value.")]
1313 #[document_examples]
1314 fn fold_left<'a, FnBrand, A: 'a + Clone, B: 'a>(
1331 func: impl Fn(B, A) -> B + 'a,
1332 initial: B,
1333 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1334 ) -> B
1335 where
1336 FnBrand: CloneFn + 'a, {
1337 match fa.evaluate() {
1338 Ok(a) => func(initial, a),
1339 Err(_) => initial,
1340 }
1341 }
1342
1343 #[document_signature]
1345 #[document_type_parameters(
1347 "The lifetime of the computation.",
1348 "The brand of the cloneable function to use.",
1349 "The type of the elements in the structure.",
1350 "The type of the monoid."
1351 )]
1352 #[document_parameters("The mapping function.", "The TryThunk to fold.")]
1354 #[document_returns("The monoid value.")]
1356 #[document_examples]
1358 fn fold_map<'a, FnBrand, A: 'a + Clone, M>(
1374 func: impl Fn(A) -> M + 'a,
1375 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1376 ) -> M
1377 where
1378 M: Monoid + 'a,
1379 FnBrand: CloneFn + 'a, {
1380 match fa.evaluate() {
1381 Ok(a) => func(a),
1382 Err(_) => M::empty(),
1383 }
1384 }
1385 }
1386
1387 #[document_type_parameters(
1388 "The lifetime of the computation.",
1389 "The success value type.",
1390 "The error value type."
1391 )]
1392 impl<'a, A: Semigroup + 'a, E: 'a> Semigroup for TryThunk<'a, A, E> {
1393 #[document_signature]
1395 #[document_parameters("The first `TryThunk`.", "The second `TryThunk`.")]
1397 #[document_returns("A new `TryThunk` containing the combined result.")]
1399 #[document_examples]
1401 fn append(
1416 a: Self,
1417 b: Self,
1418 ) -> Self {
1419 TryThunk::new(move || {
1420 let a_val = a.evaluate()?;
1421 let b_val = b.evaluate()?;
1422 Ok(Semigroup::append(a_val, b_val))
1423 })
1424 }
1425 }
1426
1427 #[document_type_parameters(
1428 "The lifetime of the computation.",
1429 "The success value type.",
1430 "The error value type."
1431 )]
1432 impl<'a, A: Monoid + 'a, E: 'a> Monoid for TryThunk<'a, A, E> {
1433 #[document_signature]
1435 #[document_returns("A `TryThunk` producing the identity value of `A`.")]
1437 #[document_examples]
1439 fn empty() -> Self {
1450 TryThunk(Thunk::pure(Ok(Monoid::empty())))
1451 }
1452 }
1453
1454 impl_kind! {
1455 for TryThunkBrand {
1462 type Of<'a, E: 'a, A: 'a>: 'a = TryThunk<'a, A, E>;
1463 }
1464 }
1465
1466 impl Bifunctor for TryThunkBrand {
1467 #[document_signature]
1471 #[document_type_parameters(
1473 "The lifetime of the values.",
1474 "The type of the error value.",
1475 "The type of the mapped error value.",
1476 "The type of the success value.",
1477 "The type of the mapped success value."
1478 )]
1479 #[document_parameters(
1481 "The function to apply to the error.",
1482 "The function to apply to the success.",
1483 "The `TryThunk` to map over."
1484 )]
1485 #[document_returns("A new `TryThunk` containing the mapped values.")]
1487 #[document_examples]
1488 fn bimap<'a, A: 'a, B: 'a, C: 'a, D: 'a>(
1509 f: impl Fn(A) -> B + 'a,
1510 g: impl Fn(C) -> D + 'a,
1511 p: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, C>),
1512 ) -> Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, B, D>) {
1513 TryThunk(p.0.map(move |result| match result {
1514 Ok(c) => Ok(g(c)),
1515 Err(a) => Err(f(a)),
1516 }))
1517 }
1518 }
1519
1520 impl Bifoldable for TryThunkBrand {
1521 #[document_signature]
1526 #[document_type_parameters(
1528 "The lifetime of the values.",
1529 "The brand of the cloneable function to use.",
1530 "The error type (first position).",
1531 "The success type (second position).",
1532 "The accumulator type."
1533 )]
1534 #[document_parameters(
1536 "The step function applied to the error value.",
1537 "The step function applied to the success value.",
1538 "The initial accumulator.",
1539 "The `TryThunk` to fold."
1540 )]
1541 #[document_returns("`f(e, z)` for `Err(e)`, or `g(a, z)` for `Ok(a)`.")]
1543 #[document_examples]
1544 fn bi_fold_right<'a, FnBrand: CloneFn + 'a, A: 'a + Clone, B: 'a + Clone, C: 'a>(
1570 f: impl Fn(A, C) -> C + 'a,
1571 g: impl Fn(B, C) -> C + 'a,
1572 z: C,
1573 p: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
1574 ) -> C {
1575 match p.evaluate() {
1576 Err(a) => f(a, z),
1577 Ok(b) => g(b, z),
1578 }
1579 }
1580
1581 #[document_signature]
1586 #[document_type_parameters(
1588 "The lifetime of the values.",
1589 "The brand of the cloneable function to use.",
1590 "The error type (first position).",
1591 "The success type (second position).",
1592 "The accumulator type."
1593 )]
1594 #[document_parameters(
1596 "The step function applied to the error value.",
1597 "The step function applied to the success value.",
1598 "The initial accumulator.",
1599 "The `TryThunk` to fold."
1600 )]
1601 #[document_returns("`f(z, e)` for `Err(e)`, or `g(z, a)` for `Ok(a)`.")]
1603 #[document_examples]
1604 fn bi_fold_left<'a, FnBrand: CloneFn + 'a, A: 'a + Clone, B: 'a + Clone, C: 'a>(
1630 f: impl Fn(C, A) -> C + 'a,
1631 g: impl Fn(C, B) -> C + 'a,
1632 z: C,
1633 p: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
1634 ) -> C {
1635 match p.evaluate() {
1636 Err(a) => f(z, a),
1637 Ok(b) => g(z, b),
1638 }
1639 }
1640
1641 #[document_signature]
1646 #[document_type_parameters(
1648 "The lifetime of the values.",
1649 "The brand of the cloneable function to use.",
1650 "The error type (first position).",
1651 "The success type (second position).",
1652 "The monoid type."
1653 )]
1654 #[document_parameters(
1656 "The function mapping the error to the monoid.",
1657 "The function mapping the success to the monoid.",
1658 "The `TryThunk` to fold."
1659 )]
1660 #[document_returns("`f(e)` for `Err(e)`, or `g(a)` for `Ok(a)`.")]
1662 #[document_examples]
1663 fn bi_fold_map<'a, FnBrand: CloneFn + 'a, A: 'a + Clone, B: 'a + Clone, M>(
1687 f: impl Fn(A) -> M + 'a,
1688 g: impl Fn(B) -> M + 'a,
1689 p: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
1690 ) -> M
1691 where
1692 M: Monoid + 'a, {
1693 match p.evaluate() {
1694 Err(a) => f(a),
1695 Ok(b) => g(b),
1696 }
1697 }
1698 }
1699
1700 impl_kind! {
1701 #[multi_brand]
1702 impl<A: 'static> for TryThunkOkAppliedBrand<A> {
1714 #[document_default]
1715 type Of<'a, E: 'a>: 'a = TryThunk<'a, A, E>;
1716 }
1717 }
1718
1719 #[document_type_parameters("The success type.")]
1720 impl<A: 'static> Functor for TryThunkOkAppliedBrand<A> {
1721 #[document_signature]
1723 #[document_type_parameters(
1725 "The lifetime of the computation.",
1726 "The type of the error value inside the `TryThunk`.",
1727 "The type of the result of the transformation."
1728 )]
1729 #[document_parameters("The function to apply to the error.", "The `TryThunk` instance.")]
1731 #[document_returns("A new `TryThunk` instance with the transformed error.")]
1733 #[document_examples]
1735 fn map<'a, E: 'a, E2: 'a>(
1748 func: impl Fn(E) -> E2 + 'a,
1749 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
1750 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
1751 fa.map_err(func)
1752 }
1753 }
1754
1755 #[document_type_parameters("The success type.")]
1756 impl<A: 'static> Pointed for TryThunkOkAppliedBrand<A> {
1757 #[document_signature]
1759 #[document_type_parameters(
1761 "The lifetime of the computation.",
1762 "The type of the value to wrap."
1763 )]
1764 #[document_parameters("The value to wrap.")]
1766 #[document_returns("A new `TryThunk` instance containing the value as an error.")]
1768 #[document_examples]
1770 fn pure<'a, E: 'a>(e: E) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>) {
1782 TryThunk::err(e)
1783 }
1784 }
1785
1786 #[document_type_parameters("The success type.")]
1787 impl<A: 'static> Lift for TryThunkOkAppliedBrand<A> {
1788 #[document_signature]
1798 #[document_type_parameters(
1800 "The lifetime of the computation.",
1801 "The type of the first error value.",
1802 "The type of the second error value.",
1803 "The type of the result error value."
1804 )]
1805 #[document_parameters(
1807 "The binary function to apply to the errors.",
1808 "The first `TryThunk`.",
1809 "The second `TryThunk`."
1810 )]
1811 #[document_returns(
1813 "A new `TryThunk` instance containing the result of applying the function to the errors."
1814 )]
1815 #[document_examples]
1816 fn lift2<'a, E1, E2, E3>(
1834 func: impl Fn(E1, E2) -> E3 + 'a,
1835 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E1>),
1836 fb: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>),
1837 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E3>)
1838 where
1839 E1: Clone + 'a,
1840 E2: Clone + 'a,
1841 E3: 'a, {
1842 TryThunk::new(move || match fa.evaluate() {
1843 Ok(a) => Ok(a),
1844 Err(e1) => match fb.evaluate() {
1845 Ok(a) => Ok(a),
1846 Err(e2) => Err(func(e1, e2)),
1847 },
1848 })
1849 }
1850 }
1851
1852 #[document_type_parameters("The success type.")]
1853 impl<A: 'static> ApplyFirst for TryThunkOkAppliedBrand<A> {}
1854
1855 #[document_type_parameters("The success type.")]
1856 impl<A: 'static> ApplySecond for TryThunkOkAppliedBrand<A> {}
1857
1858 #[document_type_parameters("The success type.")]
1859 impl<A: 'static> Semiapplicative for TryThunkOkAppliedBrand<A> {
1860 #[document_signature]
1870 #[document_type_parameters(
1872 "The lifetime of the computation.",
1873 "The brand of the cloneable function wrapper.",
1874 "The type of the input error.",
1875 "The type of the result error."
1876 )]
1877 #[document_parameters(
1879 "The `TryThunk` containing the function (in Err).",
1880 "The `TryThunk` containing the value (in Err)."
1881 )]
1882 #[document_returns(
1884 "A new `TryThunk` instance containing the result of applying the function."
1885 )]
1886 #[document_examples]
1887 fn apply<'a, FnBrand: 'a + CloneFn, E1: 'a + Clone, E2: 'a>(
1903 ff: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as CloneFn>::Of<'a, E1, E2>>),
1904 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E1>),
1905 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
1906 TryThunk::new(move || match ff.evaluate() {
1907 Ok(a) => Ok(a),
1908 Err(f) => match fa.evaluate() {
1909 Ok(a) => Ok(a),
1910 Err(e) => Err(f(e)),
1911 },
1912 })
1913 }
1914 }
1915
1916 #[document_type_parameters("The success type.")]
1917 impl<A: 'static> Semimonad for TryThunkOkAppliedBrand<A> {
1918 #[document_signature]
1920 #[document_type_parameters(
1922 "The lifetime of the computation.",
1923 "The type of the result of the first computation (error).",
1924 "The type of the result of the new computation (error)."
1925 )]
1926 #[document_parameters(
1928 "The first `TryThunk`.",
1929 "The function to apply to the error result of the computation."
1930 )]
1931 #[document_returns("A new `TryThunk` instance representing the chained computation.")]
1933 #[document_examples]
1934 fn bind<'a, E1: 'a, E2: 'a>(
1949 ma: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E1>),
1950 func: impl Fn(E1) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) + 'a,
1951 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
1952 TryThunk::new(move || match ma.evaluate() {
1953 Ok(a) => Ok(a),
1954 Err(e) => func(e).evaluate(),
1955 })
1956 }
1957 }
1958
1959 #[document_type_parameters("The success type.")]
1960 impl<A: 'static> MonadRec for TryThunkOkAppliedBrand<A> {
1961 #[document_signature]
1967 #[document_type_parameters(
1969 "The lifetime of the computation.",
1970 "The type of the initial value and loop state.",
1971 "The type of the result."
1972 )]
1973 #[document_parameters("The step function.", "The initial value.")]
1975 #[document_returns("The result of the computation.")]
1977 #[document_examples]
1979 fn tail_rec_m<'a, E: 'a, E2: 'a>(
2002 f: impl Fn(
2003 E,
2004 )
2005 -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, ControlFlow<E2, E>>)
2006 + 'a,
2007 e: E,
2008 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
2009 TryThunk::new(move || {
2010 let mut current = e;
2011 loop {
2012 match f(current).evaluate() {
2013 Err(ControlFlow::Continue(next)) => current = next,
2014 Err(ControlFlow::Break(res)) => break Err(res),
2015 Ok(a) => break Ok(a),
2016 }
2017 }
2018 })
2019 }
2020 }
2021
2022 #[document_type_parameters("The success type.")]
2023 impl<A: 'static> Foldable for TryThunkOkAppliedBrand<A> {
2024 #[document_signature]
2026 #[document_type_parameters(
2028 "The lifetime of the computation.",
2029 "The brand of the cloneable function to use.",
2030 "The type of the elements in the structure.",
2031 "The type of the accumulator."
2032 )]
2033 #[document_parameters(
2035 "The function to apply to each element and the accumulator.",
2036 "The initial value of the accumulator.",
2037 "The `TryThunk` to fold."
2038 )]
2039 #[document_returns("The final accumulator value.")]
2041 #[document_examples]
2042 fn fold_right<'a, FnBrand, E: 'a + Clone, B: 'a>(
2059 func: impl Fn(E, B) -> B + 'a,
2060 initial: B,
2061 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
2062 ) -> B
2063 where
2064 FnBrand: CloneFn + 'a, {
2065 match fa.evaluate() {
2066 Err(e) => func(e, initial),
2067 Ok(_) => initial,
2068 }
2069 }
2070
2071 #[document_signature]
2073 #[document_type_parameters(
2075 "The lifetime of the computation.",
2076 "The brand of the cloneable function to use.",
2077 "The type of the elements in the structure.",
2078 "The type of the accumulator."
2079 )]
2080 #[document_parameters(
2082 "The function to apply to the accumulator and each element.",
2083 "The initial value of the accumulator.",
2084 "The `TryThunk` to fold."
2085 )]
2086 #[document_returns("The final accumulator value.")]
2088 #[document_examples]
2089 fn fold_left<'a, FnBrand, E: 'a + Clone, B: 'a>(
2106 func: impl Fn(B, E) -> B + 'a,
2107 initial: B,
2108 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
2109 ) -> B
2110 where
2111 FnBrand: CloneFn + 'a, {
2112 match fa.evaluate() {
2113 Err(e) => func(initial, e),
2114 Ok(_) => initial,
2115 }
2116 }
2117
2118 #[document_signature]
2120 #[document_type_parameters(
2122 "The lifetime of the computation.",
2123 "The brand of the cloneable function to use.",
2124 "The type of the elements in the structure.",
2125 "The type of the monoid."
2126 )]
2127 #[document_parameters("The mapping function.", "The TryThunk to fold.")]
2129 #[document_returns("The monoid value.")]
2131 #[document_examples]
2133 fn fold_map<'a, FnBrand, E: 'a + Clone, M>(
2149 func: impl Fn(E) -> M + 'a,
2150 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
2151 ) -> M
2152 where
2153 M: Monoid + 'a,
2154 FnBrand: CloneFn + 'a, {
2155 match fa.evaluate() {
2156 Err(e) => func(e),
2157 Ok(_) => M::empty(),
2158 }
2159 }
2160 }
2161
2162 #[document_type_parameters(
2163 "The lifetime of the computation.",
2164 "The type of the success value.",
2165 "The type of the error value."
2166 )]
2167 #[document_parameters("The try-thunk to format.")]
2168 impl<'a, A, E> fmt::Debug for TryThunk<'a, A, E> {
2169 #[document_signature]
2171 #[document_parameters("The formatter.")]
2172 #[document_returns("The formatting result.")]
2173 #[document_examples]
2174 fn fmt(
2181 &self,
2182 f: &mut fmt::Formatter<'_>,
2183 ) -> fmt::Result {
2184 f.write_str("TryThunk(<unevaluated>)")
2185 }
2186 }
2187
2188 #[document_type_parameters("The error type.")]
2189 impl<E: 'static> WithIndex for TryThunkErrAppliedBrand<E> {
2190 type Index = ();
2191 }
2192
2193 #[document_type_parameters("The error type.")]
2194 impl<E: 'static> FunctorWithIndex for TryThunkErrAppliedBrand<E> {
2195 #[document_signature]
2197 #[document_type_parameters(
2198 "The lifetime of the computation.",
2199 "The type of the success value inside the `TryThunk`.",
2200 "The type of the result of applying the function."
2201 )]
2202 #[document_parameters(
2203 "The function to apply to the value and its index.",
2204 "The `TryThunk` to map over."
2205 )]
2206 #[document_returns(
2207 "A new `TryThunk` containing the result of applying the function, or the original error."
2208 )]
2209 #[document_examples]
2210 fn map_with_index<'a, A: 'a, B: 'a>(
2223 f: impl Fn((), A) -> B + 'a,
2224 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
2225 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
2226 fa.map(move |a| f((), a))
2227 }
2228 }
2229
2230 #[document_type_parameters("The error type.")]
2231 impl<E: 'static> FoldableWithIndex for TryThunkErrAppliedBrand<E> {
2232 #[document_signature]
2234 #[document_type_parameters(
2235 "The lifetime of the computation.",
2236 "The brand of the cloneable function to use.",
2237 "The type of the success value inside the `TryThunk`.",
2238 "The monoid type."
2239 )]
2240 #[document_parameters(
2241 "The function to apply to the value and its index.",
2242 "The `TryThunk` to fold."
2243 )]
2244 #[document_returns("The monoid value.")]
2245 #[document_examples]
2246 fn fold_map_with_index<'a, FnBrand, A: 'a + Clone, R: Monoid>(
2263 f: impl Fn((), A) -> R + 'a,
2264 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
2265 ) -> R
2266 where
2267 FnBrand: LiftFn + 'a, {
2268 match fa.evaluate() {
2269 Ok(a) => f((), a),
2270 Err(_) => R::empty(),
2271 }
2272 }
2273 }
2274
2275 #[document_type_parameters("The success type.")]
2276 impl<A: 'static> WithIndex for TryThunkOkAppliedBrand<A> {
2277 type Index = ();
2278 }
2279
2280 #[document_type_parameters("The success type.")]
2281 impl<A: 'static> FunctorWithIndex for TryThunkOkAppliedBrand<A> {
2282 #[document_signature]
2284 #[document_type_parameters(
2285 "The lifetime of the computation.",
2286 "The type of the error value inside the `TryThunk`.",
2287 "The type of the result of applying the function."
2288 )]
2289 #[document_parameters(
2290 "The function to apply to the error and its index.",
2291 "The `TryThunk` to map over."
2292 )]
2293 #[document_returns(
2294 "A new `TryThunk` containing the original success or the transformed error."
2295 )]
2296 #[document_examples]
2297 fn map_with_index<'a, E: 'a, E2: 'a>(
2310 f: impl Fn((), E) -> E2 + 'a,
2311 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
2312 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
2313 fa.map_err(move |e| f((), e))
2314 }
2315 }
2316
2317 #[document_type_parameters("The success type.")]
2318 impl<A: 'static> FoldableWithIndex for TryThunkOkAppliedBrand<A> {
2319 #[document_signature]
2321 #[document_type_parameters(
2322 "The lifetime of the computation.",
2323 "The brand of the cloneable function to use.",
2324 "The type of the error value inside the `TryThunk`.",
2325 "The monoid type."
2326 )]
2327 #[document_parameters(
2328 "The function to apply to the error and its index.",
2329 "The `TryThunk` to fold."
2330 )]
2331 #[document_returns("The monoid value.")]
2332 #[document_examples]
2333 fn fold_map_with_index<'a, FnBrand, E: 'a + Clone, R: Monoid>(
2350 f: impl Fn((), E) -> R + 'a,
2351 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
2352 ) -> R
2353 where
2354 FnBrand: LiftFn + 'a, {
2355 match fa.evaluate() {
2356 Err(e) => f((), e),
2357 Ok(_) => R::empty(),
2358 }
2359 }
2360 }
2361}
2362pub use inner::*;
2363
2364#[cfg(test)]
2365#[expect(
2366 clippy::unwrap_used,
2367 clippy::panic,
2368 reason = "Tests use panicking operations for brevity and clarity"
2369)]
2370mod tests {
2371 use {
2372 super::*,
2373 crate::types::Thunk,
2374 quickcheck_macros::quickcheck,
2375 };
2376
2377 #[test]
2381 fn test_success() {
2382 let try_thunk: TryThunk<i32, ()> = TryThunk::ok(42);
2383 assert_eq!(try_thunk.evaluate(), Ok(42));
2384 }
2385
2386 #[test]
2388 fn test_pure() {
2389 let try_thunk: TryThunk<i32, ()> = TryThunk::pure(42);
2390 assert_eq!(try_thunk.evaluate(), Ok(42));
2391 }
2392
2393 #[test]
2397 fn test_failure() {
2398 let try_thunk: TryThunk<i32, &str> = TryThunk::err("error");
2399 assert_eq!(try_thunk.evaluate(), Err("error"));
2400 }
2401
2402 #[test]
2406 fn test_map() {
2407 let try_thunk: TryThunk<i32, ()> = TryThunk::ok(21).map(|x| x * 2);
2408 assert_eq!(try_thunk.evaluate(), Ok(42));
2409 }
2410
2411 #[test]
2415 fn test_map_err() {
2416 let try_thunk: TryThunk<i32, i32> = TryThunk::err(21).map_err(|x| x * 2);
2417 assert_eq!(try_thunk.evaluate(), Err(42));
2418 }
2419
2420 #[test]
2424 fn test_bind() {
2425 let try_thunk: TryThunk<i32, ()> = TryThunk::ok(21).bind(|x| TryThunk::ok(x * 2));
2426 assert_eq!(try_thunk.evaluate(), Ok(42));
2427 }
2428
2429 #[test]
2433 fn test_borrowing() {
2434 let x = 42;
2435 let try_thunk: TryThunk<&i32, ()> = TryThunk::new(|| Ok(&x));
2436 assert_eq!(try_thunk.evaluate(), Ok(&42));
2437 }
2438
2439 #[test]
2443 fn test_bind_failure() {
2444 let try_thunk = TryThunk::<i32, &str>::err("error").bind(|x| TryThunk::ok(x * 2));
2445 assert_eq!(try_thunk.evaluate(), Err("error"));
2446 }
2447
2448 #[test]
2452 fn test_map_failure() {
2453 let try_thunk = TryThunk::<i32, &str>::err("error").map(|x| x * 2);
2454 assert_eq!(try_thunk.evaluate(), Err("error"));
2455 }
2456
2457 #[test]
2461 fn test_map_err_success() {
2462 let try_thunk = TryThunk::<i32, &str>::pure(42).map_err(|_| "new error");
2463 assert_eq!(try_thunk.evaluate(), Ok(42));
2464 }
2465
2466 #[test]
2468 fn test_try_thunk_from_memo() {
2469 use crate::types::RcLazy;
2470 let memo = RcLazy::new(|| 42);
2471 let try_thunk: TryThunk<i32, ()> = TryThunk::from(memo);
2472 assert_eq!(try_thunk.evaluate(), Ok(42));
2473 }
2474
2475 #[test]
2477 fn test_try_thunk_from_try_memo() {
2478 use crate::types::RcTryLazy;
2479 let memo = RcTryLazy::new(|| Ok(42));
2480 let try_thunk: TryThunk<i32, ()> = TryThunk::from(memo);
2481 assert_eq!(try_thunk.evaluate(), Ok(42));
2482 }
2483
2484 #[test]
2488 fn test_try_thunk_from_eval() {
2489 let eval = Thunk::pure(42);
2490 let try_thunk: TryThunk<i32, ()> = TryThunk::from(eval);
2491 assert_eq!(try_thunk.evaluate(), Ok(42));
2492 }
2493
2494 #[test]
2496 fn test_defer() {
2497 let try_thunk: TryThunk<i32, ()> = TryThunk::defer(|| TryThunk::ok(42));
2498 assert_eq!(try_thunk.evaluate(), Ok(42));
2499 }
2500
2501 #[test]
2505 fn test_catch() {
2506 let try_thunk: TryThunk<i32, &str> = TryThunk::err("error").catch(|_| TryThunk::ok(42));
2507 assert_eq!(try_thunk.evaluate(), Ok(42));
2508 }
2509
2510 #[test]
2514 fn test_catch_with() {
2515 let recovered: TryThunk<i32, i32> =
2516 TryThunk::<i32, &str>::err("error").catch_with(|_| TryThunk::err(42));
2517 assert_eq!(recovered.evaluate(), Err(42));
2518
2519 let ok: TryThunk<i32, i32> = TryThunk::<i32, &str>::ok(1).catch_with(|_| TryThunk::err(42));
2520 assert_eq!(ok.evaluate(), Ok(1));
2521 }
2522
2523 #[test]
2525 fn test_try_thunk_with_err_brand() {
2526 use crate::{
2527 brands::*,
2528 functions::*,
2529 };
2530
2531 let try_thunk: TryThunk<i32, ()> = TryThunk::ok(10);
2533 let mapped = explicit::map::<TryThunkErrAppliedBrand<()>, _, _, _, _>(|x| x * 2, try_thunk);
2534 assert_eq!(mapped.evaluate(), Ok(20));
2535
2536 let try_thunk: TryThunk<i32, ()> = pure::<TryThunkErrAppliedBrand<()>, _>(42);
2538 assert_eq!(try_thunk.evaluate(), Ok(42));
2539
2540 let try_thunk: TryThunk<i32, ()> = TryThunk::ok(10);
2542 let bound = explicit::bind::<TryThunkErrAppliedBrand<()>, _, _, _, _>(try_thunk, |x| {
2543 pure::<TryThunkErrAppliedBrand<()>, _>(x * 2)
2544 });
2545 assert_eq!(bound.evaluate(), Ok(20));
2546
2547 let try_thunk: TryThunk<i32, ()> = TryThunk::ok(10);
2549 let folded = explicit::fold_right::<RcFnBrand, TryThunkErrAppliedBrand<()>, _, _, _, _>(
2550 |x, acc| x + acc,
2551 5,
2552 try_thunk,
2553 );
2554 assert_eq!(folded, 15);
2555 }
2556
2557 #[test]
2559 fn test_bifunctor() {
2560 use crate::{
2561 brands::*,
2562 classes::bifunctor::*,
2563 };
2564
2565 let x: TryThunk<i32, i32> = TryThunk::ok(5);
2566 assert_eq!(bimap::<TryThunkBrand, _, _, _, _>(|e| e + 1, |s| s * 2, x).evaluate(), Ok(10));
2567
2568 let y: TryThunk<i32, i32> = TryThunk::err(5);
2569 assert_eq!(bimap::<TryThunkBrand, _, _, _, _>(|e| e + 1, |s| s * 2, y).evaluate(), Err(6));
2570 }
2571
2572 #[test]
2576 fn test_bifoldable_right() {
2577 use crate::{
2578 brands::*,
2579 functions::*,
2580 };
2581
2582 assert_eq!(
2584 explicit::bi_fold_right::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
2585 (|e: i32, acc| acc - e, |s: i32, acc| acc + s),
2586 10,
2587 TryThunk::err(3),
2588 ),
2589 7
2590 );
2591
2592 assert_eq!(
2594 explicit::bi_fold_right::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
2595 (|e: i32, acc| acc - e, |s: i32, acc| acc + s),
2596 10,
2597 TryThunk::ok(5),
2598 ),
2599 15
2600 );
2601 }
2602
2603 #[test]
2607 fn test_bifoldable_left() {
2608 use crate::{
2609 brands::*,
2610 functions::*,
2611 };
2612
2613 assert_eq!(
2614 explicit::bi_fold_left::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
2615 (|acc, e: i32| acc - e, |acc, s: i32| acc + s),
2616 10,
2617 TryThunk::err(3),
2618 ),
2619 7
2620 );
2621
2622 assert_eq!(
2623 explicit::bi_fold_left::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
2624 (|acc, e: i32| acc - e, |acc, s: i32| acc + s),
2625 10,
2626 TryThunk::ok(5),
2627 ),
2628 15
2629 );
2630 }
2631
2632 #[test]
2636 fn test_bifoldable_map() {
2637 use crate::{
2638 brands::*,
2639 functions::*,
2640 };
2641
2642 assert_eq!(
2643 explicit::bi_fold_map::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
2644 (|e: i32| e.to_string(), |s: i32| s.to_string()),
2645 TryThunk::err(3),
2646 ),
2647 "3".to_string()
2648 );
2649
2650 assert_eq!(
2651 explicit::bi_fold_map::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
2652 (|e: i32| e.to_string(), |s: i32| s.to_string()),
2653 TryThunk::ok(5),
2654 ),
2655 "5".to_string()
2656 );
2657 }
2658
2659 #[test]
2663 fn test_monad_rec_ok_applied() {
2664 use {
2665 crate::{
2666 brands::*,
2667 functions::*,
2668 },
2669 core::ops::ControlFlow,
2670 };
2671
2672 let result = tail_rec_m::<TryThunkOkAppliedBrand<i32>, _, _>(
2673 |x| {
2674 pure::<TryThunkOkAppliedBrand<i32>, _>(
2675 if x < 100 { ControlFlow::Continue(x + 1) } else { ControlFlow::Break(x) },
2676 )
2677 },
2678 0,
2679 );
2680 assert_eq!(result.evaluate(), Err(100));
2681 }
2682
2683 #[test]
2687 fn test_monad_rec_ok_applied_short_circuit() {
2688 use {
2689 crate::{
2690 brands::*,
2691 functions::*,
2692 },
2693 core::ops::ControlFlow,
2694 };
2695
2696 let result = tail_rec_m::<TryThunkOkAppliedBrand<i32>, _, _>(
2697 |x: i32| {
2698 if x == 5 {
2699 TryThunk::ok(42)
2700 } else {
2701 pure::<TryThunkOkAppliedBrand<i32>, _>(ControlFlow::<i32, i32>::Continue(x + 1))
2702 }
2703 },
2704 0,
2705 );
2706 assert_eq!(result.evaluate(), Ok(42));
2707 }
2708
2709 #[test]
2713 fn test_catch_unwind() {
2714 let thunk = TryThunk::<i32, String>::catch_unwind(|| {
2715 if true {
2716 panic!("oops")
2717 }
2718 42
2719 });
2720 assert_eq!(thunk.evaluate(), Err("oops".to_string()));
2721 }
2722
2723 #[test]
2727 fn test_catch_unwind_success() {
2728 let thunk = TryThunk::<i32, String>::catch_unwind(|| 42);
2729 assert_eq!(thunk.evaluate(), Ok(42));
2730 }
2731
2732 #[test]
2734 fn test_try_thunk_with_ok_brand() {
2735 use crate::{
2736 brands::*,
2737 functions::*,
2738 };
2739
2740 let try_thunk: TryThunk<i32, i32> = TryThunk::err(10);
2742 let mapped = explicit::map::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(|x| x * 2, try_thunk);
2743 assert_eq!(mapped.evaluate(), Err(20));
2744
2745 let try_thunk: TryThunk<i32, i32> = pure::<TryThunkOkAppliedBrand<i32>, _>(42);
2747 assert_eq!(try_thunk.evaluate(), Err(42));
2748
2749 let try_thunk: TryThunk<i32, i32> = TryThunk::err(10);
2751 let bound = explicit::bind::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(try_thunk, |x| {
2752 pure::<TryThunkOkAppliedBrand<i32>, _>(x * 2)
2753 });
2754 assert_eq!(bound.evaluate(), Err(20));
2755
2756 let try_thunk: TryThunk<i32, i32> = TryThunk::err(10);
2758 let folded = explicit::fold_right::<RcFnBrand, TryThunkOkAppliedBrand<i32>, _, _, _, _>(
2759 |x, acc| x + acc,
2760 5,
2761 try_thunk,
2762 );
2763 assert_eq!(folded, 15);
2764 }
2765
2766 #[test]
2770 fn test_try_thunk_from_result_ok() {
2771 let try_thunk: TryThunk<i32, String> = TryThunk::from(Ok(42));
2772 assert_eq!(try_thunk.evaluate(), Ok(42));
2773 }
2774
2775 #[test]
2779 fn test_try_thunk_from_result_err() {
2780 let try_thunk: TryThunk<i32, String> = TryThunk::from(Err("error".to_string()));
2781 assert_eq!(try_thunk.evaluate(), Err("error".to_string()));
2782 }
2783
2784 #[test]
2788 fn test_try_thunk_from_try_send_thunk_ok() {
2789 use crate::types::TrySendThunk;
2790 let send: TrySendThunk<i32, ()> = TrySendThunk::pure(42);
2791 let thunk: TryThunk<i32, ()> = TryThunk::from(send);
2792 assert_eq!(thunk.evaluate(), Ok(42));
2793 }
2794
2795 #[test]
2799 fn test_try_thunk_from_try_send_thunk_err() {
2800 use crate::types::TrySendThunk;
2801 let send: TrySendThunk<i32, String> = TrySendThunk::err("fail".to_string());
2802 let thunk: TryThunk<i32, String> = TryThunk::from(send);
2803 assert_eq!(thunk.evaluate(), Err("fail".to_string()));
2804 }
2805
2806 #[quickcheck]
2812 fn functor_identity(x: i32) -> bool {
2813 use crate::{
2814 brands::*,
2815 functions::*,
2816 };
2817 let t: TryThunk<i32, i32> = TryThunk::ok(x);
2818 explicit::map::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(|a| a, t).evaluate() == Ok(x)
2819 }
2820
2821 #[quickcheck]
2823 fn functor_composition(x: i32) -> bool {
2824 use crate::{
2825 brands::*,
2826 functions::*,
2827 };
2828 let f = |a: i32| a.wrapping_add(1);
2829 let g = |a: i32| a.wrapping_mul(2);
2830 let lhs = explicit::map::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(
2831 move |a| f(g(a)),
2832 TryThunk::ok(x),
2833 )
2834 .evaluate();
2835 let rhs = explicit::map::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(
2836 f,
2837 explicit::map::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(g, TryThunk::ok(x)),
2838 )
2839 .evaluate();
2840 lhs == rhs
2841 }
2842
2843 #[quickcheck]
2847 fn monad_left_identity(a: i32) -> bool {
2848 use crate::{
2849 brands::*,
2850 functions::*,
2851 };
2852 let f = |x: i32| pure::<TryThunkErrAppliedBrand<i32>, _>(x.wrapping_mul(2));
2853 let lhs = explicit::bind::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(
2854 pure::<TryThunkErrAppliedBrand<i32>, _>(a),
2855 f,
2856 )
2857 .evaluate();
2858 let rhs = f(a).evaluate();
2859 lhs == rhs
2860 }
2861
2862 #[quickcheck]
2864 fn monad_right_identity(x: i32) -> bool {
2865 use crate::{
2866 brands::*,
2867 functions::*,
2868 };
2869 let lhs = explicit::bind::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(
2870 pure::<TryThunkErrAppliedBrand<i32>, _>(x),
2871 pure::<TryThunkErrAppliedBrand<i32>, _>,
2872 )
2873 .evaluate();
2874 lhs == Ok(x)
2875 }
2876
2877 #[quickcheck]
2879 fn monad_associativity(x: i32) -> bool {
2880 use crate::{
2881 brands::*,
2882 functions::*,
2883 };
2884 let f = |a: i32| pure::<TryThunkErrAppliedBrand<i32>, _>(a.wrapping_add(1));
2885 let g = |a: i32| pure::<TryThunkErrAppliedBrand<i32>, _>(a.wrapping_mul(3));
2886 let m: TryThunk<i32, i32> = TryThunk::ok(x);
2887 let m2: TryThunk<i32, i32> = TryThunk::ok(x);
2888 let lhs = explicit::bind::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(
2889 explicit::bind::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(m, f),
2890 g,
2891 )
2892 .evaluate();
2893 let rhs = explicit::bind::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(m2, move |a| {
2894 explicit::bind::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(f(a), g)
2895 })
2896 .evaluate();
2897 lhs == rhs
2898 }
2899
2900 #[quickcheck]
2902 fn error_short_circuit(e: i32) -> bool {
2903 let t: TryThunk<i32, i32> = TryThunk::err(e);
2904 t.bind(|x| TryThunk::ok(x.wrapping_add(1))).evaluate() == Err(e)
2905 }
2906
2907 #[test]
2911 fn test_lift2_ok_ok() {
2912 let t1: TryThunk<i32, String> = TryThunk::ok(10);
2913 let t2: TryThunk<i32, String> = TryThunk::ok(20);
2914 let t3 = t1.lift2(t2, |a, b| a + b);
2915 assert_eq!(t3.evaluate(), Ok(30));
2916 }
2917
2918 #[test]
2922 fn test_lift2_err_ok() {
2923 let t1: TryThunk<i32, String> = TryThunk::err("first".to_string());
2924 let t2: TryThunk<i32, String> = TryThunk::ok(20);
2925 let t3 = t1.lift2(t2, |a, b| a + b);
2926 assert_eq!(t3.evaluate(), Err("first".to_string()));
2927 }
2928
2929 #[test]
2933 fn test_lift2_ok_err() {
2934 let t1: TryThunk<i32, String> = TryThunk::ok(10);
2935 let t2: TryThunk<i32, String> = TryThunk::err("second".to_string());
2936 let t3 = t1.lift2(t2, |a, b| a + b);
2937 assert_eq!(t3.evaluate(), Err("second".to_string()));
2938 }
2939
2940 #[test]
2944 fn test_then_ok_ok() {
2945 let t1: TryThunk<i32, String> = TryThunk::ok(10);
2946 let t2: TryThunk<i32, String> = TryThunk::ok(20);
2947 let t3 = t1.then(t2);
2948 assert_eq!(t3.evaluate(), Ok(20));
2949 }
2950
2951 #[test]
2955 fn test_then_err_ok() {
2956 let t1: TryThunk<i32, String> = TryThunk::err("first".to_string());
2957 let t2: TryThunk<i32, String> = TryThunk::ok(20);
2958 let t3 = t1.then(t2);
2959 assert_eq!(t3.evaluate(), Err("first".to_string()));
2960 }
2961
2962 #[test]
2966 fn test_then_ok_err() {
2967 let t1: TryThunk<i32, String> = TryThunk::ok(10);
2968 let t2: TryThunk<i32, String> = TryThunk::err("second".to_string());
2969 let t3 = t1.then(t2);
2970 assert_eq!(t3.evaluate(), Err("second".to_string()));
2971 }
2972
2973 #[test]
2977 fn test_into_rc_try_lazy() {
2978 let thunk: TryThunk<i32, ()> = TryThunk::ok(42);
2979 let lazy = thunk.into_rc_try_lazy();
2980 assert_eq!(lazy.evaluate(), Ok(&42));
2981 }
2982
2983 #[test]
2987 fn test_into_rc_try_lazy_caching() {
2988 use std::{
2989 cell::RefCell,
2990 rc::Rc,
2991 };
2992
2993 let counter = Rc::new(RefCell::new(0));
2994 let counter_clone = counter.clone();
2995 let thunk: TryThunk<i32, ()> = TryThunk::new(move || {
2996 *counter_clone.borrow_mut() += 1;
2997 Ok(42)
2998 });
2999 let lazy = thunk.into_rc_try_lazy();
3000
3001 assert_eq!(*counter.borrow(), 0);
3002 assert_eq!(lazy.evaluate(), Ok(&42));
3003 assert_eq!(*counter.borrow(), 1);
3004 assert_eq!(lazy.evaluate(), Ok(&42));
3005 assert_eq!(*counter.borrow(), 1);
3006 }
3007
3008 #[test]
3012 fn test_into_arc_try_lazy() {
3013 let thunk: TryThunk<i32, ()> = TryThunk::ok(42);
3014 let lazy = thunk.into_arc_try_lazy();
3015 assert_eq!(lazy.evaluate(), Ok(&42));
3016 }
3017
3018 #[test]
3022 fn test_into_arc_try_lazy_send_sync() {
3023 use std::thread;
3024
3025 let thunk: TryThunk<i32, String> = TryThunk::ok(42);
3026 let lazy = thunk.into_arc_try_lazy();
3027 let lazy_clone = lazy.clone();
3028
3029 let handle = thread::spawn(move || {
3030 assert_eq!(lazy_clone.evaluate(), Ok(&42));
3031 });
3032
3033 assert_eq!(lazy.evaluate(), Ok(&42));
3034 handle.join().unwrap();
3035 }
3036
3037 #[test]
3041 fn test_catch_unwind_with_panic() {
3042 let thunk = TryThunk::<i32, i32>::catch_unwind_with(
3043 || {
3044 if true {
3045 panic!("oops")
3046 }
3047 42
3048 },
3049 |_payload| -1,
3050 );
3051 assert_eq!(thunk.evaluate(), Err(-1));
3052 }
3053
3054 #[test]
3058 fn test_catch_unwind_with_success() {
3059 let thunk = TryThunk::<i32, i32>::catch_unwind_with(|| 42, |_payload| -1);
3060 assert_eq!(thunk.evaluate(), Ok(42));
3061 }
3062
3063 #[quickcheck]
3067 fn bifunctor_identity_ok(x: i32) -> bool {
3068 use crate::{
3069 brands::*,
3070 classes::bifunctor::*,
3071 };
3072 let t: TryThunk<i32, i32> = TryThunk::ok(x);
3073 bimap::<TryThunkBrand, _, _, _, _>(|e| e, |a| a, t).evaluate() == Ok(x)
3074 }
3075
3076 #[quickcheck]
3078 fn bifunctor_identity_err(e: i32) -> bool {
3079 use crate::{
3080 brands::*,
3081 classes::bifunctor::*,
3082 };
3083 let t: TryThunk<i32, i32> = TryThunk::err(e);
3084 bimap::<TryThunkBrand, _, _, _, _>(|e| e, |a| a, t).evaluate() == Err(e)
3085 }
3086
3087 #[quickcheck]
3089 fn bifunctor_composition_ok(x: i32) -> bool {
3090 use crate::{
3091 brands::*,
3092 classes::bifunctor::*,
3093 };
3094 let f1 = |a: i32| a.wrapping_add(1);
3095 let f2 = |a: i32| a.wrapping_mul(2);
3096 let g1 = |a: i32| a.wrapping_add(10);
3097 let g2 = |a: i32| a.wrapping_mul(3);
3098
3099 let lhs = bimap::<TryThunkBrand, _, _, _, _>(
3100 move |e| f1(f2(e)),
3101 move |a| g1(g2(a)),
3102 TryThunk::ok(x),
3103 )
3104 .evaluate();
3105 let rhs = bimap::<TryThunkBrand, _, _, _, _>(
3106 f1,
3107 g1,
3108 bimap::<TryThunkBrand, _, _, _, _>(f2, g2, TryThunk::ok(x)),
3109 )
3110 .evaluate();
3111 lhs == rhs
3112 }
3113
3114 #[quickcheck]
3116 fn bifunctor_composition_err(e: i32) -> bool {
3117 use crate::{
3118 brands::*,
3119 classes::bifunctor::*,
3120 };
3121 let f1 = |a: i32| a.wrapping_add(1);
3122 let f2 = |a: i32| a.wrapping_mul(2);
3123 let g1 = |a: i32| a.wrapping_add(10);
3124 let g2 = |a: i32| a.wrapping_mul(3);
3125
3126 let lhs = bimap::<TryThunkBrand, _, _, _, _>(
3127 move |e| f1(f2(e)),
3128 move |a| g1(g2(a)),
3129 TryThunk::err(e),
3130 )
3131 .evaluate();
3132 let rhs = bimap::<TryThunkBrand, _, _, _, _>(
3133 f1,
3134 g1,
3135 bimap::<TryThunkBrand, _, _, _, _>(f2, g2, TryThunk::err(e)),
3136 )
3137 .evaluate();
3138 lhs == rhs
3139 }
3140
3141 #[quickcheck]
3145 fn error_monad_left_identity(a: i32) -> bool {
3146 use crate::{
3147 brands::*,
3148 functions::*,
3149 };
3150 let f = |x: i32| pure::<TryThunkOkAppliedBrand<i32>, _>(x.wrapping_mul(2));
3151 let lhs = explicit::bind::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(
3152 pure::<TryThunkOkAppliedBrand<i32>, _>(a),
3153 f,
3154 )
3155 .evaluate();
3156 let rhs = f(a).evaluate();
3157 lhs == rhs
3158 }
3159
3160 #[quickcheck]
3162 fn error_monad_right_identity(x: i32) -> bool {
3163 use crate::{
3164 brands::*,
3165 functions::*,
3166 };
3167 let lhs = explicit::bind::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(
3168 pure::<TryThunkOkAppliedBrand<i32>, _>(x),
3169 pure::<TryThunkOkAppliedBrand<i32>, _>,
3170 )
3171 .evaluate();
3172 lhs == Err(x)
3173 }
3174
3175 #[quickcheck]
3177 fn error_monad_associativity(x: i32) -> bool {
3178 use crate::{
3179 brands::*,
3180 functions::*,
3181 };
3182 let f = |a: i32| pure::<TryThunkOkAppliedBrand<i32>, _>(a.wrapping_add(1));
3183 let g = |a: i32| pure::<TryThunkOkAppliedBrand<i32>, _>(a.wrapping_mul(3));
3184 let m: TryThunk<i32, i32> = TryThunk::err(x);
3185 let m2: TryThunk<i32, i32> = TryThunk::err(x);
3186 let lhs = explicit::bind::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(
3187 explicit::bind::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(m, f),
3188 g,
3189 )
3190 .evaluate();
3191 let rhs = explicit::bind::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(m2, move |a| {
3192 explicit::bind::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(f(a), g)
3193 })
3194 .evaluate();
3195 lhs == rhs
3196 }
3197
3198 #[quickcheck]
3202 fn try_thunk_semigroup_associativity(
3203 a: String,
3204 b: String,
3205 c: String,
3206 ) -> bool {
3207 use crate::classes::semigroup::append;
3208
3209 let ta: TryThunk<String, ()> = TryThunk::ok(a.clone());
3210 let tb: TryThunk<String, ()> = TryThunk::ok(b.clone());
3211 let tc: TryThunk<String, ()> = TryThunk::ok(c.clone());
3212 let ta2: TryThunk<String, ()> = TryThunk::ok(a);
3213 let tb2: TryThunk<String, ()> = TryThunk::ok(b);
3214 let tc2: TryThunk<String, ()> = TryThunk::ok(c);
3215 let lhs = append(append(ta, tb), tc).evaluate();
3216 let rhs = append(ta2, append(tb2, tc2)).evaluate();
3217 lhs == rhs
3218 }
3219
3220 #[quickcheck]
3222 fn try_thunk_monoid_left_identity(x: String) -> bool {
3223 use crate::classes::{
3224 monoid::empty,
3225 semigroup::append,
3226 };
3227
3228 let a: TryThunk<String, ()> = TryThunk::ok(x.clone());
3229 let lhs: TryThunk<String, ()> = append(empty(), a);
3230 lhs.evaluate() == Ok(x)
3231 }
3232
3233 #[quickcheck]
3235 fn try_thunk_monoid_right_identity(x: String) -> bool {
3236 use crate::classes::{
3237 monoid::empty,
3238 semigroup::append,
3239 };
3240
3241 let a: TryThunk<String, ()> = TryThunk::ok(x.clone());
3242 let rhs: TryThunk<String, ()> = append(a, empty());
3243 rhs.evaluate() == Ok(x)
3244 }
3245
3246 #[test]
3251 fn test_into_arc_try_lazy_thread_safety() {
3252 use std::{
3253 sync::{
3254 Arc,
3255 atomic::{
3256 AtomicUsize,
3257 Ordering,
3258 },
3259 },
3260 thread,
3261 };
3262
3263 let counter = Arc::new(AtomicUsize::new(0));
3264 let counter_clone = Arc::clone(&counter);
3265 let thunk: TryThunk<i32, String> = TryThunk::new(move || {
3266 counter_clone.fetch_add(1, Ordering::SeqCst);
3267 Ok(42)
3268 });
3269 let lazy = thunk.into_arc_try_lazy();
3270
3271 let handles: Vec<_> = (0 .. 8)
3272 .map(|_| {
3273 let lazy_clone = lazy.clone();
3274 thread::spawn(move || {
3275 assert_eq!(lazy_clone.evaluate(), Ok(&42));
3276 })
3277 })
3278 .collect();
3279
3280 assert_eq!(lazy.evaluate(), Ok(&42));
3281 for h in handles {
3282 h.join().unwrap();
3283 }
3284
3285 assert_eq!(counter.load(Ordering::SeqCst), 1);
3287 }
3288
3289 #[test]
3293 fn test_semigroup_append_first_err_short_circuits() {
3294 use {
3295 crate::classes::semigroup::append,
3296 std::cell::Cell,
3297 };
3298
3299 let counter = Cell::new(0u32);
3300 let t1: TryThunk<String, &str> = TryThunk::err("first failed");
3301 let t2: TryThunk<String, &str> = TryThunk::new(|| {
3302 counter.set(counter.get() + 1);
3303 Ok("second".to_string())
3304 });
3305 let result = append(t1, t2);
3306 assert_eq!(result.evaluate(), Err("first failed"));
3307 assert_eq!(counter.get(), 0, "second operand should not have been evaluated");
3308 }
3309
3310 #[test]
3315 fn test_semigroup_append_second_err_propagates() {
3316 use crate::classes::semigroup::append;
3317
3318 let t1: TryThunk<String, &str> = TryThunk::pure("hello".to_string());
3319 let t2: TryThunk<String, &str> = TryThunk::err("second failed");
3320 let result = append(t1, t2);
3321 assert_eq!(result.evaluate(), Err("second failed"));
3322 }
3323
3324 #[test]
3328 fn test_tail_rec_m_success() {
3329 use core::ops::ControlFlow;
3330 let result: TryThunk<i32, ()> = TryThunk::tail_rec_m(
3331 |x| {
3332 TryThunk::ok(
3333 if x < 1000 { ControlFlow::Continue(x + 1) } else { ControlFlow::Break(x) },
3334 )
3335 },
3336 0,
3337 );
3338 assert_eq!(result.evaluate(), Ok(1000));
3339 }
3340
3341 #[test]
3346 fn test_tail_rec_m_early_error() {
3347 use core::ops::ControlFlow;
3348 let result: TryThunk<i32, &str> = TryThunk::tail_rec_m(
3349 |x| {
3350 if x == 5 {
3351 TryThunk::err("stopped at 5")
3352 } else {
3353 TryThunk::ok(ControlFlow::Continue(x + 1))
3354 }
3355 },
3356 0,
3357 );
3358 assert_eq!(result.evaluate(), Err("stopped at 5"));
3359 }
3360
3361 #[test]
3365 fn test_tail_rec_m_stack_safety() {
3366 use core::ops::ControlFlow;
3367 let iterations: i64 = 100_000;
3368 let result: TryThunk<i64, ()> = TryThunk::tail_rec_m(
3369 |acc| {
3370 TryThunk::ok(
3371 if acc < iterations {
3372 ControlFlow::Continue(acc + 1)
3373 } else {
3374 ControlFlow::Break(acc)
3375 },
3376 )
3377 },
3378 0i64,
3379 );
3380 assert_eq!(result.evaluate(), Ok(iterations));
3381 }
3382
3383 #[test]
3389 fn test_arc_tail_rec_m_success() {
3390 use {
3391 core::ops::ControlFlow,
3392 std::sync::{
3393 Arc,
3394 atomic::{
3395 AtomicUsize,
3396 Ordering,
3397 },
3398 },
3399 };
3400 let counter = Arc::new(AtomicUsize::new(0));
3401 let counter_clone = Arc::clone(&counter);
3402 let result: TryThunk<i32, ()> = TryThunk::arc_tail_rec_m(
3403 move |x| {
3404 counter_clone.fetch_add(1, Ordering::SeqCst);
3405 TryThunk::ok(
3406 if x < 100 { ControlFlow::Continue(x + 1) } else { ControlFlow::Break(x) },
3407 )
3408 },
3409 0,
3410 );
3411 assert_eq!(result.evaluate(), Ok(100));
3412 assert_eq!(counter.load(Ordering::SeqCst), 101);
3413 }
3414
3415 #[test]
3419 fn test_arc_tail_rec_m_early_error() {
3420 use core::ops::ControlFlow;
3421 let result: TryThunk<i32, &str> = TryThunk::arc_tail_rec_m(
3422 |x| {
3423 if x == 5 {
3424 TryThunk::err("stopped at 5")
3425 } else {
3426 TryThunk::ok(ControlFlow::Continue(x + 1))
3427 }
3428 },
3429 0,
3430 );
3431 assert_eq!(result.evaluate(), Err("stopped at 5"));
3432 }
3433
3434 #[test]
3438 fn test_arc_tail_rec_m_stack_safety() {
3439 use core::ops::ControlFlow;
3440 let iterations: i64 = 100_000;
3441 let result: TryThunk<i64, ()> = TryThunk::arc_tail_rec_m(
3442 |acc| {
3443 TryThunk::ok(
3444 if acc < iterations {
3445 ControlFlow::Continue(acc + 1)
3446 } else {
3447 ControlFlow::Break(acc)
3448 },
3449 )
3450 },
3451 0i64,
3452 );
3453 assert_eq!(result.evaluate(), Ok(iterations));
3454 }
3455}