Skip to main content

fp_library/types/
try_thunk.rs

1//! Deferred, non-memoized fallible computation with higher-kinded type support.
2//!
3//! The fallible counterpart to [`Thunk`]. Each call to [`TryThunk::evaluate`] re-executes the computation and returns a [`Result`]. Supports borrowing and lifetime polymorphism.
4
5use crate::{
6	Apply,
7	brands::{TryThunkBrand, TryThunkWithErrBrand, TryThunkWithOkBrand},
8	classes::{
9		ApplyFirst, ApplySecond, Bifunctor, CloneableFn, Deferrable, Foldable, Functor, Lift,
10		MonadRec, Monoid, Pointed, Semiapplicative, Semigroup, Semimonad,
11	},
12	impl_kind,
13	kinds::*,
14	types::{Lazy, LazyConfig, Step, Thunk, TryLazy},
15};
16use fp_macros::{doc_params, doc_type_params, hm_signature};
17
18/// A deferred computation that may fail with error type `E`.
19///
20/// Like [`Thunk`], this is NOT memoized. Each [`TryThunk::evaluate`] re-executes.
21/// Unlike [`Thunk`], the result is [`Result<A, E>`].
22///
23/// ### Type Parameters
24///
25/// * `A`: The type of the value produced by the computation on success.
26/// * `E`: The type of the error produced by the computation on failure.
27///
28/// ### Fields
29///
30/// * `0`: The closure that performs the computation.
31///
32/// ### Examples
33///
34/// ```
35/// use fp_library::types::*;
36///
37/// let computation: TryThunk<i32, &str> = TryThunk::new(|| {
38///     Ok(42)
39/// });
40///
41/// match computation.evaluate() {
42///     Ok(val) => assert_eq!(val, 42),
43///     Err(_) => panic!("Should not fail"),
44/// }
45/// ```
46pub struct TryThunk<'a, A, E>(Box<dyn FnOnce() -> Result<A, E> + 'a>);
47
48impl<'a, A: 'a, E: 'a> TryThunk<'a, A, E> {
49	/// Creates a new `TryThunk` from a thunk.
50	///
51	/// ### Type Signature
52	///
53	#[hm_signature]
54	///
55	/// ### Type Parameters
56	///
57	#[doc_type_params("The type of the thunk.")]
58	///
59	/// ### Parameters
60	///
61	#[doc_params("The thunk to wrap.")]
62	///
63	/// ### Returns
64	///
65	/// A new `TryThunk` instance.
66	///
67	/// ### Examples
68	///
69	/// ```
70	/// use fp_library::types::*;
71	///
72	/// let try_thunk: TryThunk<i32, ()> = TryThunk::new(|| Ok(42));
73	/// assert_eq!(try_thunk.evaluate(), Ok(42));
74	/// ```
75	pub fn new<F>(f: F) -> Self
76	where
77		F: FnOnce() -> Result<A, E> + 'a,
78	{
79		TryThunk(Box::new(f))
80	}
81
82	/// Returns a pure value (already computed).
83	///
84	/// ### Type Signature
85	///
86	#[hm_signature]
87	///
88	/// ### Parameters
89	///
90	#[doc_params("The value to wrap.")]
91	///
92	/// ### Returns
93	///
94	/// A new `TryThunk` instance containing the value.
95	///
96	/// ### Examples
97	///
98	/// ```
99	/// use fp_library::types::*;
100	///
101	/// let try_thunk: TryThunk<i32, ()> = TryThunk::pure(42);
102	/// assert_eq!(try_thunk.evaluate(), Ok(42));
103	/// ```
104	pub fn pure(a: A) -> Self
105	where
106		A: 'a,
107	{
108		TryThunk::new(move || Ok(a))
109	}
110
111	/// Defers a computation that returns a TryThunk.
112	///
113	/// ### Type Signature
114	///
115	#[hm_signature]
116	///
117	/// ### Type Parameters
118	///
119	#[doc_type_params("The type of the thunk.")]
120	///
121	/// ### Parameters
122	///
123	#[doc_params("The thunk that returns a `TryThunk`.")]
124	///
125	/// ### Returns
126	///
127	/// A new `TryThunk` instance.
128	///
129	/// ### Examples
130	///
131	/// ```
132	/// use fp_library::types::*;
133	///
134	/// let try_thunk: TryThunk<i32, ()> = TryThunk::defer(|| TryThunk::pure(42));
135	/// assert_eq!(try_thunk.evaluate(), Ok(42));
136	/// ```
137	pub fn defer<F>(f: F) -> Self
138	where
139		F: FnOnce() -> TryThunk<'a, A, E> + 'a,
140	{
141		TryThunk::new(move || f().evaluate())
142	}
143
144	/// Alias for [`pure`](Self::pure).
145	///
146	/// Creates a successful computation.
147	///
148	/// ### Type Signature
149	///
150	#[hm_signature]
151	///
152	/// ### Parameters
153	///
154	#[doc_params("The value to wrap.")]
155	///
156	/// ### Returns
157	///
158	/// A new `TryThunk` instance containing the value.
159	///
160	/// ### Examples
161	///
162	/// ```
163	/// use fp_library::types::*;
164	///
165	/// let try_thunk: TryThunk<i32, ()> = TryThunk::ok(42);
166	/// assert_eq!(try_thunk.evaluate(), Ok(42));
167	/// ```
168	pub fn ok(a: A) -> Self
169	where
170		A: 'a,
171	{
172		Self::pure(a)
173	}
174
175	/// Returns a pure error.
176	///
177	/// ### Type Signature
178	///
179	#[hm_signature]
180	///
181	/// ### Parameters
182	///
183	#[doc_params("The error to wrap.")]
184	///
185	/// ### Returns
186	///
187	/// A new `TryThunk` instance containing the error.
188	///
189	/// ### Examples
190	///
191	/// ```
192	/// use fp_library::types::*;
193	///
194	/// let try_thunk: TryThunk<i32, &str> = TryThunk::err("error");
195	/// assert_eq!(try_thunk.evaluate(), Err("error"));
196	/// ```
197	pub fn err(e: E) -> Self
198	where
199		E: 'a,
200	{
201		TryThunk::new(move || Err(e))
202	}
203
204	/// Monadic bind: chains computations.
205	///
206	/// ### Type Signature
207	///
208	#[hm_signature]
209	///
210	/// ### Type Parameters
211	///
212	#[doc_type_params(
213		"The type of the result of the new computation.",
214		"The type of the function to apply."
215	)]
216	///
217	/// ### Parameters
218	///
219	#[doc_params("The function to apply to the result of the computation.")]
220	///
221	/// ### Returns
222	///
223	/// A new `TryThunk` instance representing the chained computation.
224	///
225	/// ### Examples
226	///
227	/// ```
228	/// use fp_library::types::*;
229	///
230	/// let try_thunk: TryThunk<i32, ()> = TryThunk::pure(21).bind(|x| TryThunk::pure(x * 2));
231	/// assert_eq!(try_thunk.evaluate(), Ok(42));
232	/// ```
233	pub fn bind<B: 'a, F>(
234		self,
235		f: F,
236	) -> TryThunk<'a, B, E>
237	where
238		F: FnOnce(A) -> TryThunk<'a, B, E> + 'a,
239	{
240		TryThunk::new(move || match (self.0)() {
241			Ok(a) => (f(a).0)(),
242			Err(e) => Err(e),
243		})
244	}
245
246	/// Functor map: transforms the result.
247	///
248	/// ### Type Signature
249	///
250	#[hm_signature]
251	///
252	/// ### Type Parameters
253	///
254	#[doc_type_params(
255		"The type of the result of the transformation.",
256		("F", "The type of the transformation function.")
257	)]
258	///
259	/// ### Parameters
260	///
261	#[doc_params("The function to apply to the result of the computation.")]
262	///
263	/// ### Returns
264	///
265	/// A new `TryThunk` instance with the transformed result.
266	///
267	/// ### Examples
268	///
269	/// ```
270	/// use fp_library::types::*;
271	///
272	/// let try_thunk: TryThunk<i32, ()> = TryThunk::pure(21).map(|x| x * 2);
273	/// assert_eq!(try_thunk.evaluate(), Ok(42));
274	/// ```
275	pub fn map<B: 'a, Func>(
276		self,
277		func: Func,
278	) -> TryThunk<'a, B, E>
279	where
280		Func: FnOnce(A) -> B + 'a,
281	{
282		TryThunk::new(move || (self.0)().map(func))
283	}
284
285	/// Map error: transforms the error.
286	///
287	/// ### Type Signature
288	///
289	#[hm_signature]
290	///
291	/// ### Type Parameters
292	///
293	#[doc_type_params("The type of the new error.", "The type of the transformation function.")]
294	///
295	/// ### Parameters
296	///
297	#[doc_params("The function to apply to the error.")]
298	///
299	/// ### Returns
300	///
301	/// A new `TryThunk` instance with the transformed error.
302	///
303	/// ### Examples
304	///
305	/// ```
306	/// use fp_library::types::*;
307	///
308	/// let try_thunk: TryThunk<i32, i32> = TryThunk::err(21).map_err(|x| x * 2);
309	/// assert_eq!(try_thunk.evaluate(), Err(42));
310	/// ```
311	pub fn map_err<E2: 'a, F>(
312		self,
313		f: F,
314	) -> TryThunk<'a, A, E2>
315	where
316		F: FnOnce(E) -> E2 + 'a,
317	{
318		TryThunk::new(move || (self.0)().map_err(f))
319	}
320
321	/// Recovers from an error.
322	///
323	/// ### Type Signature
324	///
325	#[hm_signature]
326	///
327	/// ### Type Parameters
328	///
329	#[doc_type_params("The type of the recovery function.")]
330	///
331	/// ### Parameters
332	///
333	#[doc_params("The function to apply to the error value.")]
334	///
335	/// ### Returns
336	///
337	/// A new `TryThunk` that attempts to recover from failure.
338	///
339	/// ### Examples
340	///
341	/// ```
342	/// use fp_library::types::*;
343	///
344	/// let try_thunk: TryThunk<i32, &str> = TryThunk::err("error")
345	///     .catch(|_| TryThunk::pure(42));
346	/// assert_eq!(try_thunk.evaluate(), Ok(42));
347	/// ```
348	pub fn catch<F>(
349		self,
350		f: F,
351	) -> Self
352	where
353		F: FnOnce(E) -> TryThunk<'a, A, E> + 'a,
354	{
355		TryThunk::new(move || match (self.0)() {
356			Ok(a) => Ok(a),
357			Err(e) => (f(e).0)(),
358		})
359	}
360
361	/// Forces evaluation and returns the result.
362	///
363	/// ### Type Signature
364	///
365	#[hm_signature]
366	///
367	/// ### Returns
368	///
369	/// The result of the computation.
370	///
371	/// ### Examples
372	///
373	/// ```
374	/// use fp_library::types::*;
375	///
376	/// let try_thunk: TryThunk<i32, ()> = TryThunk::pure(42);
377	/// assert_eq!(try_thunk.evaluate(), Ok(42));
378	/// ```
379	pub fn evaluate(self) -> Result<A, E> {
380		(self.0)()
381	}
382}
383
384impl<'a, A, E, Config> From<Lazy<'a, A, Config>> for TryThunk<'a, A, E>
385where
386	A: Clone + 'a,
387	E: 'a,
388	Config: LazyConfig,
389{
390	fn from(memo: Lazy<'a, A, Config>) -> Self {
391		TryThunk::new(move || Ok(memo.evaluate().clone()))
392	}
393}
394
395impl<'a, A, E, Config> From<TryLazy<'a, A, E, Config>> for TryThunk<'a, A, E>
396where
397	A: Clone + 'a,
398	E: Clone + 'a,
399	Config: LazyConfig,
400{
401	fn from(memo: TryLazy<'a, A, E, Config>) -> Self {
402		TryThunk::new(move || memo.evaluate().cloned().map_err(Clone::clone))
403	}
404}
405
406impl<'a, A: 'a, E: 'a> From<Thunk<'a, A>> for TryThunk<'a, A, E> {
407	fn from(eval: Thunk<'a, A>) -> Self {
408		TryThunk::new(move || Ok(eval.evaluate()))
409	}
410}
411
412impl<'a, A, E> Deferrable<'a> for TryThunk<'a, A, E>
413where
414	A: 'a,
415	E: 'a,
416{
417	/// Creates a `TryThunk` from a computation that produces it.
418	///
419	/// ### Type Signature
420	///
421	#[hm_signature(Deferrable)]
422	///
423	/// ### Type Parameters
424	///
425	#[doc_type_params("The type of the thunk.")]
426	///
427	/// ### Parameters
428	///
429	#[doc_params("A thunk that produces the try thunk.")]
430	///
431	/// ### Returns
432	///
433	/// The deferred try thunk.
434	///
435	/// ### Examples
436	///
437	/// ```
438	/// use fp_library::{brands::*, functions::*, types::*, classes::Deferrable};
439	///
440	/// let task: TryThunk<i32, ()> = Deferrable::defer(|| TryThunk::pure(42));
441	/// assert_eq!(task.evaluate(), Ok(42));
442	/// ```
443	fn defer<F>(f: F) -> Self
444	where
445		F: FnOnce() -> Self + 'a,
446		Self: Sized,
447	{
448		TryThunk::defer(f)
449	}
450}
451
452impl_kind! {
453	impl<E: 'static> for TryThunkWithErrBrand<E> {
454		type Of<'a, A: 'a>: 'a = TryThunk<'a, A, E>;
455	}
456}
457
458impl<E: 'static> Functor for TryThunkWithErrBrand<E> {
459	/// Maps a function over the result of a `TryThunk` computation.
460	///
461	/// ### Type Signature
462	///
463	#[hm_signature(Functor)]
464	///
465	/// ### Type Parameters
466	///
467	#[doc_type_params(
468		"The lifetime of the computation.",
469		"The type of the value inside the `TryThunk`.",
470		"The type of the result of the transformation.",
471		"The type of the transformation function."
472	)]
473	///
474	/// ### Parameters
475	///
476	#[doc_params(
477		"The function to apply to the result of the computation.",
478		"The `TryThunk` instance."
479	)]
480	///
481	/// ### Returns
482	///
483	/// A new `TryThunk` instance with the transformed result.
484	///
485	/// ### Examples
486	///
487	/// ```
488	/// use fp_library::{brands::*, functions::*, types::*};
489	///
490	/// let try_thunk: TryThunk<i32, ()> = pure::<TryThunkWithErrBrand<()>, _>(10);
491	/// let mapped = map::<TryThunkWithErrBrand<()>, _, _, _>(|x| x * 2, try_thunk);
492	/// assert_eq!(mapped.evaluate(), Ok(20));
493	/// ```
494	fn map<'a, A: 'a, B: 'a, Func>(
495		func: Func,
496		fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
497	) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>)
498	where
499		Func: Fn(A) -> B + 'a,
500	{
501		fa.map(func)
502	}
503}
504
505impl<E: 'static> Pointed for TryThunkWithErrBrand<E> {
506	/// Wraps a value in a `TryThunk` context.
507	///
508	/// ### Type Signature
509	///
510	#[hm_signature(Pointed)]
511	///
512	/// ### Type Parameters
513	///
514	#[doc_type_params("The lifetime of the computation.", "The type of the value to wrap.")]
515	///
516	/// ### Parameters
517	///
518	#[doc_params("The value to wrap.")]
519	///
520	/// ### Returns
521	///
522	/// A new `TryThunk` instance containing the value.
523	///
524	/// ### Examples
525	///
526	/// ```
527	/// use fp_library::{brands::*, functions::*, types::*};
528	///
529	/// let try_thunk: TryThunk<i32, ()> = pure::<TryThunkWithErrBrand<()>, _>(42);
530	/// assert_eq!(try_thunk.evaluate(), Ok(42));
531	/// ```
532	fn pure<'a, A: 'a>(a: A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>) {
533		TryThunk::pure(a)
534	}
535}
536
537impl<E: 'static> Lift for TryThunkWithErrBrand<E> {
538	/// Lifts a binary function into the `TryThunk` context.
539	///
540	/// ### Type Signature
541	///
542	#[hm_signature(Lift)]
543	///
544	/// ### Type Parameters
545	///
546	#[doc_type_params(
547		"The lifetime of the computation.",
548		"The type of the first value.",
549		"The type of the second value.",
550		"The type of the result.",
551		"The type of the binary function."
552	)]
553	///
554	/// ### Parameters
555	///
556	#[doc_params(
557		"The binary function to apply.",
558		"The first `TryThunk`.",
559		"The second `TryThunk`."
560	)]
561	///
562	/// ### Returns
563	///
564	/// A new `TryThunk` instance containing the result of applying the function.
565	///
566	/// ### Examples
567	///
568	/// ```
569	/// use fp_library::{brands::*, functions::*, types::*};
570	///
571	/// let eval1: TryThunk<i32, ()> = pure::<TryThunkWithErrBrand<()>, _>(10);
572	/// let eval2: TryThunk<i32, ()> = pure::<TryThunkWithErrBrand<()>, _>(20);
573	/// let result = lift2::<TryThunkWithErrBrand<()>, _, _, _, _>(|a, b| a + b, eval1, eval2);
574	/// assert_eq!(result.evaluate(), Ok(30));
575	/// ```
576	fn lift2<'a, A, B, C, Func>(
577		func: Func,
578		fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
579		fb: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
580	) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>)
581	where
582		Func: Fn(A, B) -> C + 'a,
583		A: Clone + 'a,
584		B: Clone + 'a,
585		C: 'a,
586	{
587		fa.bind(move |a| fb.map(move |b| func(a, b)))
588	}
589}
590
591impl<E: 'static> ApplyFirst for TryThunkWithErrBrand<E> {}
592impl<E: 'static> ApplySecond for TryThunkWithErrBrand<E> {}
593
594impl<E: 'static> Semiapplicative for TryThunkWithErrBrand<E> {
595	/// Applies a function wrapped in `TryThunk` to a value wrapped in `TryThunk`.
596	///
597	/// ### Type Signature
598	///
599	#[hm_signature(Semiapplicative)]
600	///
601	/// ### Type Parameters
602	///
603	#[doc_type_params(
604		"The lifetime of the computation.",
605		"The brand of the cloneable function wrapper.",
606		"The type of the input.",
607		"The type of the result."
608	)]
609	///
610	/// ### Parameters
611	///
612	#[doc_params("The `TryThunk` containing the function.", "The `TryThunk` containing the value.")]
613	///
614	/// ### Returns
615	///
616	/// A new `TryThunk` instance containing the result of applying the function.
617	///
618	/// ### Examples
619	///
620	/// ```
621	/// use fp_library::{brands::*, functions::*, types::*};
622	///
623	/// let func: TryThunk<_, ()> = pure::<TryThunkWithErrBrand<()>, _>(cloneable_fn_new::<RcFnBrand, _, _>(|x: i32| x * 2));
624	/// let val: TryThunk<_, ()> = pure::<TryThunkWithErrBrand<()>, _>(21);
625	/// let result = apply::<RcFnBrand, TryThunkWithErrBrand<()>, _, _>(func, val);
626	/// assert_eq!(result.evaluate(), Ok(42));
627	/// ```
628	fn apply<'a, FnBrand: 'a + CloneableFn, A: 'a + Clone, B: 'a>(
629		ff: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as CloneableFn>::Of<'a, A, B>>),
630		fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
631	) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
632		ff.bind(move |f| {
633			fa.map(
634				#[allow(clippy::redundant_closure)]
635				move |a| f(a),
636			)
637		})
638	}
639}
640
641impl<E: 'static> Semimonad for TryThunkWithErrBrand<E> {
642	/// Chains `TryThunk` computations.
643	///
644	/// ### Type Signature
645	///
646	#[hm_signature(Semimonad)]
647	///
648	/// ### Type Parameters
649	///
650	#[doc_type_params(
651		"The lifetime of the computation.",
652		"The type of the result of the first computation.",
653		"The type of the result of the new computation.",
654		"The type of the function to apply."
655	)]
656	///
657	/// ### Parameters
658	///
659	#[doc_params(
660		"The first `TryThunk`.",
661		"The function to apply to the result of the computation."
662	)]
663	///
664	/// ### Returns
665	///
666	/// A new `TryThunk` instance representing the chained computation.
667	///
668	/// ### Examples
669	///
670	/// ```
671	/// use fp_library::{brands::*, functions::*, types::*};
672	///
673	/// let try_thunk: TryThunk<i32, ()> = pure::<TryThunkWithErrBrand<()>, _>(10);
674	/// let result = bind::<TryThunkWithErrBrand<()>, _, _, _>(try_thunk, |x| pure::<TryThunkWithErrBrand<()>, _>(x * 2));
675	/// assert_eq!(result.evaluate(), Ok(20));
676	/// ```
677	fn bind<'a, A: 'a, B: 'a, Func>(
678		ma: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
679		func: Func,
680	) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>)
681	where
682		Func: Fn(A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) + 'a,
683	{
684		ma.bind(func)
685	}
686}
687
688impl<E: 'static> MonadRec for TryThunkWithErrBrand<E> {
689	/// Performs tail-recursive monadic computation.
690	///
691	/// ### Type Signature
692	///
693	#[hm_signature(MonadRec)]
694	///
695	/// ### Type Parameters
696	///
697	#[doc_type_params(
698		"The lifetime of the computation.",
699		"The type of the initial value and loop state.",
700		"The type of the result.",
701		"The type of the step function."
702	)]
703	///
704	/// ### Parameters
705	///
706	#[doc_params("The step function.", "The initial value.")]
707	///
708	/// ### Returns
709	///
710	/// The result of the computation.
711	///
712	/// ### Examples
713	///
714	/// ```
715	/// use fp_library::{brands::*, classes::*, functions::*, types::*};
716	///
717	/// let result = tail_rec_m::<TryThunkWithErrBrand<()>, _, _, _>(
718	///     |x| pure::<TryThunkWithErrBrand<()>, _>(if x < 1000 { Step::Loop(x + 1) } else { Step::Done(x) }),
719	///     0,
720	/// );
721	/// assert_eq!(result.evaluate(), Ok(1000));
722	/// ```
723	fn tail_rec_m<'a, A: 'a, B: 'a, F>(
724		f: F,
725		a: A,
726	) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>)
727	where
728		F: Fn(A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Step<A, B>>)
729			+ Clone
730			+ 'a,
731	{
732		TryThunk::new(move || {
733			let mut current = a;
734			loop {
735				match f(current).evaluate() {
736					Ok(Step::Loop(next)) => current = next,
737					Ok(Step::Done(res)) => break Ok(res),
738					Err(e) => break Err(e),
739				}
740			}
741		})
742	}
743}
744
745impl<E: 'static> Foldable for TryThunkWithErrBrand<E> {
746	/// Folds the `TryThunk` from the right.
747	///
748	/// ### Type Signature
749	///
750	#[hm_signature(Foldable)]
751	///
752	/// ### Type Parameters
753	///
754	#[doc_type_params(
755		"The lifetime of the computation.",
756		"The brand of the cloneable function to use.",
757		"The type of the elements in the structure.",
758		"The type of the accumulator.",
759		"The type of the folding function."
760	)]
761	///
762	/// ### Parameters
763	///
764	#[doc_params(
765		"The function to apply to each element and the accumulator.",
766		"The initial value of the accumulator.",
767		"The `TryThunk` to fold."
768	)]
769	///
770	/// ### Returns
771	///
772	/// The final accumulator value.
773	///
774	/// ### Examples
775	///
776	/// ```
777	/// use fp_library::{brands::*, functions::*, types::*};
778	///
779	/// let try_thunk: TryThunk<i32, ()> = pure::<TryThunkWithErrBrand<()>, _>(10);
780	/// let result = fold_right::<RcFnBrand, TryThunkWithErrBrand<()>, _, _, _>(|a, b| a + b, 5, try_thunk);
781	/// assert_eq!(result, 15);
782	/// ```
783	fn fold_right<'a, FnBrand, A: 'a, B: 'a, Func>(
784		func: Func,
785		initial: B,
786		fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
787	) -> B
788	where
789		Func: Fn(A, B) -> B + 'a,
790		FnBrand: CloneableFn + 'a,
791	{
792		match fa.evaluate() {
793			Ok(a) => func(a, initial),
794			Err(_) => initial,
795		}
796	}
797
798	/// Folds the `TryThunk` from the left.
799	///
800	/// ### Type Signature
801	///
802	#[hm_signature(Foldable)]
803	///
804	/// ### Type Parameters
805	///
806	#[doc_type_params(
807		"The lifetime of the computation.",
808		"The brand of the cloneable function to use.",
809		"The type of the elements in the structure.",
810		"The type of the accumulator.",
811		"The type of the folding function."
812	)]
813	///
814	/// ### Parameters
815	///
816	#[doc_params(
817		"The function to apply to the accumulator and each element.",
818		"The initial value of the accumulator.",
819		"The `TryThunk` to fold."
820	)]
821	///
822	/// ### Returns
823	///
824	/// The final accumulator value.
825	///
826	/// ### Examples
827	///
828	/// ```
829	/// use fp_library::{brands::*, functions::*, types::*};
830	///
831	/// let try_thunk: TryThunk<i32, ()> = pure::<TryThunkWithErrBrand<()>, _>(10);
832	/// let result = fold_left::<RcFnBrand, TryThunkWithErrBrand<()>, _, _, _>(|b, a| b + a, 5, try_thunk);
833	/// assert_eq!(result, 15);
834	/// ```
835	fn fold_left<'a, FnBrand, A: 'a, B: 'a, Func>(
836		func: Func,
837		initial: B,
838		fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
839	) -> B
840	where
841		Func: Fn(B, A) -> B + 'a,
842		FnBrand: CloneableFn + 'a,
843	{
844		match fa.evaluate() {
845			Ok(a) => func(initial, a),
846			Err(_) => initial,
847		}
848	}
849
850	/// Maps the value to a monoid and returns it.
851	///
852	/// ### Type Signature
853	///
854	#[hm_signature(Foldable)]
855	///
856	/// ### Type Parameters
857	///
858	#[doc_type_params(
859		"The lifetime of the computation.",
860		"The brand of the cloneable function to use.",
861		"The type of the elements in the structure.",
862		"The type of the monoid.",
863		"The type of the mapping function."
864	)]
865	///
866	/// ### Parameters
867	///
868	#[doc_params("The mapping function.", "The Thunk to fold.")]
869	///
870	/// ### Returns
871	///
872	/// The monoid value.
873	///
874	/// ### Examples
875	///
876	/// ```
877	/// use fp_library::{brands::*, functions::*, types::*};
878	///
879	/// let try_thunk: TryThunk<i32, ()> = pure::<TryThunkWithErrBrand<()>, _>(10);
880	/// let result = fold_map::<RcFnBrand, TryThunkWithErrBrand<()>, _, _, _>(|a| a.to_string(), try_thunk);
881	/// assert_eq!(result, "10");
882	/// ```
883	fn fold_map<'a, FnBrand, A: 'a, M, Func>(
884		func: Func,
885		fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
886	) -> M
887	where
888		M: Monoid + 'a,
889		Func: Fn(A) -> M + 'a,
890		FnBrand: CloneableFn + 'a,
891	{
892		match fa.evaluate() {
893			Ok(a) => func(a),
894			Err(_) => M::empty(),
895		}
896	}
897}
898
899impl<'a, A: Semigroup + 'a, E: 'a> Semigroup for TryThunk<'a, A, E> {
900	/// Combines two `TryThunk`s by combining their results.
901	///
902	/// ### Type Signature
903	///
904	#[hm_signature(Semigroup)]
905	///
906	/// ### Parameters
907	///
908	#[doc_params("The first `TryThunk`.", "The second `TryThunk`.")]
909	///
910	/// ### Returns
911	///
912	/// A new `TryThunk` containing the combined result.
913	///
914	/// ### Examples
915	///
916	/// ```
917	/// use fp_library::{brands::*, classes::*, functions::*, types::*};
918	///
919	/// let t1: TryThunk<String, ()> = pure::<TryThunkWithErrBrand<()>, _>("Hello".to_string());
920	/// let t2: TryThunk<String, ()> = pure::<TryThunkWithErrBrand<()>, _>(" World".to_string());
921	/// let t3 = append::<_>(t1, t2);
922	/// assert_eq!(t3.evaluate(), Ok("Hello World".to_string()));
923	/// ```
924	fn append(
925		a: Self,
926		b: Self,
927	) -> Self {
928		TryThunk::new(move || match (a.evaluate(), b.evaluate()) {
929			(Ok(a_val), Ok(b_val)) => Ok(Semigroup::append(a_val, b_val)),
930			(Err(e), _) => Err(e),
931			(_, Err(e)) => Err(e),
932		})
933	}
934}
935
936impl<'a, A: Monoid + 'a, E: 'a> Monoid for TryThunk<'a, A, E> {
937	/// Returns the identity `TryThunk`.
938	///
939	/// ### Type Signature
940	///
941	#[hm_signature(Monoid)]
942	///
943	/// ### Returns
944	///
945	/// A `TryThunk` producing the identity value of `A`.
946	///
947	/// ### Examples
948	///
949	/// ```
950	/// use fp_library::{classes::*, types::*};
951	///
952	/// let t: TryThunk<String, ()> = TryThunk::empty();
953	/// assert_eq!(t.evaluate(), Ok("".to_string()));
954	/// ```
955	fn empty() -> Self {
956		TryThunk::new(|| Ok(Monoid::empty()))
957	}
958}
959
960impl_kind! {
961	for TryThunkBrand {
962		type Of<'a, E: 'a, A: 'a>: 'a = TryThunk<'a, A, E>;
963	}
964}
965
966impl Bifunctor for TryThunkBrand {
967	/// Maps functions over the values in the `TryThunk`.
968	///
969	/// This method applies one function to the error value and another to the success value.
970	///
971	/// ### Type Signature
972	///
973	#[hm_signature(Bifunctor)]
974	///
975	/// ### Type Parameters
976	///
977	#[doc_type_params(
978		"The lifetime of the values.",
979		"The type of the error value.",
980		"The type of the mapped error value.",
981		"The type of the success value.",
982		"The type of the mapped success value.",
983		"The type of the function to apply to the error.",
984		"The type of the function to apply to the success."
985	)]
986	///
987	/// ### Parameters
988	///
989	#[doc_params(
990		"The function to apply to the error.",
991		"The function to apply to the success.",
992		"The `TryThunk` to map over."
993	)]
994	///
995	/// ### Returns
996	///
997	/// A new `TryThunk` containing the mapped values.
998	///
999	/// ### Examples
1000	///
1001	/// ```
1002	/// use fp_library::{brands::*, classes::bifunctor::*, functions::*, types::*};
1003	///
1004	/// let x: TryThunk<i32, i32> = TryThunk::pure(5);
1005	/// assert_eq!(bimap::<TryThunkBrand, _, _, _, _, _, _>(|e| e + 1, |s| s * 2, x).evaluate(), Ok(10));
1006	///
1007	/// let y: TryThunk<i32, i32> = TryThunk::err(5);
1008	/// assert_eq!(bimap::<TryThunkBrand, _, _, _, _, _, _>(|e| e + 1, |s| s * 2, y).evaluate(), Err(6));
1009	/// ```
1010	fn bimap<'a, A: 'a, B: 'a, C: 'a, D: 'a, F, G>(
1011		f: F,
1012		g: G,
1013		p: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, C>),
1014	) -> Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, B, D>)
1015	where
1016		F: Fn(A) -> B + 'a,
1017		G: Fn(C) -> D + 'a,
1018	{
1019		TryThunk::new(move || match p.evaluate() {
1020			Ok(c) => Ok(g(c)),
1021			Err(a) => Err(f(a)),
1022		})
1023	}
1024}
1025
1026impl_kind! {
1027	impl<A: 'static> for TryThunkWithOkBrand<A> {
1028		type Of<'a, E: 'a>: 'a = TryThunk<'a, A, E>;
1029	}
1030}
1031
1032impl<A: 'static> Functor for TryThunkWithOkBrand<A> {
1033	/// Maps a function over the error value in the `TryThunk`.
1034	///
1035	/// ### Type Signature
1036	///
1037	#[hm_signature(Functor)]
1038	///
1039	/// ### Type Parameters
1040	///
1041	#[doc_type_params(
1042		"The lifetime of the computation.",
1043		"The type of the error value inside the `TryThunk`.",
1044		"The type of the result of the transformation.",
1045		"The type of the transformation function."
1046	)]
1047	///
1048	/// ### Parameters
1049	///
1050	#[doc_params("The function to apply to the error.", "The `TryThunk` instance.")]
1051	///
1052	/// ### Returns
1053	///
1054	/// A new `TryThunk` instance with the transformed error.
1055	///
1056	/// ### Examples
1057	///
1058	/// ```
1059	/// use fp_library::{brands::*, functions::*, types::*};
1060	///
1061	/// let try_thunk: TryThunk<i32, i32> = pure::<TryThunkWithOkBrand<i32>, _>(10);
1062	/// let mapped = map::<TryThunkWithOkBrand<i32>, _, _, _>(|x| x * 2, try_thunk);
1063	/// assert_eq!(mapped.evaluate(), Err(20));
1064	/// ```
1065	fn map<'a, E: 'a, E2: 'a, Func>(
1066		func: Func,
1067		fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
1068	) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>)
1069	where
1070		Func: Fn(E) -> E2 + 'a,
1071	{
1072		fa.map_err(func)
1073	}
1074}
1075
1076impl<A: 'static> Pointed for TryThunkWithOkBrand<A> {
1077	/// Wraps a value in a `TryThunk` context (as error).
1078	///
1079	/// ### Type Signature
1080	///
1081	#[hm_signature(Pointed)]
1082	///
1083	/// ### Type Parameters
1084	///
1085	#[doc_type_params("The lifetime of the computation.", "The type of the value to wrap.")]
1086	///
1087	/// ### Parameters
1088	///
1089	#[doc_params("The value to wrap.")]
1090	///
1091	/// ### Returns
1092	///
1093	/// A new `TryThunk` instance containing the value as an error.
1094	///
1095	/// ### Examples
1096	///
1097	/// ```
1098	/// use fp_library::{brands::*, functions::*, types::*};
1099	///
1100	/// let try_thunk: TryThunk<i32, i32> = pure::<TryThunkWithOkBrand<i32>, _>(42);
1101	/// assert_eq!(try_thunk.evaluate(), Err(42));
1102	/// ```
1103	fn pure<'a, E: 'a>(e: E) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>) {
1104		TryThunk::err(e)
1105	}
1106}
1107
1108impl<A: 'static> Lift for TryThunkWithOkBrand<A> {
1109	/// Lifts a binary function into the `TryThunk` context (over error).
1110	///
1111	/// ### Type Signature
1112	///
1113	#[hm_signature(Lift)]
1114	///
1115	/// ### Type Parameters
1116	///
1117	#[doc_type_params(
1118		"The lifetime of the computation.",
1119		"The type of the first error value.",
1120		"The type of the second error value.",
1121		"The type of the result error value.",
1122		"The type of the binary function."
1123	)]
1124	///
1125	/// ### Parameters
1126	///
1127	#[doc_params(
1128		"The binary function to apply to the errors.",
1129		"The first `TryThunk`.",
1130		"The second `TryThunk`."
1131	)]
1132	///
1133	/// ### Returns
1134	///
1135	/// A new `TryThunk` instance containing the result of applying the function to the errors.
1136	///
1137	/// ### Examples
1138	///
1139	/// ```
1140	/// use fp_library::{brands::*, functions::*, types::*};
1141	///
1142	/// let eval1: TryThunk<i32, i32> = pure::<TryThunkWithOkBrand<i32>, _>(10);
1143	/// let eval2: TryThunk<i32, i32> = pure::<TryThunkWithOkBrand<i32>, _>(20);
1144	/// let result = lift2::<TryThunkWithOkBrand<i32>, _, _, _, _>(|a, b| a + b, eval1, eval2);
1145	/// assert_eq!(result.evaluate(), Err(30));
1146	/// ```
1147	fn lift2<'a, E1, E2, E3, Func>(
1148		func: Func,
1149		fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E1>),
1150		fb: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>),
1151	) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E3>)
1152	where
1153		Func: Fn(E1, E2) -> E3 + 'a,
1154		E1: Clone + 'a,
1155		E2: Clone + 'a,
1156		E3: 'a,
1157	{
1158		TryThunk::new(move || match (fa.evaluate(), fb.evaluate()) {
1159			(Err(e1), Err(e2)) => Err(func(e1, e2)),
1160			(Ok(a), _) => Ok(a),
1161			(_, Ok(a)) => Ok(a),
1162		})
1163	}
1164}
1165
1166impl<A: 'static> ApplyFirst for TryThunkWithOkBrand<A> {}
1167impl<A: 'static> ApplySecond for TryThunkWithOkBrand<A> {}
1168
1169impl<A: 'static> Semiapplicative for TryThunkWithOkBrand<A> {
1170	/// Applies a function wrapped in `TryThunk` (as error) to a value wrapped in `TryThunk` (as error).
1171	///
1172	/// ### Type Signature
1173	///
1174	#[hm_signature(Semiapplicative)]
1175	///
1176	/// ### Type Parameters
1177	///
1178	#[doc_type_params(
1179		"The lifetime of the computation.",
1180		"The brand of the cloneable function wrapper.",
1181		"The type of the input error.",
1182		"The type of the result error."
1183	)]
1184	///
1185	/// ### Parameters
1186	///
1187	#[doc_params(
1188		"The `TryThunk` containing the function (in Err).",
1189		"The `TryThunk` containing the value (in Err)."
1190	)]
1191	///
1192	/// ### Returns
1193	///
1194	/// A new `TryThunk` instance containing the result of applying the function.
1195	///
1196	/// ### Examples
1197	///
1198	/// ```
1199	/// use fp_library::{brands::*, functions::*, types::*};
1200	///
1201	/// let func: TryThunk<i32, _> = pure::<TryThunkWithOkBrand<i32>, _>(cloneable_fn_new::<RcFnBrand, _, _>(|x: i32| x * 2));
1202	/// let val: TryThunk<i32, _> = pure::<TryThunkWithOkBrand<i32>, _>(21);
1203	/// let result = apply::<RcFnBrand, TryThunkWithOkBrand<i32>, _, _>(func, val);
1204	/// assert_eq!(result.evaluate(), Err(42));
1205	/// ```
1206	fn apply<'a, FnBrand: 'a + CloneableFn, E1: 'a + Clone, E2: 'a>(
1207		ff: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as CloneableFn>::Of<'a, E1, E2>>),
1208		fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E1>),
1209	) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
1210		TryThunk::new(move || match (ff.evaluate(), fa.evaluate()) {
1211			(Err(f), Err(e)) => Err(f(e)),
1212			(Ok(a), _) => Ok(a),
1213			(_, Ok(a)) => Ok(a),
1214		})
1215	}
1216}
1217
1218impl<A: 'static> Semimonad for TryThunkWithOkBrand<A> {
1219	/// Chains `TryThunk` computations (over error).
1220	///
1221	/// ### Type Signature
1222	///
1223	#[hm_signature(Semimonad)]
1224	///
1225	/// ### Type Parameters
1226	///
1227	#[doc_type_params(
1228		"The lifetime of the computation.",
1229		"The type of the result of the first computation (error).",
1230		"The type of the result of the new computation (error).",
1231		"The type of the function to apply."
1232	)]
1233	///
1234	/// ### Parameters
1235	///
1236	#[doc_params(
1237		"The first `TryThunk`.",
1238		"The function to apply to the error result of the computation."
1239	)]
1240	///
1241	/// ### Returns
1242	///
1243	/// A new `TryThunk` instance representing the chained computation.
1244	///
1245	/// ### Examples
1246	///
1247	/// ```
1248	/// use fp_library::{brands::*, functions::*, types::*};
1249	///
1250	/// let try_thunk: TryThunk<i32, i32> = pure::<TryThunkWithOkBrand<i32>, _>(10);
1251	/// let result = bind::<TryThunkWithOkBrand<i32>, _, _, _>(try_thunk, |x| pure::<TryThunkWithOkBrand<i32>, _>(x * 2));
1252	/// assert_eq!(result.evaluate(), Err(20));
1253	/// ```
1254	fn bind<'a, E1: 'a, E2: 'a, Func>(
1255		ma: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E1>),
1256		func: Func,
1257	) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>)
1258	where
1259		Func: Fn(E1) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) + 'a,
1260	{
1261		TryThunk::new(move || match ma.evaluate() {
1262			Ok(a) => Ok(a),
1263			Err(e) => func(e).evaluate(),
1264		})
1265	}
1266}
1267
1268impl<A: 'static> Foldable for TryThunkWithOkBrand<A> {
1269	/// Folds the `TryThunk` from the right (over error).
1270	///
1271	/// ### Type Signature
1272	///
1273	#[hm_signature(Foldable)]
1274	///
1275	/// ### Type Parameters
1276	///
1277	#[doc_type_params(
1278		"The lifetime of the computation.",
1279		"The brand of the cloneable function to use.",
1280		"The type of the elements in the structure.",
1281		"The type of the accumulator.",
1282		"The type of the folding function."
1283	)]
1284	///
1285	/// ### Parameters
1286	///
1287	#[doc_params(
1288		"The function to apply to each element and the accumulator.",
1289		"The initial value of the accumulator.",
1290		"The `TryThunk` to fold."
1291	)]
1292	///
1293	/// ### Returns
1294	///
1295	/// The final accumulator value.
1296	///
1297	/// ### Examples
1298	///
1299	/// ```
1300	/// use fp_library::{brands::*, functions::*, types::*};
1301	///
1302	/// let try_thunk: TryThunk<i32, i32> = pure::<TryThunkWithOkBrand<i32>, _>(10);
1303	/// let result = fold_right::<RcFnBrand, TryThunkWithOkBrand<i32>, _, _, _>(|a, b| a + b, 5, try_thunk);
1304	/// assert_eq!(result, 15);
1305	/// ```
1306	fn fold_right<'a, FnBrand, E: 'a, B: 'a, Func>(
1307		func: Func,
1308		initial: B,
1309		fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
1310	) -> B
1311	where
1312		Func: Fn(E, B) -> B + 'a,
1313		FnBrand: CloneableFn + 'a,
1314	{
1315		match fa.evaluate() {
1316			Err(e) => func(e, initial),
1317			Ok(_) => initial,
1318		}
1319	}
1320
1321	/// Folds the `TryThunk` from the left (over error).
1322	///
1323	/// ### Type Signature
1324	///
1325	#[hm_signature(Foldable)]
1326	///
1327	/// ### Type Parameters
1328	///
1329	#[doc_type_params(
1330		"The lifetime of the computation.",
1331		"The brand of the cloneable function to use.",
1332		"The type of the elements in the structure.",
1333		"The type of the accumulator.",
1334		"The type of the folding function."
1335	)]
1336	///
1337	/// ### Parameters
1338	///
1339	#[doc_params(
1340		"The function to apply to the accumulator and each element.",
1341		"The initial value of the accumulator.",
1342		"The `TryThunk` to fold."
1343	)]
1344	///
1345	/// ### Returns
1346	///
1347	/// The final accumulator value.
1348	///
1349	/// ### Examples
1350	///
1351	/// ```
1352	/// use fp_library::{brands::*, functions::*, types::*};
1353	///
1354	/// let try_thunk: TryThunk<i32, i32> = pure::<TryThunkWithOkBrand<i32>, _>(10);
1355	/// let result = fold_left::<RcFnBrand, TryThunkWithOkBrand<i32>, _, _, _>(|b, a| b + a, 5, try_thunk);
1356	/// assert_eq!(result, 15);
1357	/// ```
1358	fn fold_left<'a, FnBrand, E: 'a, B: 'a, Func>(
1359		func: Func,
1360		initial: B,
1361		fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
1362	) -> B
1363	where
1364		Func: Fn(B, E) -> B + 'a,
1365		FnBrand: CloneableFn + 'a,
1366	{
1367		match fa.evaluate() {
1368			Err(e) => func(initial, e),
1369			Ok(_) => initial,
1370		}
1371	}
1372
1373	/// Maps the value to a monoid and returns it (over error).
1374	///
1375	/// ### Type Signature
1376	///
1377	#[hm_signature(Foldable)]
1378	///
1379	/// ### Type Parameters
1380	///
1381	#[doc_type_params(
1382		"The lifetime of the computation.",
1383		"The brand of the cloneable function to use.",
1384		"The type of the elements in the structure.",
1385		"The type of the monoid.",
1386		"The type of the mapping function."
1387	)]
1388	///
1389	/// ### Parameters
1390	///
1391	#[doc_params("The mapping function.", "The Thunk to fold.")]
1392	///
1393	/// ### Returns
1394	///
1395	/// The monoid value.
1396	///
1397	/// ### Examples
1398	///
1399	/// ```
1400	/// use fp_library::{brands::*, functions::*, types::*};
1401	///
1402	/// let try_thunk: TryThunk<i32, i32> = pure::<TryThunkWithOkBrand<i32>, _>(10);
1403	/// let result = fold_map::<RcFnBrand, TryThunkWithOkBrand<i32>, _, _, _>(|a| a.to_string(), try_thunk);
1404	/// assert_eq!(result, "10");
1405	/// ```
1406	fn fold_map<'a, FnBrand, E: 'a, M, Func>(
1407		func: Func,
1408		fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
1409	) -> M
1410	where
1411		M: Monoid + 'a,
1412		Func: Fn(E) -> M + 'a,
1413		FnBrand: CloneableFn + 'a,
1414	{
1415		match fa.evaluate() {
1416			Err(e) => func(e),
1417			Ok(_) => M::empty(),
1418		}
1419	}
1420}
1421
1422#[cfg(test)]
1423mod tests {
1424	use super::*;
1425
1426	/// Tests success path.
1427	///
1428	/// Verifies that `TryThunk::pure` creates a successful computation.
1429	#[test]
1430	fn test_success() {
1431		let try_thunk: TryThunk<i32, ()> = TryThunk::pure(42);
1432		assert_eq!(try_thunk.evaluate(), Ok(42));
1433	}
1434
1435	/// Tests failure path.
1436	///
1437	/// Verifies that `TryThunk::err` creates a failed computation.
1438	#[test]
1439	fn test_failure() {
1440		let try_thunk: TryThunk<i32, &str> = TryThunk::err("error");
1441		assert_eq!(try_thunk.evaluate(), Err("error"));
1442	}
1443
1444	/// Tests `TryThunk::map`.
1445	///
1446	/// Verifies that `map` transforms the success value.
1447	#[test]
1448	fn test_map() {
1449		let try_thunk: TryThunk<i32, ()> = TryThunk::pure(21).map(|x| x * 2);
1450		assert_eq!(try_thunk.evaluate(), Ok(42));
1451	}
1452
1453	/// Tests `TryThunk::map_err`.
1454	///
1455	/// Verifies that `map_err` transforms the error value.
1456	#[test]
1457	fn test_map_err() {
1458		let try_thunk: TryThunk<i32, i32> = TryThunk::err(21).map_err(|x| x * 2);
1459		assert_eq!(try_thunk.evaluate(), Err(42));
1460	}
1461
1462	/// Tests `TryThunk::bind`.
1463	///
1464	/// Verifies that `bind` chains computations.
1465	#[test]
1466	fn test_bind() {
1467		let try_thunk: TryThunk<i32, ()> = TryThunk::pure(21).bind(|x| TryThunk::pure(x * 2));
1468		assert_eq!(try_thunk.evaluate(), Ok(42));
1469	}
1470
1471	/// Tests borrowing in TryThunk.
1472	///
1473	/// Verifies that `TryThunk` can capture references.
1474	#[test]
1475	fn test_borrowing() {
1476		let x = 42;
1477		let try_thunk: TryThunk<&i32, ()> = TryThunk::new(|| Ok(&x));
1478		assert_eq!(try_thunk.evaluate(), Ok(&42));
1479	}
1480
1481	/// Tests `TryThunk::bind` failure propagation.
1482	///
1483	/// Verifies that if the first computation fails, the second one is not executed.
1484	#[test]
1485	fn test_bind_failure() {
1486		let try_thunk = TryThunk::<i32, &str>::err("error").bind(|x| TryThunk::pure(x * 2));
1487		assert_eq!(try_thunk.evaluate(), Err("error"));
1488	}
1489
1490	/// Tests `TryThunk::map` failure propagation.
1491	///
1492	/// Verifies that `map` is not executed if the computation fails.
1493	#[test]
1494	fn test_map_failure() {
1495		let try_thunk = TryThunk::<i32, &str>::err("error").map(|x| x * 2);
1496		assert_eq!(try_thunk.evaluate(), Err("error"));
1497	}
1498
1499	/// Tests `TryThunk::map_err` success propagation.
1500	///
1501	/// Verifies that `map_err` is not executed if the computation succeeds.
1502	#[test]
1503	fn test_map_err_success() {
1504		let try_thunk = TryThunk::<i32, &str>::pure(42).map_err(|_| "new error");
1505		assert_eq!(try_thunk.evaluate(), Ok(42));
1506	}
1507
1508	/// Tests `From<Lazy>`.
1509	#[test]
1510	fn test_try_thunk_from_memo() {
1511		use crate::types::RcLazy;
1512		let memo = RcLazy::new(|| 42);
1513		let try_thunk: TryThunk<i32, ()> = TryThunk::from(memo);
1514		assert_eq!(try_thunk.evaluate(), Ok(42));
1515	}
1516
1517	/// Tests `From<TryLazy>`.
1518	#[test]
1519	fn test_try_thunk_from_try_memo() {
1520		use crate::types::RcTryLazy;
1521		let memo = RcTryLazy::new(|| Ok(42));
1522		let try_thunk: TryThunk<i32, ()> = TryThunk::from(memo);
1523		assert_eq!(try_thunk.evaluate(), Ok(42));
1524	}
1525
1526	/// Tests `Thunk::into_try`.
1527	///
1528	/// Verifies that `From<Thunk>` converts a `Thunk` into a `TryThunk` that succeeds.
1529	#[test]
1530	fn test_try_thunk_from_eval() {
1531		let eval = Thunk::pure(42);
1532		let try_thunk: TryThunk<i32, ()> = TryThunk::from(eval);
1533		assert_eq!(try_thunk.evaluate(), Ok(42));
1534	}
1535
1536	/// Tests `TryThunk::defer`.
1537	#[test]
1538	fn test_defer() {
1539		let try_thunk: TryThunk<i32, ()> = TryThunk::defer(|| TryThunk::pure(42));
1540		assert_eq!(try_thunk.evaluate(), Ok(42));
1541	}
1542
1543	/// Tests `TryThunk::catch`.
1544	///
1545	/// Verifies that `catch` recovers from failure.
1546	#[test]
1547	fn test_catch() {
1548		let try_thunk: TryThunk<i32, &str> = TryThunk::err("error").catch(|_| TryThunk::pure(42));
1549		assert_eq!(try_thunk.evaluate(), Ok(42));
1550	}
1551
1552	/// Tests `TryThunkWithErrBrand` (Functor over Success).
1553	#[test]
1554	fn test_try_thunk_with_err_brand() {
1555		use crate::{brands::*, functions::*};
1556
1557		// Functor (map over success)
1558		let try_thunk: TryThunk<i32, ()> = TryThunk::pure(10);
1559		let mapped = map::<TryThunkWithErrBrand<()>, _, _, _>(|x| x * 2, try_thunk);
1560		assert_eq!(mapped.evaluate(), Ok(20));
1561
1562		// Pointed (pure -> ok)
1563		let try_thunk: TryThunk<i32, ()> = pure::<TryThunkWithErrBrand<()>, _>(42);
1564		assert_eq!(try_thunk.evaluate(), Ok(42));
1565
1566		// Semimonad (bind over success)
1567		let try_thunk: TryThunk<i32, ()> = TryThunk::pure(10);
1568		let bound = bind::<TryThunkWithErrBrand<()>, _, _, _>(try_thunk, |x| {
1569			pure::<TryThunkWithErrBrand<()>, _>(x * 2)
1570		});
1571		assert_eq!(bound.evaluate(), Ok(20));
1572
1573		// Foldable (fold over success)
1574		let try_thunk: TryThunk<i32, ()> = TryThunk::pure(10);
1575		let folded = fold_right::<RcFnBrand, TryThunkWithErrBrand<()>, _, _, _>(
1576			|x, acc| x + acc,
1577			5,
1578			try_thunk,
1579		);
1580		assert_eq!(folded, 15);
1581	}
1582
1583	/// Tests `Bifunctor` for `TryThunkBrand`.
1584	#[test]
1585	fn test_bifunctor() {
1586		use crate::{brands::*, classes::bifunctor::*};
1587
1588		let x: TryThunk<i32, i32> = TryThunk::pure(5);
1589		assert_eq!(
1590			bimap::<TryThunkBrand, _, _, _, _, _, _>(|e| e + 1, |s| s * 2, x).evaluate(),
1591			Ok(10)
1592		);
1593
1594		let y: TryThunk<i32, i32> = TryThunk::err(5);
1595		assert_eq!(
1596			bimap::<TryThunkBrand, _, _, _, _, _, _>(|e| e + 1, |s| s * 2, y).evaluate(),
1597			Err(6)
1598		);
1599	}
1600
1601	/// Tests `TryThunkWithOkBrand` (Functor over Error).
1602	#[test]
1603	fn test_try_thunk_with_ok_brand() {
1604		use crate::{brands::*, functions::*};
1605
1606		// Functor (map over error)
1607		let try_thunk: TryThunk<i32, i32> = TryThunk::err(10);
1608		let mapped = map::<TryThunkWithOkBrand<i32>, _, _, _>(|x| x * 2, try_thunk);
1609		assert_eq!(mapped.evaluate(), Err(20));
1610
1611		// Pointed (pure -> err)
1612		let try_thunk: TryThunk<i32, i32> = pure::<TryThunkWithOkBrand<i32>, _>(42);
1613		assert_eq!(try_thunk.evaluate(), Err(42));
1614
1615		// Semimonad (bind over error)
1616		let try_thunk: TryThunk<i32, i32> = TryThunk::err(10);
1617		let bound = bind::<TryThunkWithOkBrand<i32>, _, _, _>(try_thunk, |x| {
1618			pure::<TryThunkWithOkBrand<i32>, _>(x * 2)
1619		});
1620		assert_eq!(bound.evaluate(), Err(20));
1621
1622		// Foldable (fold over error)
1623		let try_thunk: TryThunk<i32, i32> = TryThunk::err(10);
1624		let folded = fold_right::<RcFnBrand, TryThunkWithOkBrand<i32>, _, _, _>(
1625			|x, acc| x + acc,
1626			5,
1627			try_thunk,
1628		);
1629		assert_eq!(folded, 15);
1630	}
1631}