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