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 CloneableFn,
21 Deferrable,
22 Foldable,
23 FoldableWithIndex,
24 Functor,
25 FunctorWithIndex,
26 LazyConfig,
27 Lift,
28 MonadRec,
29 Monoid,
30 Pointed,
31 Semiapplicative,
32 Semigroup,
33 Semimonad,
34 TryLazyConfig,
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::fmt,
52 };
53
54 #[document_type_parameters(
60 "The lifetime of the computation.",
61 "The type of the value produced by the computation on success.",
62 "The type of the error produced by the computation on failure."
63 )]
64 pub struct TryThunk<'a, A, E>(
102 Thunk<'a, Result<A, E>>,
104 );
105
106 #[document_type_parameters(
107 "The lifetime of the computation.",
108 "The type of the success value.",
109 "The type of the error value."
110 )]
111 #[document_parameters("The `TryThunk` instance.")]
112 impl<'a, A: 'a, E: 'a> TryThunk<'a, A, E> {
113 #[document_signature]
115 #[document_parameters("The thunk to wrap.")]
117 #[document_returns("A new `TryThunk` instance.")]
119 #[document_examples]
121 #[inline]
129 pub fn new(f: impl FnOnce() -> Result<A, E> + 'a) -> Self {
130 TryThunk(Thunk::new(f))
131 }
132
133 #[document_signature]
135 #[document_parameters("The value to wrap.")]
137 #[document_returns("A new `TryThunk` instance containing the value.")]
139 #[document_examples]
141 #[inline]
149 pub fn pure(a: A) -> Self {
150 TryThunk(Thunk::pure(Ok(a)))
151 }
152
153 #[document_signature]
155 #[document_parameters("The thunk that returns a `TryThunk`.")]
157 #[document_returns("A new `TryThunk` instance.")]
159 #[document_examples]
161 #[inline]
169 pub fn defer(f: impl FnOnce() -> TryThunk<'a, A, E> + 'a) -> Self {
170 TryThunk(Thunk::defer(move || f().0))
171 }
172
173 #[document_signature]
180 #[document_parameters("The value to wrap.")]
182 #[document_returns("A new `TryThunk` instance containing the value.")]
184 #[document_examples]
186 #[inline]
194 pub fn ok(a: A) -> Self {
195 TryThunk(Thunk::pure(Ok(a)))
196 }
197
198 #[document_signature]
200 #[document_parameters("The error to wrap.")]
202 #[document_returns("A new `TryThunk` instance containing the error.")]
204 #[document_examples]
206 #[inline]
214 pub fn err(e: E) -> Self {
215 TryThunk(Thunk::pure(Err(e)))
216 }
217
218 #[document_signature]
220 #[document_type_parameters("The type of the result of the new computation.")]
222 #[document_parameters("The function to apply to the result of the computation.")]
224 #[document_returns("A new `TryThunk` instance representing the chained computation.")]
226 #[document_examples]
228 #[inline]
236 pub fn bind<B: 'a>(
237 self,
238 f: impl FnOnce(A) -> TryThunk<'a, B, E> + 'a,
239 ) -> TryThunk<'a, B, E> {
240 TryThunk(self.0.bind(|result| match result {
241 Ok(a) => f(a).0,
242 Err(e) => Thunk::pure(Err(e)),
243 }))
244 }
245
246 #[document_signature]
248 #[document_type_parameters("The type of the result of the transformation.")]
250 #[document_parameters("The function to apply to the result of the computation.")]
252 #[document_returns("A new `TryThunk` instance with the transformed result.")]
254 #[document_examples]
256 #[inline]
264 pub fn map<B: 'a>(
265 self,
266 func: impl FnOnce(A) -> B + 'a,
267 ) -> TryThunk<'a, B, E> {
268 TryThunk(self.0.map(|result| result.map(func)))
269 }
270
271 #[document_signature]
273 #[document_type_parameters("The type of the new error.")]
275 #[document_parameters("The function to apply to the error.")]
277 #[document_returns("A new `TryThunk` instance with the transformed error.")]
279 #[document_examples]
281 #[inline]
289 pub fn map_err<E2: 'a>(
290 self,
291 f: impl FnOnce(E) -> E2 + 'a,
292 ) -> TryThunk<'a, A, E2> {
293 TryThunk(self.0.map(|result| result.map_err(f)))
294 }
295
296 #[document_signature]
298 #[document_parameters("The function to apply to the error value.")]
300 #[document_returns("A new `TryThunk` that attempts to recover from failure.")]
302 #[document_examples]
304 #[inline]
312 pub fn catch(
313 self,
314 f: impl FnOnce(E) -> TryThunk<'a, A, E> + 'a,
315 ) -> Self {
316 TryThunk(self.0.bind(|result| match result {
317 Ok(a) => Thunk::pure(Ok(a)),
318 Err(e) => f(e).0,
319 }))
320 }
321
322 #[document_signature]
329 #[document_type_parameters("The error type produced by the recovery computation.")]
331 #[document_parameters("The monadic recovery function applied to the error value.")]
333 #[document_returns(
335 "A new `TryThunk` that either passes through the success value or uses the result of the recovery computation."
336 )]
337 #[document_examples]
339 #[inline]
351 pub fn catch_with<E2: 'a>(
352 self,
353 f: impl FnOnce(E) -> TryThunk<'a, A, E2> + 'a,
354 ) -> TryThunk<'a, A, E2> {
355 TryThunk(Thunk::new(move || match self.evaluate() {
356 Ok(a) => Ok(a),
357 Err(e) => f(e).evaluate(),
358 }))
359 }
360
361 #[document_signature]
363 #[document_type_parameters(
365 "The type of the new success value.",
366 "The type of the new error value."
367 )]
368 #[document_parameters(
370 "The function to apply to the success value.",
371 "The function to apply to the error value."
372 )]
373 #[document_returns("A new `TryThunk` with both sides transformed.")]
375 #[document_examples]
377 pub fn bimap<B: 'a, E2: 'a>(
388 self,
389 f: impl FnOnce(A) -> B + 'a,
390 g: impl FnOnce(E) -> E2 + 'a,
391 ) -> TryThunk<'a, B, E2> {
392 TryThunk(self.0.map(|result| match result {
393 Ok(a) => Ok(f(a)),
394 Err(e) => Err(g(e)),
395 }))
396 }
397
398 #[document_signature]
400 #[document_returns("The result of the computation.")]
402 #[document_examples]
404 #[inline]
412 pub fn evaluate(self) -> Result<A, E> {
413 self.0.evaluate()
414 }
415
416 #[document_signature]
420 #[document_type_parameters(
422 "The type of the second computation's success value.",
423 "The type of the combined result."
424 )]
425 #[document_parameters("The second computation.", "The function to combine the results.")]
427 #[document_returns("A new `TryThunk` producing the combined result.")]
429 #[document_examples]
431 #[inline]
446 pub fn lift2<B: 'a, C: 'a>(
447 self,
448 other: TryThunk<'a, B, E>,
449 f: impl FnOnce(A, B) -> C + 'a,
450 ) -> TryThunk<'a, C, E> {
451 self.bind(move |a| other.map(move |b| f(a, b)))
452 }
453
454 #[document_signature]
458 #[document_type_parameters("The type of the second computation's success value.")]
460 #[document_parameters("The second computation.")]
462 #[document_returns(
464 "A new `TryThunk` that runs both computations and returns the result of the second."
465 )]
466 #[document_examples]
468 #[inline]
483 pub fn then<B: 'a>(
484 self,
485 other: TryThunk<'a, B, E>,
486 ) -> TryThunk<'a, B, E> {
487 self.bind(move |_| other)
488 }
489
490 #[document_signature]
495 #[document_returns("A memoized `RcTryLazy` wrapping this computation.")]
497 #[document_examples]
499 #[inline]
508 pub fn into_rc_try_lazy(self) -> RcTryLazy<'a, A, E> {
509 RcTryLazy::from(self)
510 }
511
512 #[document_signature]
517 #[document_returns("A thread-safe memoized `ArcTryLazy` wrapping this computation.")]
519 #[document_examples]
521 #[inline]
530 pub fn into_arc_try_lazy(self) -> ArcTryLazy<'a, A, E>
531 where
532 A: Send + Sync + 'a,
533 E: Send + Sync + 'a, {
534 let result = self.evaluate();
535 ArcTryLazy::new(move || result)
536 }
537 }
538
539 #[document_type_parameters(
540 "The lifetime of the computation.",
541 "The type of the computed value.",
542 "The type of the error value."
543 )]
544 #[document_parameters("The `TryThunk` to operate on.")]
545 impl<'a, A: 'a, E: 'a> TryThunk<'a, A, E> {
546 #[document_signature]
553 #[document_parameters(
555 "The closure that might panic.",
556 "The function that converts a panic payload into the error type."
557 )]
558 #[document_returns(
560 "A new `TryThunk` instance where panics are converted to `Err(E)` via the handler."
561 )]
562 #[document_examples]
564 pub fn catch_unwind_with(
580 f: impl FnOnce() -> A + std::panic::UnwindSafe + 'a,
581 handler: impl FnOnce(Box<dyn std::any::Any + Send>) -> E + 'a,
582 ) -> Self {
583 TryThunk::new(move || std::panic::catch_unwind(f).map_err(handler))
584 }
585
586 #[document_signature]
588 #[document_returns("The underlying `Thunk` that produces a `Result`.")]
590 #[document_examples]
592 pub fn into_inner(self) -> Thunk<'a, Result<A, E>> {
601 self.0
602 }
603 }
604
605 #[document_type_parameters(
606 "The lifetime of the computation.",
607 "The type of the computed value."
608 )]
609 impl<'a, A: 'a> TryThunk<'a, A, String> {
610 #[document_signature]
619 #[document_parameters("The closure that might panic.")]
621 #[document_returns(
623 "A new `TryThunk` instance where panics are converted to `Err(String)`."
624 )]
625 #[document_examples]
627 pub fn catch_unwind(f: impl FnOnce() -> A + std::panic::UnwindSafe + 'a) -> Self {
640 Self::catch_unwind_with(f, crate::utils::panic_payload_to_string)
641 }
642 }
643
644 #[document_type_parameters(
645 "The lifetime of the computation.",
646 "The type of the success value.",
647 "The type of the error value.",
648 "The memoization configuration."
649 )]
650 impl<'a, A, E, Config> From<Lazy<'a, A, Config>> for TryThunk<'a, A, E>
651 where
652 A: Clone + 'a,
653 E: 'a,
654 Config: LazyConfig,
655 {
656 #[document_signature]
657 #[document_parameters("The lazy value to convert.")]
658 #[document_returns("A new `TryThunk` instance that wraps the lazy value.")]
659 #[document_examples]
660 fn from(memo: Lazy<'a, A, Config>) -> Self {
668 TryThunk::new(move || Ok(memo.evaluate().clone()))
669 }
670 }
671
672 #[document_type_parameters(
673 "The lifetime of the computation.",
674 "The type of the success value.",
675 "The type of the error value.",
676 "The memoization configuration."
677 )]
678 impl<'a, A, E, Config> From<TryLazy<'a, A, E, Config>> for TryThunk<'a, A, E>
679 where
680 A: Clone + 'a,
681 E: Clone + 'a,
682 Config: TryLazyConfig,
683 {
684 #[document_signature]
689 #[document_parameters("The fallible lazy value to convert.")]
690 #[document_returns("A new `TryThunk` instance that wraps the fallible lazy value.")]
691 #[document_examples]
692 fn from(memo: TryLazy<'a, A, E, Config>) -> Self {
700 TryThunk::new(move || memo.evaluate().cloned().map_err(Clone::clone))
701 }
702 }
703
704 #[document_type_parameters(
705 "The lifetime of the computation.",
706 "The type of the success value.",
707 "The type of the error value."
708 )]
709 impl<'a, A: 'a, E: 'a> From<Thunk<'a, A>> for TryThunk<'a, A, E> {
710 #[document_signature]
711 #[document_parameters("The thunk to convert.")]
712 #[document_returns("A new `TryThunk` instance that wraps the thunk.")]
713 #[document_examples]
714 fn from(eval: Thunk<'a, A>) -> Self {
722 TryThunk(eval.map(Ok))
723 }
724 }
725
726 #[document_type_parameters(
727 "The lifetime of the computation.",
728 "The type of the success value.",
729 "The type of the error value."
730 )]
731 impl<'a, A: 'a, E: 'a> From<Result<A, E>> for TryThunk<'a, A, E> {
732 #[document_signature]
733 #[document_parameters("The result to convert.")]
734 #[document_returns("A new `TryThunk` instance that produces the result.")]
735 #[document_examples]
736 fn from(result: Result<A, E>) -> Self {
746 TryThunk(Thunk::pure(result))
747 }
748 }
749
750 #[document_type_parameters("The type of the success value.", "The type of the error value.")]
751 impl<A: 'static, E: 'static> From<TryTrampoline<A, E>> for TryThunk<'static, A, E> {
752 #[document_signature]
756 #[document_parameters("The fallible trampoline to convert.")]
757 #[document_returns("A new `TryThunk` instance that evaluates the trampoline.")]
758 #[document_examples]
759 fn from(tramp: TryTrampoline<A, E>) -> Self {
768 TryThunk::new(move || tramp.evaluate())
769 }
770 }
771
772 #[document_type_parameters(
773 "The lifetime of the computation.",
774 "The type of the success value.",
775 "The type of the error value."
776 )]
777 impl<'a, A: 'a, E: 'a> From<TrySendThunk<'a, A, E>> for TryThunk<'a, A, E> {
778 #[document_signature]
785 #[document_parameters("The send try-thunk to convert.")]
786 #[document_returns("A `TryThunk` wrapping the same deferred computation.")]
787 #[document_examples]
788 fn from(send_thunk: TrySendThunk<'a, A, E>) -> Self {
796 TryThunk(Thunk::from(send_thunk.into_inner()))
797 }
798 }
799
800 #[document_type_parameters(
801 "The lifetime of the computation.",
802 "The type of the success value.",
803 "The type of the error value."
804 )]
805 impl<'a, A, E> Deferrable<'a> for TryThunk<'a, A, E>
806 where
807 A: 'a,
808 E: 'a,
809 {
810 #[document_signature]
812 #[document_parameters("A thunk that produces the try thunk.")]
814 #[document_returns("The deferred try thunk.")]
816 #[document_examples]
818 fn defer(f: impl FnOnce() -> Self + 'a) -> Self
831 where
832 Self: Sized, {
833 TryThunk::defer(f)
834 }
835 }
836
837 impl_kind! {
838 impl<E: 'static> for TryThunkErrAppliedBrand<E> {
839 #[document_default]
840 type Of<'a, A: 'a>: 'a = TryThunk<'a, A, E>;
841 }
842 }
843
844 #[document_type_parameters("The error type.")]
845 impl<E: 'static> Functor for TryThunkErrAppliedBrand<E> {
846 #[document_signature]
848 #[document_type_parameters(
850 "The lifetime of the computation.",
851 "The type of the value inside the `TryThunk`.",
852 "The type of the result of the transformation."
853 )]
854 #[document_parameters(
856 "The function to apply to the result of the computation.",
857 "The `TryThunk` instance."
858 )]
859 #[document_returns("A new `TryThunk` instance with the transformed result.")]
861 #[document_examples]
862 fn map<'a, A: 'a, B: 'a>(
875 func: impl Fn(A) -> B + 'a,
876 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
877 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
878 fa.map(func)
879 }
880 }
881
882 #[document_type_parameters("The error type.")]
883 impl<E: 'static> Pointed for TryThunkErrAppliedBrand<E> {
884 #[document_signature]
886 #[document_type_parameters(
888 "The lifetime of the computation.",
889 "The type of the value to wrap."
890 )]
891 #[document_parameters("The value to wrap.")]
893 #[document_returns("A new `TryThunk` instance containing the value.")]
895 #[document_examples]
897 fn pure<'a, A: 'a>(a: A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>) {
909 TryThunk::ok(a)
910 }
911 }
912
913 #[document_type_parameters("The error type.")]
914 impl<E: 'static> Lift for TryThunkErrAppliedBrand<E> {
915 #[document_signature]
917 #[document_type_parameters(
919 "The lifetime of the computation.",
920 "The type of the first value.",
921 "The type of the second value.",
922 "The type of the result."
923 )]
924 #[document_parameters(
926 "The binary function to apply.",
927 "The first `TryThunk`.",
928 "The second `TryThunk`."
929 )]
930 #[document_returns(
932 "A new `TryThunk` instance containing the result of applying the function."
933 )]
934 #[document_examples]
935 fn lift2<'a, A, B, C>(
949 func: impl Fn(A, B) -> C + 'a,
950 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
951 fb: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
952 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>)
953 where
954 A: Clone + 'a,
955 B: Clone + 'a,
956 C: 'a, {
957 fa.bind(move |a| fb.map(move |b| func(a, b)))
958 }
959 }
960
961 #[document_type_parameters("The error type.")]
962 impl<E: 'static> ApplyFirst for TryThunkErrAppliedBrand<E> {}
963
964 #[document_type_parameters("The error type.")]
965 impl<E: 'static> ApplySecond for TryThunkErrAppliedBrand<E> {}
966
967 #[document_type_parameters("The error type.")]
968 impl<E: 'static> Semiapplicative for TryThunkErrAppliedBrand<E> {
969 #[document_signature]
971 #[document_type_parameters(
973 "The lifetime of the computation.",
974 "The brand of the cloneable function wrapper.",
975 "The type of the input.",
976 "The type of the result."
977 )]
978 #[document_parameters(
980 "The `TryThunk` containing the function.",
981 "The `TryThunk` containing the value."
982 )]
983 #[document_returns(
985 "A new `TryThunk` instance containing the result of applying the function."
986 )]
987 #[document_examples]
988 fn apply<'a, FnBrand: 'a + CloneableFn, A: 'a + Clone, B: 'a>(
1003 ff: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as CloneableFn>::Of<'a, A, B>>),
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 ff.bind(move |f| {
1007 fa.map(
1008 #[allow(clippy::redundant_closure)] move |a| f(a),
1010 )
1011 })
1012 }
1013 }
1014
1015 #[document_type_parameters("The error type.")]
1016 impl<E: 'static> Semimonad for TryThunkErrAppliedBrand<E> {
1017 #[document_signature]
1019 #[document_type_parameters(
1021 "The lifetime of the computation.",
1022 "The type of the result of the first computation.",
1023 "The type of the result of the new computation."
1024 )]
1025 #[document_parameters(
1027 "The first `TryThunk`.",
1028 "The function to apply to the result of the computation."
1029 )]
1030 #[document_returns("A new `TryThunk` instance representing the chained computation.")]
1032 #[document_examples]
1033 fn bind<'a, A: 'a, B: 'a>(
1048 ma: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1049 func: impl Fn(A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) + 'a,
1050 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
1051 ma.bind(func)
1052 }
1053 }
1054
1055 #[document_type_parameters("The error type.")]
1056 impl<E: 'static> MonadRec for TryThunkErrAppliedBrand<E> {
1057 #[document_signature]
1059 #[document_type_parameters(
1061 "The lifetime of the computation.",
1062 "The type of the initial value and loop state.",
1063 "The type of the result."
1064 )]
1065 #[document_parameters("The step function.", "The initial value.")]
1067 #[document_returns("The result of the computation.")]
1069 #[document_examples]
1071 fn tail_rec_m<'a, A: 'a, B: 'a>(
1094 f: impl Fn(
1095 A,
1096 )
1097 -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, ControlFlow<B, A>>)
1098 + 'a,
1099 a: A,
1100 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
1101 TryThunk::new(move || {
1102 let mut current = a;
1103 loop {
1104 match f(current).evaluate() {
1105 Ok(ControlFlow::Continue(next)) => current = next,
1106 Ok(ControlFlow::Break(res)) => break Ok(res),
1107 Err(e) => break Err(e),
1108 }
1109 }
1110 })
1111 }
1112 }
1113
1114 #[document_type_parameters("The error type.")]
1115 impl<E: 'static> Foldable for TryThunkErrAppliedBrand<E> {
1116 #[document_signature]
1118 #[document_type_parameters(
1120 "The lifetime of the computation.",
1121 "The brand of the cloneable function to use.",
1122 "The type of the elements in the structure.",
1123 "The type of the accumulator."
1124 )]
1125 #[document_parameters(
1127 "The function to apply to each element and the accumulator.",
1128 "The initial value of the accumulator.",
1129 "The `TryThunk` to fold."
1130 )]
1131 #[document_returns("The final accumulator value.")]
1133 #[document_examples]
1134 fn fold_right<'a, FnBrand, A: 'a + Clone, B: 'a>(
1148 func: impl Fn(A, B) -> B + 'a,
1149 initial: B,
1150 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1151 ) -> B
1152 where
1153 FnBrand: CloneableFn + 'a, {
1154 match fa.evaluate() {
1155 Ok(a) => func(a, initial),
1156 Err(_) => initial,
1157 }
1158 }
1159
1160 #[document_signature]
1162 #[document_type_parameters(
1164 "The lifetime of the computation.",
1165 "The brand of the cloneable function to use.",
1166 "The type of the elements in the structure.",
1167 "The type of the accumulator."
1168 )]
1169 #[document_parameters(
1171 "The function to apply to the accumulator and each element.",
1172 "The initial value of the accumulator.",
1173 "The `TryThunk` to fold."
1174 )]
1175 #[document_returns("The final accumulator value.")]
1177 #[document_examples]
1178 fn fold_left<'a, FnBrand, A: 'a + Clone, B: 'a>(
1192 func: impl Fn(B, A) -> B + 'a,
1193 initial: B,
1194 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1195 ) -> B
1196 where
1197 FnBrand: CloneableFn + 'a, {
1198 match fa.evaluate() {
1199 Ok(a) => func(initial, a),
1200 Err(_) => initial,
1201 }
1202 }
1203
1204 #[document_signature]
1206 #[document_type_parameters(
1208 "The lifetime of the computation.",
1209 "The brand of the cloneable function to use.",
1210 "The type of the elements in the structure.",
1211 "The type of the monoid."
1212 )]
1213 #[document_parameters("The mapping function.", "The TryThunk to fold.")]
1215 #[document_returns("The monoid value.")]
1217 #[document_examples]
1219 fn fold_map<'a, FnBrand, A: 'a + Clone, M>(
1233 func: impl Fn(A) -> M + 'a,
1234 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1235 ) -> M
1236 where
1237 M: Monoid + 'a,
1238 FnBrand: CloneableFn + 'a, {
1239 match fa.evaluate() {
1240 Ok(a) => func(a),
1241 Err(_) => M::empty(),
1242 }
1243 }
1244 }
1245
1246 #[document_type_parameters(
1247 "The lifetime of the computation.",
1248 "The success value type.",
1249 "The error value type."
1250 )]
1251 impl<'a, A: Semigroup + 'a, E: 'a> Semigroup for TryThunk<'a, A, E> {
1252 #[document_signature]
1254 #[document_parameters("The first `TryThunk`.", "The second `TryThunk`.")]
1256 #[document_returns("A new `TryThunk` containing the combined result.")]
1258 #[document_examples]
1260 fn append(
1275 a: Self,
1276 b: Self,
1277 ) -> Self {
1278 TryThunk::new(move || {
1279 let a_val = a.evaluate()?;
1280 let b_val = b.evaluate()?;
1281 Ok(Semigroup::append(a_val, b_val))
1282 })
1283 }
1284 }
1285
1286 #[document_type_parameters(
1287 "The lifetime of the computation.",
1288 "The success value type.",
1289 "The error value type."
1290 )]
1291 impl<'a, A: Monoid + 'a, E: 'a> Monoid for TryThunk<'a, A, E> {
1292 #[document_signature]
1294 #[document_returns("A `TryThunk` producing the identity value of `A`.")]
1296 #[document_examples]
1298 fn empty() -> Self {
1309 TryThunk(Thunk::pure(Ok(Monoid::empty())))
1310 }
1311 }
1312
1313 impl_kind! {
1314 for TryThunkBrand {
1321 type Of<'a, E: 'a, A: 'a>: 'a = TryThunk<'a, A, E>;
1322 }
1323 }
1324
1325 impl Bifunctor for TryThunkBrand {
1326 #[document_signature]
1330 #[document_type_parameters(
1332 "The lifetime of the values.",
1333 "The type of the error value.",
1334 "The type of the mapped error value.",
1335 "The type of the success value.",
1336 "The type of the mapped success value."
1337 )]
1338 #[document_parameters(
1340 "The function to apply to the error.",
1341 "The function to apply to the success.",
1342 "The `TryThunk` to map over."
1343 )]
1344 #[document_returns("A new `TryThunk` containing the mapped values.")]
1346 #[document_examples]
1347 fn bimap<'a, A: 'a, B: 'a, C: 'a, D: 'a>(
1363 f: impl Fn(A) -> B + 'a,
1364 g: impl Fn(C) -> D + 'a,
1365 p: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, C>),
1366 ) -> Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, B, D>) {
1367 TryThunk(p.0.map(move |result| match result {
1368 Ok(c) => Ok(g(c)),
1369 Err(a) => Err(f(a)),
1370 }))
1371 }
1372 }
1373
1374 impl Bifoldable for TryThunkBrand {
1375 #[document_signature]
1380 #[document_type_parameters(
1382 "The lifetime of the values.",
1383 "The brand of the cloneable function to use.",
1384 "The error type (first position).",
1385 "The success type (second position).",
1386 "The accumulator type."
1387 )]
1388 #[document_parameters(
1390 "The step function applied to the error value.",
1391 "The step function applied to the success value.",
1392 "The initial accumulator.",
1393 "The `TryThunk` to fold."
1394 )]
1395 #[document_returns("`f(e, z)` for `Err(e)`, or `g(a, z)` for `Ok(a)`.")]
1397 #[document_examples]
1398 fn bi_fold_right<'a, FnBrand: CloneableFn + 'a, A: 'a + Clone, B: 'a + Clone, C: 'a>(
1426 f: impl Fn(A, C) -> C + 'a,
1427 g: impl Fn(B, C) -> C + 'a,
1428 z: C,
1429 p: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
1430 ) -> C {
1431 match p.evaluate() {
1432 Err(a) => f(a, z),
1433 Ok(b) => g(b, z),
1434 }
1435 }
1436
1437 #[document_signature]
1442 #[document_type_parameters(
1444 "The lifetime of the values.",
1445 "The brand of the cloneable function to use.",
1446 "The error type (first position).",
1447 "The success type (second position).",
1448 "The accumulator type."
1449 )]
1450 #[document_parameters(
1452 "The step function applied to the error value.",
1453 "The step function applied to the success value.",
1454 "The initial accumulator.",
1455 "The `TryThunk` to fold."
1456 )]
1457 #[document_returns("`f(z, e)` for `Err(e)`, or `g(z, a)` for `Ok(a)`.")]
1459 #[document_examples]
1460 fn bi_fold_left<'a, FnBrand: CloneableFn + 'a, A: 'a + Clone, B: 'a + Clone, C: 'a>(
1488 f: impl Fn(C, A) -> C + 'a,
1489 g: impl Fn(C, B) -> C + 'a,
1490 z: C,
1491 p: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
1492 ) -> C {
1493 match p.evaluate() {
1494 Err(a) => f(z, a),
1495 Ok(b) => g(z, b),
1496 }
1497 }
1498
1499 #[document_signature]
1504 #[document_type_parameters(
1506 "The lifetime of the values.",
1507 "The brand of the cloneable function to use.",
1508 "The error type (first position).",
1509 "The success type (second position).",
1510 "The monoid type."
1511 )]
1512 #[document_parameters(
1514 "The function mapping the error to the monoid.",
1515 "The function mapping the success to the monoid.",
1516 "The `TryThunk` to fold."
1517 )]
1518 #[document_returns("`f(e)` for `Err(e)`, or `g(a)` for `Ok(a)`.")]
1520 #[document_examples]
1521 fn bi_fold_map<'a, FnBrand: CloneableFn + 'a, A: 'a + Clone, B: 'a + Clone, M>(
1547 f: impl Fn(A) -> M + 'a,
1548 g: impl Fn(B) -> M + 'a,
1549 p: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
1550 ) -> M
1551 where
1552 M: Monoid + 'a, {
1553 match p.evaluate() {
1554 Err(a) => f(a),
1555 Ok(b) => g(b),
1556 }
1557 }
1558 }
1559
1560 impl_kind! {
1561 impl<A: 'static> for TryThunkOkAppliedBrand<A> {
1573 #[document_default]
1574 type Of<'a, E: 'a>: 'a = TryThunk<'a, A, E>;
1575 }
1576 }
1577
1578 #[document_type_parameters("The success type.")]
1579 impl<A: 'static> Functor for TryThunkOkAppliedBrand<A> {
1580 #[document_signature]
1582 #[document_type_parameters(
1584 "The lifetime of the computation.",
1585 "The type of the error value inside the `TryThunk`.",
1586 "The type of the result of the transformation."
1587 )]
1588 #[document_parameters("The function to apply to the error.", "The `TryThunk` instance.")]
1590 #[document_returns("A new `TryThunk` instance with the transformed error.")]
1592 #[document_examples]
1594 fn map<'a, E: 'a, E2: 'a>(
1607 func: impl Fn(E) -> E2 + 'a,
1608 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
1609 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
1610 fa.map_err(func)
1611 }
1612 }
1613
1614 #[document_type_parameters("The success type.")]
1615 impl<A: 'static> Pointed for TryThunkOkAppliedBrand<A> {
1616 #[document_signature]
1618 #[document_type_parameters(
1620 "The lifetime of the computation.",
1621 "The type of the value to wrap."
1622 )]
1623 #[document_parameters("The value to wrap.")]
1625 #[document_returns("A new `TryThunk` instance containing the value as an error.")]
1627 #[document_examples]
1629 fn pure<'a, E: 'a>(e: E) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>) {
1641 TryThunk::err(e)
1642 }
1643 }
1644
1645 #[document_type_parameters("The success type.")]
1646 impl<A: 'static> Lift for TryThunkOkAppliedBrand<A> {
1647 #[document_signature]
1657 #[document_type_parameters(
1659 "The lifetime of the computation.",
1660 "The type of the first error value.",
1661 "The type of the second error value.",
1662 "The type of the result error value."
1663 )]
1664 #[document_parameters(
1666 "The binary function to apply to the errors.",
1667 "The first `TryThunk`.",
1668 "The second `TryThunk`."
1669 )]
1670 #[document_returns(
1672 "A new `TryThunk` instance containing the result of applying the function to the errors."
1673 )]
1674 #[document_examples]
1675 fn lift2<'a, E1, E2, E3>(
1689 func: impl Fn(E1, E2) -> E3 + 'a,
1690 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E1>),
1691 fb: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>),
1692 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E3>)
1693 where
1694 E1: Clone + 'a,
1695 E2: Clone + 'a,
1696 E3: 'a, {
1697 TryThunk::new(move || match (fa.evaluate(), fb.evaluate()) {
1698 (Err(e1), Err(e2)) => Err(func(e1, e2)),
1699 (Ok(a), _) => Ok(a),
1700 (_, Ok(a)) => Ok(a),
1701 })
1702 }
1703 }
1704
1705 #[document_type_parameters("The success type.")]
1706 impl<A: 'static> ApplyFirst for TryThunkOkAppliedBrand<A> {}
1707
1708 #[document_type_parameters("The success type.")]
1709 impl<A: 'static> ApplySecond for TryThunkOkAppliedBrand<A> {}
1710
1711 #[document_type_parameters("The success type.")]
1712 impl<A: 'static> Semiapplicative for TryThunkOkAppliedBrand<A> {
1713 #[document_signature]
1723 #[document_type_parameters(
1725 "The lifetime of the computation.",
1726 "The brand of the cloneable function wrapper.",
1727 "The type of the input error.",
1728 "The type of the result error."
1729 )]
1730 #[document_parameters(
1732 "The `TryThunk` containing the function (in Err).",
1733 "The `TryThunk` containing the value (in Err)."
1734 )]
1735 #[document_returns(
1737 "A new `TryThunk` instance containing the result of applying the function."
1738 )]
1739 #[document_examples]
1740 fn apply<'a, FnBrand: 'a + CloneableFn, E1: 'a + Clone, E2: 'a>(
1755 ff: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as CloneableFn>::Of<'a, E1, E2>>),
1756 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E1>),
1757 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
1758 TryThunk::new(move || match (ff.evaluate(), fa.evaluate()) {
1759 (Err(f), Err(e)) => Err(f(e)),
1760 (Ok(a), _) => Ok(a),
1761 (_, Ok(a)) => Ok(a),
1762 })
1763 }
1764 }
1765
1766 #[document_type_parameters("The success type.")]
1767 impl<A: 'static> Semimonad for TryThunkOkAppliedBrand<A> {
1768 #[document_signature]
1770 #[document_type_parameters(
1772 "The lifetime of the computation.",
1773 "The type of the result of the first computation (error).",
1774 "The type of the result of the new computation (error)."
1775 )]
1776 #[document_parameters(
1778 "The first `TryThunk`.",
1779 "The function to apply to the error result of the computation."
1780 )]
1781 #[document_returns("A new `TryThunk` instance representing the chained computation.")]
1783 #[document_examples]
1784 fn bind<'a, E1: 'a, E2: 'a>(
1799 ma: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E1>),
1800 func: impl Fn(E1) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) + 'a,
1801 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
1802 TryThunk::new(move || match ma.evaluate() {
1803 Ok(a) => Ok(a),
1804 Err(e) => func(e).evaluate(),
1805 })
1806 }
1807 }
1808
1809 #[document_type_parameters("The success type.")]
1810 impl<A: 'static> MonadRec for TryThunkOkAppliedBrand<A> {
1811 #[document_signature]
1817 #[document_type_parameters(
1819 "The lifetime of the computation.",
1820 "The type of the initial value and loop state.",
1821 "The type of the result."
1822 )]
1823 #[document_parameters("The step function.", "The initial value.")]
1825 #[document_returns("The result of the computation.")]
1827 #[document_examples]
1829 fn tail_rec_m<'a, E: 'a, E2: 'a>(
1852 f: impl Fn(
1853 E,
1854 )
1855 -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, ControlFlow<E2, E>>)
1856 + 'a,
1857 e: E,
1858 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
1859 TryThunk::new(move || {
1860 let mut current = e;
1861 loop {
1862 match f(current).evaluate() {
1863 Err(ControlFlow::Continue(next)) => current = next,
1864 Err(ControlFlow::Break(res)) => break Err(res),
1865 Ok(a) => break Ok(a),
1866 }
1867 }
1868 })
1869 }
1870 }
1871
1872 #[document_type_parameters("The success type.")]
1873 impl<A: 'static> Foldable for TryThunkOkAppliedBrand<A> {
1874 #[document_signature]
1876 #[document_type_parameters(
1878 "The lifetime of the computation.",
1879 "The brand of the cloneable function to use.",
1880 "The type of the elements in the structure.",
1881 "The type of the accumulator."
1882 )]
1883 #[document_parameters(
1885 "The function to apply to each element and the accumulator.",
1886 "The initial value of the accumulator.",
1887 "The `TryThunk` to fold."
1888 )]
1889 #[document_returns("The final accumulator value.")]
1891 #[document_examples]
1892 fn fold_right<'a, FnBrand, E: 'a + Clone, B: 'a>(
1906 func: impl Fn(E, B) -> B + 'a,
1907 initial: B,
1908 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
1909 ) -> B
1910 where
1911 FnBrand: CloneableFn + 'a, {
1912 match fa.evaluate() {
1913 Err(e) => func(e, initial),
1914 Ok(_) => initial,
1915 }
1916 }
1917
1918 #[document_signature]
1920 #[document_type_parameters(
1922 "The lifetime of the computation.",
1923 "The brand of the cloneable function to use.",
1924 "The type of the elements in the structure.",
1925 "The type of the accumulator."
1926 )]
1927 #[document_parameters(
1929 "The function to apply to the accumulator and each element.",
1930 "The initial value of the accumulator.",
1931 "The `TryThunk` to fold."
1932 )]
1933 #[document_returns("The final accumulator value.")]
1935 #[document_examples]
1936 fn fold_left<'a, FnBrand, E: 'a + Clone, B: 'a>(
1950 func: impl Fn(B, E) -> B + 'a,
1951 initial: B,
1952 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
1953 ) -> B
1954 where
1955 FnBrand: CloneableFn + 'a, {
1956 match fa.evaluate() {
1957 Err(e) => func(initial, e),
1958 Ok(_) => initial,
1959 }
1960 }
1961
1962 #[document_signature]
1964 #[document_type_parameters(
1966 "The lifetime of the computation.",
1967 "The brand of the cloneable function to use.",
1968 "The type of the elements in the structure.",
1969 "The type of the monoid."
1970 )]
1971 #[document_parameters("The mapping function.", "The TryThunk to fold.")]
1973 #[document_returns("The monoid value.")]
1975 #[document_examples]
1977 fn fold_map<'a, FnBrand, E: 'a + Clone, M>(
1991 func: impl Fn(E) -> M + 'a,
1992 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
1993 ) -> M
1994 where
1995 M: Monoid + 'a,
1996 FnBrand: CloneableFn + 'a, {
1997 match fa.evaluate() {
1998 Err(e) => func(e),
1999 Ok(_) => M::empty(),
2000 }
2001 }
2002 }
2003
2004 #[document_type_parameters(
2005 "The lifetime of the computation.",
2006 "The type of the success value.",
2007 "The type of the error value."
2008 )]
2009 #[document_parameters("The try-thunk to format.")]
2010 impl<'a, A, E> fmt::Debug for TryThunk<'a, A, E> {
2011 #[document_signature]
2013 #[document_parameters("The formatter.")]
2014 #[document_returns("The formatting result.")]
2015 #[document_examples]
2016 fn fmt(
2023 &self,
2024 f: &mut fmt::Formatter<'_>,
2025 ) -> fmt::Result {
2026 f.write_str("TryThunk(<unevaluated>)")
2027 }
2028 }
2029
2030 #[document_type_parameters("The error type.")]
2031 impl<E: 'static> WithIndex for TryThunkErrAppliedBrand<E> {
2032 type Index = ();
2033 }
2034
2035 #[document_type_parameters("The error type.")]
2036 impl<E: 'static> FunctorWithIndex for TryThunkErrAppliedBrand<E> {
2037 #[document_signature]
2039 #[document_type_parameters(
2040 "The lifetime of the computation.",
2041 "The type of the success value inside the `TryThunk`.",
2042 "The type of the result of applying the function."
2043 )]
2044 #[document_parameters(
2045 "The function to apply to the value and its index.",
2046 "The `TryThunk` to map over."
2047 )]
2048 #[document_returns(
2049 "A new `TryThunk` containing the result of applying the function, or the original error."
2050 )]
2051 #[document_examples]
2052 fn map_with_index<'a, A: 'a, B: 'a>(
2065 f: impl Fn((), A) -> B + 'a,
2066 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
2067 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
2068 fa.map(move |a| f((), a))
2069 }
2070 }
2071
2072 #[document_type_parameters("The error type.")]
2073 impl<E: 'static> FoldableWithIndex for TryThunkErrAppliedBrand<E> {
2074 #[document_signature]
2076 #[document_type_parameters(
2077 "The lifetime of the computation.",
2078 "The type of the success value inside the `TryThunk`.",
2079 "The monoid type."
2080 )]
2081 #[document_parameters(
2082 "The function to apply to the value and its index.",
2083 "The `TryThunk` to fold."
2084 )]
2085 #[document_returns("The monoid value.")]
2086 #[document_examples]
2087 fn fold_map_with_index<'a, A: 'a + Clone, R: Monoid>(
2103 f: impl Fn((), A) -> R + 'a,
2104 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
2105 ) -> R {
2106 match fa.evaluate() {
2107 Ok(a) => f((), a),
2108 Err(_) => R::empty(),
2109 }
2110 }
2111 }
2112
2113 #[document_type_parameters("The success type.")]
2114 impl<A: 'static> WithIndex for TryThunkOkAppliedBrand<A> {
2115 type Index = ();
2116 }
2117
2118 #[document_type_parameters("The success type.")]
2119 impl<A: 'static> FunctorWithIndex for TryThunkOkAppliedBrand<A> {
2120 #[document_signature]
2122 #[document_type_parameters(
2123 "The lifetime of the computation.",
2124 "The type of the error value inside the `TryThunk`.",
2125 "The type of the result of applying the function."
2126 )]
2127 #[document_parameters(
2128 "The function to apply to the error and its index.",
2129 "The `TryThunk` to map over."
2130 )]
2131 #[document_returns(
2132 "A new `TryThunk` containing the original success or the transformed error."
2133 )]
2134 #[document_examples]
2135 fn map_with_index<'a, E: 'a, E2: 'a>(
2148 f: impl Fn((), E) -> E2 + 'a,
2149 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
2150 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
2151 fa.map_err(move |e| f((), e))
2152 }
2153 }
2154
2155 #[document_type_parameters("The success type.")]
2156 impl<A: 'static> FoldableWithIndex for TryThunkOkAppliedBrand<A> {
2157 #[document_signature]
2159 #[document_type_parameters(
2160 "The lifetime of the computation.",
2161 "The type of the error value inside the `TryThunk`.",
2162 "The monoid type."
2163 )]
2164 #[document_parameters(
2165 "The function to apply to the error and its index.",
2166 "The `TryThunk` to fold."
2167 )]
2168 #[document_returns("The monoid value.")]
2169 #[document_examples]
2170 fn fold_map_with_index<'a, E: 'a + Clone, R: Monoid>(
2186 f: impl Fn((), E) -> R + 'a,
2187 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
2188 ) -> R {
2189 match fa.evaluate() {
2190 Err(e) => f((), e),
2191 Ok(_) => R::empty(),
2192 }
2193 }
2194 }
2195}
2196pub use inner::*;
2197
2198#[cfg(test)]
2199mod tests {
2200 use {
2201 super::*,
2202 crate::types::Thunk,
2203 quickcheck_macros::quickcheck,
2204 };
2205
2206 #[test]
2210 fn test_success() {
2211 let try_thunk: TryThunk<i32, ()> = TryThunk::ok(42);
2212 assert_eq!(try_thunk.evaluate(), Ok(42));
2213 }
2214
2215 #[test]
2217 fn test_pure() {
2218 let try_thunk: TryThunk<i32, ()> = TryThunk::pure(42);
2219 assert_eq!(try_thunk.evaluate(), Ok(42));
2220 }
2221
2222 #[test]
2226 fn test_failure() {
2227 let try_thunk: TryThunk<i32, &str> = TryThunk::err("error");
2228 assert_eq!(try_thunk.evaluate(), Err("error"));
2229 }
2230
2231 #[test]
2235 fn test_map() {
2236 let try_thunk: TryThunk<i32, ()> = TryThunk::ok(21).map(|x| x * 2);
2237 assert_eq!(try_thunk.evaluate(), Ok(42));
2238 }
2239
2240 #[test]
2244 fn test_map_err() {
2245 let try_thunk: TryThunk<i32, i32> = TryThunk::err(21).map_err(|x| x * 2);
2246 assert_eq!(try_thunk.evaluate(), Err(42));
2247 }
2248
2249 #[test]
2253 fn test_bind() {
2254 let try_thunk: TryThunk<i32, ()> = TryThunk::ok(21).bind(|x| TryThunk::ok(x * 2));
2255 assert_eq!(try_thunk.evaluate(), Ok(42));
2256 }
2257
2258 #[test]
2262 fn test_borrowing() {
2263 let x = 42;
2264 let try_thunk: TryThunk<&i32, ()> = TryThunk::new(|| Ok(&x));
2265 assert_eq!(try_thunk.evaluate(), Ok(&42));
2266 }
2267
2268 #[test]
2272 fn test_bind_failure() {
2273 let try_thunk = TryThunk::<i32, &str>::err("error").bind(|x| TryThunk::ok(x * 2));
2274 assert_eq!(try_thunk.evaluate(), Err("error"));
2275 }
2276
2277 #[test]
2281 fn test_map_failure() {
2282 let try_thunk = TryThunk::<i32, &str>::err("error").map(|x| x * 2);
2283 assert_eq!(try_thunk.evaluate(), Err("error"));
2284 }
2285
2286 #[test]
2290 fn test_map_err_success() {
2291 let try_thunk = TryThunk::<i32, &str>::pure(42).map_err(|_| "new error");
2292 assert_eq!(try_thunk.evaluate(), Ok(42));
2293 }
2294
2295 #[test]
2297 fn test_try_thunk_from_memo() {
2298 use crate::types::RcLazy;
2299 let memo = RcLazy::new(|| 42);
2300 let try_thunk: TryThunk<i32, ()> = TryThunk::from(memo);
2301 assert_eq!(try_thunk.evaluate(), Ok(42));
2302 }
2303
2304 #[test]
2306 fn test_try_thunk_from_try_memo() {
2307 use crate::types::RcTryLazy;
2308 let memo = RcTryLazy::new(|| Ok(42));
2309 let try_thunk: TryThunk<i32, ()> = TryThunk::from(memo);
2310 assert_eq!(try_thunk.evaluate(), Ok(42));
2311 }
2312
2313 #[test]
2317 fn test_try_thunk_from_eval() {
2318 let eval = Thunk::pure(42);
2319 let try_thunk: TryThunk<i32, ()> = TryThunk::from(eval);
2320 assert_eq!(try_thunk.evaluate(), Ok(42));
2321 }
2322
2323 #[test]
2325 fn test_defer() {
2326 let try_thunk: TryThunk<i32, ()> = TryThunk::defer(|| TryThunk::ok(42));
2327 assert_eq!(try_thunk.evaluate(), Ok(42));
2328 }
2329
2330 #[test]
2334 fn test_catch() {
2335 let try_thunk: TryThunk<i32, &str> = TryThunk::err("error").catch(|_| TryThunk::ok(42));
2336 assert_eq!(try_thunk.evaluate(), Ok(42));
2337 }
2338
2339 #[test]
2343 fn test_catch_with() {
2344 let recovered: TryThunk<i32, i32> =
2345 TryThunk::<i32, &str>::err("error").catch_with(|_| TryThunk::err(42));
2346 assert_eq!(recovered.evaluate(), Err(42));
2347
2348 let ok: TryThunk<i32, i32> = TryThunk::<i32, &str>::ok(1).catch_with(|_| TryThunk::err(42));
2349 assert_eq!(ok.evaluate(), Ok(1));
2350 }
2351
2352 #[test]
2354 fn test_try_thunk_with_err_brand() {
2355 use crate::{
2356 brands::*,
2357 functions::*,
2358 };
2359
2360 let try_thunk: TryThunk<i32, ()> = TryThunk::ok(10);
2362 let mapped = map::<TryThunkErrAppliedBrand<()>, _, _>(|x| x * 2, try_thunk);
2363 assert_eq!(mapped.evaluate(), Ok(20));
2364
2365 let try_thunk: TryThunk<i32, ()> = pure::<TryThunkErrAppliedBrand<()>, _>(42);
2367 assert_eq!(try_thunk.evaluate(), Ok(42));
2368
2369 let try_thunk: TryThunk<i32, ()> = TryThunk::ok(10);
2371 let bound = bind::<TryThunkErrAppliedBrand<()>, _, _>(try_thunk, |x| {
2372 pure::<TryThunkErrAppliedBrand<()>, _>(x * 2)
2373 });
2374 assert_eq!(bound.evaluate(), Ok(20));
2375
2376 let try_thunk: TryThunk<i32, ()> = TryThunk::ok(10);
2378 let folded = fold_right::<RcFnBrand, TryThunkErrAppliedBrand<()>, _, _>(
2379 |x, acc| x + acc,
2380 5,
2381 try_thunk,
2382 );
2383 assert_eq!(folded, 15);
2384 }
2385
2386 #[test]
2388 fn test_bifunctor() {
2389 use crate::{
2390 brands::*,
2391 classes::bifunctor::*,
2392 };
2393
2394 let x: TryThunk<i32, i32> = TryThunk::ok(5);
2395 assert_eq!(bimap::<TryThunkBrand, _, _, _, _>(|e| e + 1, |s| s * 2, x).evaluate(), Ok(10));
2396
2397 let y: TryThunk<i32, i32> = TryThunk::err(5);
2398 assert_eq!(bimap::<TryThunkBrand, _, _, _, _>(|e| e + 1, |s| s * 2, y).evaluate(), Err(6));
2399 }
2400
2401 #[test]
2405 fn test_bifoldable_right() {
2406 use crate::{
2407 brands::*,
2408 functions::*,
2409 };
2410
2411 assert_eq!(
2413 bi_fold_right::<RcFnBrand, TryThunkBrand, _, _, _>(
2414 |e: i32, acc| acc - e,
2415 |s: i32, acc| acc + s,
2416 10,
2417 TryThunk::err(3),
2418 ),
2419 7
2420 );
2421
2422 assert_eq!(
2424 bi_fold_right::<RcFnBrand, TryThunkBrand, _, _, _>(
2425 |e: i32, acc| acc - e,
2426 |s: i32, acc| acc + s,
2427 10,
2428 TryThunk::ok(5),
2429 ),
2430 15
2431 );
2432 }
2433
2434 #[test]
2438 fn test_bifoldable_left() {
2439 use crate::{
2440 brands::*,
2441 functions::*,
2442 };
2443
2444 assert_eq!(
2445 bi_fold_left::<RcFnBrand, TryThunkBrand, _, _, _>(
2446 |acc, e: i32| acc - e,
2447 |acc, s: i32| acc + s,
2448 10,
2449 TryThunk::err(3),
2450 ),
2451 7
2452 );
2453
2454 assert_eq!(
2455 bi_fold_left::<RcFnBrand, TryThunkBrand, _, _, _>(
2456 |acc, e: i32| acc - e,
2457 |acc, s: i32| acc + s,
2458 10,
2459 TryThunk::ok(5),
2460 ),
2461 15
2462 );
2463 }
2464
2465 #[test]
2469 fn test_bifoldable_map() {
2470 use crate::{
2471 brands::*,
2472 functions::*,
2473 };
2474
2475 assert_eq!(
2476 bi_fold_map::<RcFnBrand, TryThunkBrand, _, _, _>(
2477 |e: i32| e.to_string(),
2478 |s: i32| s.to_string(),
2479 TryThunk::err(3),
2480 ),
2481 "3".to_string()
2482 );
2483
2484 assert_eq!(
2485 bi_fold_map::<RcFnBrand, TryThunkBrand, _, _, _>(
2486 |e: i32| e.to_string(),
2487 |s: i32| s.to_string(),
2488 TryThunk::ok(5),
2489 ),
2490 "5".to_string()
2491 );
2492 }
2493
2494 #[test]
2498 fn test_monad_rec_ok_applied() {
2499 use {
2500 crate::{
2501 brands::*,
2502 functions::*,
2503 },
2504 core::ops::ControlFlow,
2505 };
2506
2507 let result = tail_rec_m::<TryThunkOkAppliedBrand<i32>, _, _>(
2508 |x| {
2509 pure::<TryThunkOkAppliedBrand<i32>, _>(
2510 if x < 100 { ControlFlow::Continue(x + 1) } else { ControlFlow::Break(x) },
2511 )
2512 },
2513 0,
2514 );
2515 assert_eq!(result.evaluate(), Err(100));
2516 }
2517
2518 #[test]
2522 fn test_monad_rec_ok_applied_short_circuit() {
2523 use {
2524 crate::{
2525 brands::*,
2526 functions::*,
2527 },
2528 core::ops::ControlFlow,
2529 };
2530
2531 let result = tail_rec_m::<TryThunkOkAppliedBrand<i32>, _, _>(
2532 |x: i32| {
2533 if x == 5 {
2534 TryThunk::ok(42)
2535 } else {
2536 pure::<TryThunkOkAppliedBrand<i32>, _>(ControlFlow::<i32, i32>::Continue(x + 1))
2537 }
2538 },
2539 0,
2540 );
2541 assert_eq!(result.evaluate(), Ok(42));
2542 }
2543
2544 #[test]
2548 fn test_catch_unwind() {
2549 let thunk = TryThunk::<i32, String>::catch_unwind(|| {
2550 if true {
2551 panic!("oops")
2552 }
2553 42
2554 });
2555 assert_eq!(thunk.evaluate(), Err("oops".to_string()));
2556 }
2557
2558 #[test]
2562 fn test_catch_unwind_success() {
2563 let thunk = TryThunk::<i32, String>::catch_unwind(|| 42);
2564 assert_eq!(thunk.evaluate(), Ok(42));
2565 }
2566
2567 #[test]
2569 fn test_try_thunk_with_ok_brand() {
2570 use crate::{
2571 brands::*,
2572 functions::*,
2573 };
2574
2575 let try_thunk: TryThunk<i32, i32> = TryThunk::err(10);
2577 let mapped = map::<TryThunkOkAppliedBrand<i32>, _, _>(|x| x * 2, try_thunk);
2578 assert_eq!(mapped.evaluate(), Err(20));
2579
2580 let try_thunk: TryThunk<i32, i32> = pure::<TryThunkOkAppliedBrand<i32>, _>(42);
2582 assert_eq!(try_thunk.evaluate(), Err(42));
2583
2584 let try_thunk: TryThunk<i32, i32> = TryThunk::err(10);
2586 let bound = bind::<TryThunkOkAppliedBrand<i32>, _, _>(try_thunk, |x| {
2587 pure::<TryThunkOkAppliedBrand<i32>, _>(x * 2)
2588 });
2589 assert_eq!(bound.evaluate(), Err(20));
2590
2591 let try_thunk: TryThunk<i32, i32> = TryThunk::err(10);
2593 let folded = fold_right::<RcFnBrand, TryThunkOkAppliedBrand<i32>, _, _>(
2594 |x, acc| x + acc,
2595 5,
2596 try_thunk,
2597 );
2598 assert_eq!(folded, 15);
2599 }
2600
2601 #[test]
2605 fn test_try_thunk_from_result_ok() {
2606 let try_thunk: TryThunk<i32, String> = TryThunk::from(Ok(42));
2607 assert_eq!(try_thunk.evaluate(), Ok(42));
2608 }
2609
2610 #[test]
2614 fn test_try_thunk_from_result_err() {
2615 let try_thunk: TryThunk<i32, String> = TryThunk::from(Err("error".to_string()));
2616 assert_eq!(try_thunk.evaluate(), Err("error".to_string()));
2617 }
2618
2619 #[test]
2623 fn test_try_thunk_from_try_send_thunk_ok() {
2624 use crate::types::TrySendThunk;
2625 let send: TrySendThunk<i32, ()> = TrySendThunk::pure(42);
2626 let thunk: TryThunk<i32, ()> = TryThunk::from(send);
2627 assert_eq!(thunk.evaluate(), Ok(42));
2628 }
2629
2630 #[test]
2634 fn test_try_thunk_from_try_send_thunk_err() {
2635 use crate::types::TrySendThunk;
2636 let send: TrySendThunk<i32, String> = TrySendThunk::err("fail".to_string());
2637 let thunk: TryThunk<i32, String> = TryThunk::from(send);
2638 assert_eq!(thunk.evaluate(), Err("fail".to_string()));
2639 }
2640
2641 #[quickcheck]
2647 fn functor_identity(x: i32) -> bool {
2648 use crate::{
2649 brands::*,
2650 functions::*,
2651 };
2652 let t: TryThunk<i32, i32> = TryThunk::ok(x);
2653 map::<TryThunkErrAppliedBrand<i32>, _, _>(|a| a, t).evaluate() == Ok(x)
2654 }
2655
2656 #[quickcheck]
2658 fn functor_composition(x: i32) -> bool {
2659 use crate::{
2660 brands::*,
2661 functions::*,
2662 };
2663 let f = |a: i32| a.wrapping_add(1);
2664 let g = |a: i32| a.wrapping_mul(2);
2665 let lhs =
2666 map::<TryThunkErrAppliedBrand<i32>, _, _>(move |a| f(g(a)), TryThunk::ok(x)).evaluate();
2667 let rhs = map::<TryThunkErrAppliedBrand<i32>, _, _>(
2668 f,
2669 map::<TryThunkErrAppliedBrand<i32>, _, _>(g, TryThunk::ok(x)),
2670 )
2671 .evaluate();
2672 lhs == rhs
2673 }
2674
2675 #[quickcheck]
2679 fn monad_left_identity(a: i32) -> bool {
2680 use crate::{
2681 brands::*,
2682 functions::*,
2683 };
2684 let f = |x: i32| pure::<TryThunkErrAppliedBrand<i32>, _>(x.wrapping_mul(2));
2685 let lhs = bind::<TryThunkErrAppliedBrand<i32>, _, _>(
2686 pure::<TryThunkErrAppliedBrand<i32>, _>(a),
2687 f,
2688 )
2689 .evaluate();
2690 let rhs = f(a).evaluate();
2691 lhs == rhs
2692 }
2693
2694 #[quickcheck]
2696 fn monad_right_identity(x: i32) -> bool {
2697 use crate::{
2698 brands::*,
2699 functions::*,
2700 };
2701 let lhs = bind::<TryThunkErrAppliedBrand<i32>, _, _>(
2702 pure::<TryThunkErrAppliedBrand<i32>, _>(x),
2703 pure::<TryThunkErrAppliedBrand<i32>, _>,
2704 )
2705 .evaluate();
2706 lhs == Ok(x)
2707 }
2708
2709 #[quickcheck]
2711 fn monad_associativity(x: i32) -> bool {
2712 use crate::{
2713 brands::*,
2714 functions::*,
2715 };
2716 let f = |a: i32| pure::<TryThunkErrAppliedBrand<i32>, _>(a.wrapping_add(1));
2717 let g = |a: i32| pure::<TryThunkErrAppliedBrand<i32>, _>(a.wrapping_mul(3));
2718 let m: TryThunk<i32, i32> = TryThunk::ok(x);
2719 let m2: TryThunk<i32, i32> = TryThunk::ok(x);
2720 let lhs = bind::<TryThunkErrAppliedBrand<i32>, _, _>(
2721 bind::<TryThunkErrAppliedBrand<i32>, _, _>(m, f),
2722 g,
2723 )
2724 .evaluate();
2725 let rhs = bind::<TryThunkErrAppliedBrand<i32>, _, _>(m2, move |a| {
2726 bind::<TryThunkErrAppliedBrand<i32>, _, _>(f(a), g)
2727 })
2728 .evaluate();
2729 lhs == rhs
2730 }
2731
2732 #[quickcheck]
2734 fn error_short_circuit(e: i32) -> bool {
2735 let t: TryThunk<i32, i32> = TryThunk::err(e);
2736 t.bind(|x| TryThunk::ok(x.wrapping_add(1))).evaluate() == Err(e)
2737 }
2738
2739 #[test]
2743 fn test_lift2_ok_ok() {
2744 let t1: TryThunk<i32, String> = TryThunk::ok(10);
2745 let t2: TryThunk<i32, String> = TryThunk::ok(20);
2746 let t3 = t1.lift2(t2, |a, b| a + b);
2747 assert_eq!(t3.evaluate(), Ok(30));
2748 }
2749
2750 #[test]
2754 fn test_lift2_err_ok() {
2755 let t1: TryThunk<i32, String> = TryThunk::err("first".to_string());
2756 let t2: TryThunk<i32, String> = TryThunk::ok(20);
2757 let t3 = t1.lift2(t2, |a, b| a + b);
2758 assert_eq!(t3.evaluate(), Err("first".to_string()));
2759 }
2760
2761 #[test]
2765 fn test_lift2_ok_err() {
2766 let t1: TryThunk<i32, String> = TryThunk::ok(10);
2767 let t2: TryThunk<i32, String> = TryThunk::err("second".to_string());
2768 let t3 = t1.lift2(t2, |a, b| a + b);
2769 assert_eq!(t3.evaluate(), Err("second".to_string()));
2770 }
2771
2772 #[test]
2776 fn test_then_ok_ok() {
2777 let t1: TryThunk<i32, String> = TryThunk::ok(10);
2778 let t2: TryThunk<i32, String> = TryThunk::ok(20);
2779 let t3 = t1.then(t2);
2780 assert_eq!(t3.evaluate(), Ok(20));
2781 }
2782
2783 #[test]
2787 fn test_then_err_ok() {
2788 let t1: TryThunk<i32, String> = TryThunk::err("first".to_string());
2789 let t2: TryThunk<i32, String> = TryThunk::ok(20);
2790 let t3 = t1.then(t2);
2791 assert_eq!(t3.evaluate(), Err("first".to_string()));
2792 }
2793
2794 #[test]
2798 fn test_then_ok_err() {
2799 let t1: TryThunk<i32, String> = TryThunk::ok(10);
2800 let t2: TryThunk<i32, String> = TryThunk::err("second".to_string());
2801 let t3 = t1.then(t2);
2802 assert_eq!(t3.evaluate(), Err("second".to_string()));
2803 }
2804
2805 #[test]
2809 fn test_into_rc_try_lazy() {
2810 let thunk: TryThunk<i32, ()> = TryThunk::ok(42);
2811 let lazy = thunk.into_rc_try_lazy();
2812 assert_eq!(lazy.evaluate(), Ok(&42));
2813 }
2814
2815 #[test]
2819 fn test_into_rc_try_lazy_caching() {
2820 use std::{
2821 cell::RefCell,
2822 rc::Rc,
2823 };
2824
2825 let counter = Rc::new(RefCell::new(0));
2826 let counter_clone = counter.clone();
2827 let thunk: TryThunk<i32, ()> = TryThunk::new(move || {
2828 *counter_clone.borrow_mut() += 1;
2829 Ok(42)
2830 });
2831 let lazy = thunk.into_rc_try_lazy();
2832
2833 assert_eq!(*counter.borrow(), 0);
2834 assert_eq!(lazy.evaluate(), Ok(&42));
2835 assert_eq!(*counter.borrow(), 1);
2836 assert_eq!(lazy.evaluate(), Ok(&42));
2837 assert_eq!(*counter.borrow(), 1);
2838 }
2839
2840 #[test]
2844 fn test_into_arc_try_lazy() {
2845 let thunk: TryThunk<i32, ()> = TryThunk::ok(42);
2846 let lazy = thunk.into_arc_try_lazy();
2847 assert_eq!(lazy.evaluate(), Ok(&42));
2848 }
2849
2850 #[test]
2854 fn test_into_arc_try_lazy_send_sync() {
2855 use std::thread;
2856
2857 let thunk: TryThunk<i32, String> = TryThunk::ok(42);
2858 let lazy = thunk.into_arc_try_lazy();
2859 let lazy_clone = lazy.clone();
2860
2861 let handle = thread::spawn(move || {
2862 assert_eq!(lazy_clone.evaluate(), Ok(&42));
2863 });
2864
2865 assert_eq!(lazy.evaluate(), Ok(&42));
2866 handle.join().unwrap();
2867 }
2868
2869 #[test]
2873 fn test_catch_unwind_with_panic() {
2874 let thunk = TryThunk::<i32, i32>::catch_unwind_with(
2875 || {
2876 if true {
2877 panic!("oops")
2878 }
2879 42
2880 },
2881 |_payload| -1,
2882 );
2883 assert_eq!(thunk.evaluate(), Err(-1));
2884 }
2885
2886 #[test]
2890 fn test_catch_unwind_with_success() {
2891 let thunk = TryThunk::<i32, i32>::catch_unwind_with(|| 42, |_payload| -1);
2892 assert_eq!(thunk.evaluate(), Ok(42));
2893 }
2894
2895 #[quickcheck]
2899 fn bifunctor_identity_ok(x: i32) -> bool {
2900 use crate::{
2901 brands::*,
2902 classes::bifunctor::*,
2903 };
2904 let t: TryThunk<i32, i32> = TryThunk::ok(x);
2905 bimap::<TryThunkBrand, _, _, _, _>(|e| e, |a| a, t).evaluate() == Ok(x)
2906 }
2907
2908 #[quickcheck]
2910 fn bifunctor_identity_err(e: i32) -> bool {
2911 use crate::{
2912 brands::*,
2913 classes::bifunctor::*,
2914 };
2915 let t: TryThunk<i32, i32> = TryThunk::err(e);
2916 bimap::<TryThunkBrand, _, _, _, _>(|e| e, |a| a, t).evaluate() == Err(e)
2917 }
2918
2919 #[quickcheck]
2921 fn bifunctor_composition_ok(x: i32) -> bool {
2922 use crate::{
2923 brands::*,
2924 classes::bifunctor::*,
2925 };
2926 let f1 = |a: i32| a.wrapping_add(1);
2927 let f2 = |a: i32| a.wrapping_mul(2);
2928 let g1 = |a: i32| a.wrapping_add(10);
2929 let g2 = |a: i32| a.wrapping_mul(3);
2930
2931 let lhs = bimap::<TryThunkBrand, _, _, _, _>(
2932 move |e| f1(f2(e)),
2933 move |a| g1(g2(a)),
2934 TryThunk::ok(x),
2935 )
2936 .evaluate();
2937 let rhs = bimap::<TryThunkBrand, _, _, _, _>(
2938 f1,
2939 g1,
2940 bimap::<TryThunkBrand, _, _, _, _>(f2, g2, TryThunk::ok(x)),
2941 )
2942 .evaluate();
2943 lhs == rhs
2944 }
2945
2946 #[quickcheck]
2948 fn bifunctor_composition_err(e: i32) -> bool {
2949 use crate::{
2950 brands::*,
2951 classes::bifunctor::*,
2952 };
2953 let f1 = |a: i32| a.wrapping_add(1);
2954 let f2 = |a: i32| a.wrapping_mul(2);
2955 let g1 = |a: i32| a.wrapping_add(10);
2956 let g2 = |a: i32| a.wrapping_mul(3);
2957
2958 let lhs = bimap::<TryThunkBrand, _, _, _, _>(
2959 move |e| f1(f2(e)),
2960 move |a| g1(g2(a)),
2961 TryThunk::err(e),
2962 )
2963 .evaluate();
2964 let rhs = bimap::<TryThunkBrand, _, _, _, _>(
2965 f1,
2966 g1,
2967 bimap::<TryThunkBrand, _, _, _, _>(f2, g2, TryThunk::err(e)),
2968 )
2969 .evaluate();
2970 lhs == rhs
2971 }
2972
2973 #[quickcheck]
2977 fn error_monad_left_identity(a: i32) -> bool {
2978 use crate::{
2979 brands::*,
2980 functions::*,
2981 };
2982 let f = |x: i32| pure::<TryThunkOkAppliedBrand<i32>, _>(x.wrapping_mul(2));
2983 let lhs =
2984 bind::<TryThunkOkAppliedBrand<i32>, _, _>(pure::<TryThunkOkAppliedBrand<i32>, _>(a), f)
2985 .evaluate();
2986 let rhs = f(a).evaluate();
2987 lhs == rhs
2988 }
2989
2990 #[quickcheck]
2992 fn error_monad_right_identity(x: i32) -> bool {
2993 use crate::{
2994 brands::*,
2995 functions::*,
2996 };
2997 let lhs = bind::<TryThunkOkAppliedBrand<i32>, _, _>(
2998 pure::<TryThunkOkAppliedBrand<i32>, _>(x),
2999 pure::<TryThunkOkAppliedBrand<i32>, _>,
3000 )
3001 .evaluate();
3002 lhs == Err(x)
3003 }
3004
3005 #[quickcheck]
3007 fn error_monad_associativity(x: i32) -> bool {
3008 use crate::{
3009 brands::*,
3010 functions::*,
3011 };
3012 let f = |a: i32| pure::<TryThunkOkAppliedBrand<i32>, _>(a.wrapping_add(1));
3013 let g = |a: i32| pure::<TryThunkOkAppliedBrand<i32>, _>(a.wrapping_mul(3));
3014 let m: TryThunk<i32, i32> = TryThunk::err(x);
3015 let m2: TryThunk<i32, i32> = TryThunk::err(x);
3016 let lhs = bind::<TryThunkOkAppliedBrand<i32>, _, _>(
3017 bind::<TryThunkOkAppliedBrand<i32>, _, _>(m, f),
3018 g,
3019 )
3020 .evaluate();
3021 let rhs = bind::<TryThunkOkAppliedBrand<i32>, _, _>(m2, move |a| {
3022 bind::<TryThunkOkAppliedBrand<i32>, _, _>(f(a), g)
3023 })
3024 .evaluate();
3025 lhs == rhs
3026 }
3027
3028 #[quickcheck]
3032 fn try_thunk_semigroup_associativity(
3033 a: String,
3034 b: String,
3035 c: String,
3036 ) -> bool {
3037 use crate::classes::semigroup::append;
3038
3039 let ta: TryThunk<String, ()> = TryThunk::ok(a.clone());
3040 let tb: TryThunk<String, ()> = TryThunk::ok(b.clone());
3041 let tc: TryThunk<String, ()> = TryThunk::ok(c.clone());
3042 let ta2: TryThunk<String, ()> = TryThunk::ok(a);
3043 let tb2: TryThunk<String, ()> = TryThunk::ok(b);
3044 let tc2: TryThunk<String, ()> = TryThunk::ok(c);
3045 let lhs = append(append(ta, tb), tc).evaluate();
3046 let rhs = append(ta2, append(tb2, tc2)).evaluate();
3047 lhs == rhs
3048 }
3049
3050 #[quickcheck]
3052 fn try_thunk_monoid_left_identity(x: String) -> bool {
3053 use crate::classes::{
3054 monoid::empty,
3055 semigroup::append,
3056 };
3057
3058 let a: TryThunk<String, ()> = TryThunk::ok(x.clone());
3059 let lhs: TryThunk<String, ()> = append(empty(), a);
3060 lhs.evaluate() == Ok(x)
3061 }
3062
3063 #[quickcheck]
3065 fn try_thunk_monoid_right_identity(x: String) -> bool {
3066 use crate::classes::{
3067 monoid::empty,
3068 semigroup::append,
3069 };
3070
3071 let a: TryThunk<String, ()> = TryThunk::ok(x.clone());
3072 let rhs: TryThunk<String, ()> = append(a, empty());
3073 rhs.evaluate() == Ok(x)
3074 }
3075
3076 #[test]
3081 fn test_into_arc_try_lazy_thread_safety() {
3082 use std::{
3083 sync::{
3084 Arc,
3085 atomic::{
3086 AtomicUsize,
3087 Ordering,
3088 },
3089 },
3090 thread,
3091 };
3092
3093 let counter = Arc::new(AtomicUsize::new(0));
3094 let counter_clone = Arc::clone(&counter);
3095 let thunk: TryThunk<i32, String> = TryThunk::new(move || {
3096 counter_clone.fetch_add(1, Ordering::SeqCst);
3097 Ok(42)
3098 });
3099 let lazy = thunk.into_arc_try_lazy();
3100
3101 let handles: Vec<_> = (0 .. 8)
3102 .map(|_| {
3103 let lazy_clone = lazy.clone();
3104 thread::spawn(move || {
3105 assert_eq!(lazy_clone.evaluate(), Ok(&42));
3106 })
3107 })
3108 .collect();
3109
3110 assert_eq!(lazy.evaluate(), Ok(&42));
3111 for h in handles {
3112 h.join().unwrap();
3113 }
3114
3115 assert_eq!(counter.load(Ordering::SeqCst), 1);
3117 }
3118
3119 #[test]
3123 fn test_semigroup_append_first_err_short_circuits() {
3124 use {
3125 crate::classes::semigroup::append,
3126 std::cell::Cell,
3127 };
3128
3129 let counter = Cell::new(0u32);
3130 let t1: TryThunk<String, &str> = TryThunk::err("first failed");
3131 let t2: TryThunk<String, &str> = TryThunk::new(|| {
3132 counter.set(counter.get() + 1);
3133 Ok("second".to_string())
3134 });
3135 let result = append(t1, t2);
3136 assert_eq!(result.evaluate(), Err("first failed"));
3137 assert_eq!(counter.get(), 0, "second operand should not have been evaluated");
3138 }
3139
3140 #[test]
3145 fn test_semigroup_append_second_err_propagates() {
3146 use crate::classes::semigroup::append;
3147
3148 let t1: TryThunk<String, &str> = TryThunk::pure("hello".to_string());
3149 let t2: TryThunk<String, &str> = TryThunk::err("second failed");
3150 let result = append(t1, t2);
3151 assert_eq!(result.evaluate(), Err("second failed"));
3152 }
3153}