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