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				Bifoldable,
19				Bifunctor,
20				CloneableFn,
21				Deferrable,
22				Foldable,
23				FoldableWithIndex,
24				Functor,
25				FunctorWithIndex,
26				LazyConfig,
27				Lift,
28				MonadRec,
29				Monoid,
30				Pointed,
31				Semiapplicative,
32				Semigroup,
33				Semimonad,
34				TryLazyConfig,
35				WithIndex,
36			},
37			impl_kind,
38			kinds::*,
39			types::{
40				ArcTryLazy,
41				Lazy,
42				RcTryLazy,
43				Thunk,
44				TryLazy,
45				TrySendThunk,
46				TryTrampoline,
47			},
48		},
49		core::ops::ControlFlow,
50		fp_macros::*,
51		std::fmt,
52	};
53
54	/// A deferred computation that may fail with error type `E`.
55	///
56	/// This is [`Thunk<'a, Result<A, E>>`] with ergonomic combinators for error handling.
57	/// Like [`Thunk`], this is NOT memoized. Each [`TryThunk::evaluate`] re-executes.
58	/// Unlike [`Thunk`], the result is [`Result<A, E>`].
59	#[document_type_parameters(
60		"The lifetime of the computation.",
61		"The type of the value produced by the computation on success.",
62		"The type of the error produced by the computation on failure."
63	)]
64	///
65	/// ### Higher-Kinded Type Representation
66	///
67	/// This type has multiple higher-kinded representations:
68	/// - [`TryThunkBrand`](crate::brands::TryThunkBrand): fully polymorphic over both error and success types (bifunctor).
69	/// - [`TryThunkErrAppliedBrand<E>`](crate::brands::TryThunkErrAppliedBrand): the error type is fixed, polymorphic over the success type (functor over `Ok`).
70	/// - [`TryThunkOkAppliedBrand<A>`](crate::brands::TryThunkOkAppliedBrand): the success type is fixed, polymorphic over the error type (functor over `Err`).
71	///
72	/// ### When to Use
73	///
74	/// Use `TryThunk` for lightweight fallible deferred computation with full HKT support.
75	/// It is not stack-safe for deep [`bind`](TryThunk::bind) chains. For stack-safe fallible
76	/// recursion, use [`TryTrampoline`](crate::types::TryTrampoline). For memoized fallible
77	/// computation, use [`TryLazy`](crate::types::TryLazy).
78	///
79	/// ### Algebraic Properties
80	///
81	/// `TryThunk` forms a monad over the success type `A` (with `E` fixed):
82	/// - `TryThunk::pure(a).bind(f).evaluate() == f(a).evaluate()` (left identity).
83	/// - `thunk.bind(TryThunk::pure).evaluate() == thunk.evaluate()` (right identity).
84	/// - `thunk.bind(f).bind(g).evaluate() == thunk.bind(|a| f(a).bind(g)).evaluate()` (associativity).
85	///
86	/// On the error channel, `bind` short-circuits: if the computation produces `Err(e)`,
87	/// the continuation `f` is never called.
88	///
89	/// ### Stack Safety
90	///
91	/// `TryThunk::bind` chains are **not** stack-safe. Each nested [`bind`](TryThunk::bind)
92	/// adds a frame to the call stack, so sufficiently deep chains will cause a stack overflow.
93	/// For stack-safe fallible recursion, use [`TryTrampoline`](crate::types::TryTrampoline).
94	///
95	/// ### Limitations
96	///
97	/// **Cannot implement `Traversable`**: `TryThunk` wraps a `FnOnce` closure, which cannot be
98	/// cloned because `FnOnce` is consumed when called. The [`Traversable`](crate::classes::Traversable)
99	/// trait requires `Clone` bounds on the result type, making it fundamentally incompatible
100	/// with `TryThunk`'s design. This mirrors the same limitation on [`Thunk`].
101	pub struct TryThunk<'a, A, E>(
102		/// The internal `Thunk` wrapping a `Result`.
103		Thunk<'a, Result<A, E>>,
104	);
105
106	#[document_type_parameters(
107		"The lifetime of the computation.",
108		"The type of the success value.",
109		"The type of the error value."
110	)]
111	#[document_parameters("The `TryThunk` instance.")]
112	impl<'a, A: 'a, E: 'a> TryThunk<'a, A, E> {
113		/// Creates a new `TryThunk` from a thunk.
114		#[document_signature]
115		///
116		#[document_parameters("The thunk to wrap.")]
117		///
118		#[document_returns("A new `TryThunk` instance.")]
119		///
120		#[document_examples]
121		///
122		/// ```
123		/// use fp_library::types::*;
124		///
125		/// let try_thunk: TryThunk<i32, ()> = TryThunk::new(|| Ok(42));
126		/// assert_eq!(try_thunk.evaluate(), Ok(42));
127		/// ```
128		#[inline]
129		pub fn new(f: impl FnOnce() -> Result<A, E> + 'a) -> Self {
130			TryThunk(Thunk::new(f))
131		}
132
133		/// Returns a pure value (already computed).
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::pure(42);
146		/// assert_eq!(try_thunk.evaluate(), Ok(42));
147		/// ```
148		#[inline]
149		pub fn pure(a: A) -> Self {
150			TryThunk(Thunk::pure(Ok(a)))
151		}
152
153		/// Defers a computation that returns a TryThunk.
154		#[document_signature]
155		///
156		#[document_parameters("The thunk that returns a `TryThunk`.")]
157		///
158		#[document_returns("A new `TryThunk` instance.")]
159		///
160		#[document_examples]
161		///
162		/// ```
163		/// use fp_library::types::*;
164		///
165		/// let try_thunk: TryThunk<i32, ()> = TryThunk::defer(|| TryThunk::ok(42));
166		/// assert_eq!(try_thunk.evaluate(), Ok(42));
167		/// ```
168		#[inline]
169		pub fn defer(f: impl FnOnce() -> TryThunk<'a, A, E> + 'a) -> Self {
170			TryThunk(Thunk::defer(move || f().0))
171		}
172
173		/// Alias for [`pure`](Self::pure), provided for readability.
174		///
175		/// Both `TryThunk::ok(x)` and `TryThunk::pure(x)` produce the same result: a
176		/// deferred computation that succeeds with `x`. The `ok` variant mirrors the
177		/// `Result::Ok` constructor name, making intent clearer when working directly
178		/// with `TryThunk` values rather than through HKT abstractions.
179		#[document_signature]
180		///
181		#[document_parameters("The value to wrap.")]
182		///
183		#[document_returns("A new `TryThunk` instance containing the value.")]
184		///
185		#[document_examples]
186		///
187		/// ```
188		/// use fp_library::types::*;
189		///
190		/// let try_thunk: TryThunk<i32, ()> = TryThunk::ok(42);
191		/// assert_eq!(try_thunk.evaluate(), Ok(42));
192		/// ```
193		#[inline]
194		pub fn ok(a: A) -> Self {
195			TryThunk(Thunk::pure(Ok(a)))
196		}
197
198		/// Returns a pure error.
199		#[document_signature]
200		///
201		#[document_parameters("The error to wrap.")]
202		///
203		#[document_returns("A new `TryThunk` instance containing the error.")]
204		///
205		#[document_examples]
206		///
207		/// ```
208		/// use fp_library::types::*;
209		///
210		/// let try_thunk: TryThunk<i32, &str> = TryThunk::err("error");
211		/// assert_eq!(try_thunk.evaluate(), Err("error"));
212		/// ```
213		#[inline]
214		pub fn err(e: E) -> Self {
215			TryThunk(Thunk::pure(Err(e)))
216		}
217
218		/// Monadic bind: chains computations.
219		#[document_signature]
220		///
221		#[document_type_parameters("The type of the result of the new computation.")]
222		///
223		#[document_parameters("The function to apply to the result of the computation.")]
224		///
225		#[document_returns("A new `TryThunk` instance representing the chained computation.")]
226		///
227		#[document_examples]
228		///
229		/// ```
230		/// use fp_library::types::*;
231		///
232		/// let try_thunk: TryThunk<i32, ()> = TryThunk::ok(21).bind(|x| TryThunk::ok(x * 2));
233		/// assert_eq!(try_thunk.evaluate(), Ok(42));
234		/// ```
235		#[inline]
236		pub fn bind<B: 'a>(
237			self,
238			f: impl FnOnce(A) -> TryThunk<'a, B, E> + 'a,
239		) -> TryThunk<'a, B, E> {
240			TryThunk(self.0.bind(|result| match result {
241				Ok(a) => f(a).0,
242				Err(e) => Thunk::pure(Err(e)),
243			}))
244		}
245
246		/// Functor map: transforms the result.
247		#[document_signature]
248		///
249		#[document_type_parameters("The type of the result of the transformation.")]
250		///
251		#[document_parameters("The function to apply to the result of the computation.")]
252		///
253		#[document_returns("A new `TryThunk` instance with the transformed result.")]
254		///
255		#[document_examples]
256		///
257		/// ```
258		/// use fp_library::types::*;
259		///
260		/// let try_thunk: TryThunk<i32, ()> = TryThunk::ok(21).map(|x| x * 2);
261		/// assert_eq!(try_thunk.evaluate(), Ok(42));
262		/// ```
263		#[inline]
264		pub fn map<B: 'a>(
265			self,
266			func: impl FnOnce(A) -> B + 'a,
267		) -> TryThunk<'a, B, E> {
268			TryThunk(self.0.map(|result| result.map(func)))
269		}
270
271		/// Map error: transforms the error.
272		#[document_signature]
273		///
274		#[document_type_parameters("The type of the new error.")]
275		///
276		#[document_parameters("The function to apply to the error.")]
277		///
278		#[document_returns("A new `TryThunk` instance with the transformed error.")]
279		///
280		#[document_examples]
281		///
282		/// ```
283		/// use fp_library::types::*;
284		///
285		/// let try_thunk: TryThunk<i32, i32> = TryThunk::err(21).map_err(|x| x * 2);
286		/// assert_eq!(try_thunk.evaluate(), Err(42));
287		/// ```
288		#[inline]
289		pub fn map_err<E2: 'a>(
290			self,
291			f: impl FnOnce(E) -> E2 + 'a,
292		) -> TryThunk<'a, A, E2> {
293			TryThunk(self.0.map(|result| result.map_err(f)))
294		}
295
296		/// Recovers from an error.
297		#[document_signature]
298		///
299		#[document_parameters("The function to apply to the error value.")]
300		///
301		#[document_returns("A new `TryThunk` that attempts to recover from failure.")]
302		///
303		#[document_examples]
304		///
305		/// ```
306		/// use fp_library::types::*;
307		///
308		/// let try_thunk: TryThunk<i32, &str> = TryThunk::err("error").catch(|_| TryThunk::ok(42));
309		/// assert_eq!(try_thunk.evaluate(), Ok(42));
310		/// ```
311		#[inline]
312		pub fn catch(
313			self,
314			f: impl FnOnce(E) -> TryThunk<'a, A, E> + 'a,
315		) -> Self {
316			TryThunk(self.0.bind(|result| match result {
317				Ok(a) => Thunk::pure(Ok(a)),
318				Err(e) => f(e).0,
319			}))
320		}
321
322		/// Recovers from an error using a fallible recovery function that may produce a different error type.
323		///
324		/// Unlike [`catch`](TryThunk::catch), `catch_with` allows the recovery function to return a
325		/// `TryThunk` with a different error type `E2`. On success, the value is passed through
326		/// unchanged. On failure, the recovery function is applied to the error value and its result
327		/// is evaluated.
328		#[document_signature]
329		///
330		#[document_type_parameters("The error type produced by the recovery computation.")]
331		///
332		#[document_parameters("The monadic recovery function applied to the error value.")]
333		///
334		#[document_returns(
335			"A new `TryThunk` that either passes through the success value or uses the result of the recovery computation."
336		)]
337		///
338		#[document_examples]
339		///
340		/// ```
341		/// use fp_library::types::*;
342		///
343		/// let recovered: TryThunk<i32, i32> =
344		/// 	TryThunk::<i32, &str>::err("error").catch_with(|_| TryThunk::err(42));
345		/// assert_eq!(recovered.evaluate(), Err(42));
346		///
347		/// let ok: TryThunk<i32, i32> = TryThunk::<i32, &str>::ok(1).catch_with(|_| TryThunk::err(42));
348		/// assert_eq!(ok.evaluate(), Ok(1));
349		/// ```
350		#[inline]
351		pub fn catch_with<E2: 'a>(
352			self,
353			f: impl FnOnce(E) -> TryThunk<'a, A, E2> + 'a,
354		) -> TryThunk<'a, A, E2> {
355			TryThunk(Thunk::new(move || match self.evaluate() {
356				Ok(a) => Ok(a),
357				Err(e) => f(e).evaluate(),
358			}))
359		}
360
361		/// Maps both the success and error values simultaneously.
362		#[document_signature]
363		///
364		#[document_type_parameters(
365			"The type of the new success value.",
366			"The type of the new error value."
367		)]
368		///
369		#[document_parameters(
370			"The function to apply to the success value.",
371			"The function to apply to the error value."
372		)]
373		///
374		#[document_returns("A new `TryThunk` with both sides transformed.")]
375		///
376		#[document_examples]
377		///
378		/// ```
379		/// use fp_library::types::*;
380		///
381		/// let ok: TryThunk<i32, i32> = TryThunk::pure(5);
382		/// assert_eq!(ok.bimap(|x| x * 2, |e| e + 1).evaluate(), Ok(10));
383		///
384		/// let err: TryThunk<i32, i32> = TryThunk::err(5);
385		/// assert_eq!(err.bimap(|x| x * 2, |e| e + 1).evaluate(), Err(6));
386		/// ```
387		pub fn bimap<B: 'a, E2: 'a>(
388			self,
389			f: impl FnOnce(A) -> B + 'a,
390			g: impl FnOnce(E) -> E2 + 'a,
391		) -> TryThunk<'a, B, E2> {
392			TryThunk(self.0.map(|result| match result {
393				Ok(a) => Ok(f(a)),
394				Err(e) => Err(g(e)),
395			}))
396		}
397
398		/// Forces evaluation and returns the result.
399		#[document_signature]
400		///
401		#[document_returns("The result of the computation.")]
402		///
403		#[document_examples]
404		///
405		/// ```
406		/// use fp_library::types::*;
407		///
408		/// let try_thunk: TryThunk<i32, ()> = TryThunk::ok(42);
409		/// assert_eq!(try_thunk.evaluate(), Ok(42));
410		/// ```
411		#[inline]
412		pub fn evaluate(self) -> Result<A, E> {
413			self.0.evaluate()
414		}
415
416		/// Combines two `TryThunk`s, running both and combining their results.
417		///
418		/// Short-circuits on error: if `self` fails, `other` is never evaluated.
419		#[document_signature]
420		///
421		#[document_type_parameters(
422			"The type of the second computation's success value.",
423			"The type of the combined result."
424		)]
425		///
426		#[document_parameters("The second computation.", "The function to combine the results.")]
427		///
428		#[document_returns("A new `TryThunk` producing the combined result.")]
429		///
430		#[document_examples]
431		///
432		/// ```
433		/// use fp_library::types::*;
434		///
435		/// let t1: TryThunk<i32, String> = TryThunk::ok(10);
436		/// let t2: TryThunk<i32, String> = TryThunk::ok(20);
437		/// let t3 = t1.lift2(t2, |a, b| a + b);
438		/// assert_eq!(t3.evaluate(), Ok(30));
439		///
440		/// let t4: TryThunk<i32, String> = TryThunk::err("fail".to_string());
441		/// let t5: TryThunk<i32, String> = TryThunk::ok(20);
442		/// let t6 = t4.lift2(t5, |a, b| a + b);
443		/// assert_eq!(t6.evaluate(), Err("fail".to_string()));
444		/// ```
445		#[inline]
446		pub fn lift2<B: 'a, C: 'a>(
447			self,
448			other: TryThunk<'a, B, E>,
449			f: impl FnOnce(A, B) -> C + 'a,
450		) -> TryThunk<'a, C, E> {
451			self.bind(move |a| other.map(move |b| f(a, b)))
452		}
453
454		/// Sequences two `TryThunk`s, discarding the first result.
455		///
456		/// Short-circuits on error: if `self` fails, `other` is never evaluated.
457		#[document_signature]
458		///
459		#[document_type_parameters("The type of the second computation's success value.")]
460		///
461		#[document_parameters("The second computation.")]
462		///
463		#[document_returns(
464			"A new `TryThunk` that runs both computations and returns the result of the second."
465		)]
466		///
467		#[document_examples]
468		///
469		/// ```
470		/// use fp_library::types::*;
471		///
472		/// let t1: TryThunk<i32, String> = TryThunk::ok(10);
473		/// let t2: TryThunk<i32, String> = TryThunk::ok(20);
474		/// let t3 = t1.then(t2);
475		/// assert_eq!(t3.evaluate(), Ok(20));
476		///
477		/// let t4: TryThunk<i32, String> = TryThunk::err("fail".to_string());
478		/// let t5: TryThunk<i32, String> = TryThunk::ok(20);
479		/// let t6 = t4.then(t5);
480		/// assert_eq!(t6.evaluate(), Err("fail".to_string()));
481		/// ```
482		#[inline]
483		pub fn then<B: 'a>(
484			self,
485			other: TryThunk<'a, B, E>,
486		) -> TryThunk<'a, B, E> {
487			self.bind(move |_| other)
488		}
489
490		/// Converts this `TryThunk` into a memoized [`RcTryLazy`].
491		///
492		/// The resulting `RcTryLazy` will evaluate the computation on first
493		/// access and cache the result for subsequent accesses.
494		#[document_signature]
495		///
496		#[document_returns("A memoized `RcTryLazy` wrapping this computation.")]
497		///
498		#[document_examples]
499		///
500		/// ```
501		/// use fp_library::types::*;
502		///
503		/// let thunk: TryThunk<i32, ()> = TryThunk::ok(42);
504		/// let lazy: RcTryLazy<i32, ()> = thunk.into_rc_try_lazy();
505		/// assert_eq!(lazy.evaluate(), Ok(&42));
506		/// ```
507		#[inline]
508		pub fn into_rc_try_lazy(self) -> RcTryLazy<'a, A, E> {
509			RcTryLazy::from(self)
510		}
511
512		/// Evaluates this `TryThunk` and wraps the result in a thread-safe [`ArcTryLazy`].
513		///
514		/// The thunk is evaluated eagerly because its inner closure is not
515		/// `Send`. The result is stored in an `ArcTryLazy` for thread-safe sharing.
516		#[document_signature]
517		///
518		#[document_returns("A thread-safe memoized `ArcTryLazy` wrapping this computation.")]
519		///
520		#[document_examples]
521		///
522		/// ```
523		/// use fp_library::types::*;
524		///
525		/// let thunk: TryThunk<i32, ()> = TryThunk::ok(42);
526		/// let lazy: ArcTryLazy<i32, ()> = thunk.into_arc_try_lazy();
527		/// assert_eq!(lazy.evaluate(), Ok(&42));
528		/// ```
529		#[inline]
530		pub fn into_arc_try_lazy(self) -> ArcTryLazy<'a, A, E>
531		where
532			A: Send + Sync + 'a,
533			E: Send + Sync + 'a, {
534			let result = self.evaluate();
535			ArcTryLazy::new(move || result)
536		}
537	}
538
539	#[document_type_parameters(
540		"The lifetime of the computation.",
541		"The type of the computed value.",
542		"The type of the error value."
543	)]
544	#[document_parameters("The `TryThunk` to operate on.")]
545	impl<'a, A: 'a, E: 'a> TryThunk<'a, A, E> {
546		/// Creates a `TryThunk` that catches unwinds (panics), converting the
547		/// panic payload using a custom conversion function.
548		///
549		/// The closure `f` is executed when the thunk is evaluated. If `f`
550		/// panics, the panic payload is passed to `handler` to produce the
551		/// error value. If `f` returns normally, the value is wrapped in `Ok`.
552		#[document_signature]
553		///
554		#[document_parameters(
555			"The closure that might panic.",
556			"The function that converts a panic payload into the error type."
557		)]
558		///
559		#[document_returns(
560			"A new `TryThunk` instance where panics are converted to `Err(E)` via the handler."
561		)]
562		///
563		#[document_examples]
564		///
565		/// ```
566		/// use fp_library::types::*;
567		///
568		/// let thunk = TryThunk::<i32, i32>::catch_unwind_with(
569		/// 	|| {
570		/// 		if true {
571		/// 			panic!("oops")
572		/// 		}
573		/// 		42
574		/// 	},
575		/// 	|_payload| -1,
576		/// );
577		/// assert_eq!(thunk.evaluate(), Err(-1));
578		/// ```
579		pub fn catch_unwind_with(
580			f: impl FnOnce() -> A + std::panic::UnwindSafe + 'a,
581			handler: impl FnOnce(Box<dyn std::any::Any + Send>) -> E + 'a,
582		) -> Self {
583			TryThunk::new(move || std::panic::catch_unwind(f).map_err(handler))
584		}
585
586		/// Unwraps the newtype, returning the inner `Thunk<'a, Result<A, E>>`.
587		#[document_signature]
588		///
589		#[document_returns("The underlying `Thunk` that produces a `Result`.")]
590		///
591		#[document_examples]
592		///
593		/// ```
594		/// use fp_library::types::*;
595		///
596		/// let try_thunk: TryThunk<i32, ()> = TryThunk::pure(42);
597		/// let inner = try_thunk.into_inner();
598		/// assert_eq!(inner.evaluate(), Ok(42));
599		/// ```
600		pub fn into_inner(self) -> Thunk<'a, Result<A, E>> {
601			self.0
602		}
603	}
604
605	#[document_type_parameters(
606		"The lifetime of the computation.",
607		"The type of the computed value."
608	)]
609	impl<'a, A: 'a> TryThunk<'a, A, String> {
610		/// Creates a `TryThunk` that catches unwinds (panics).
611		///
612		/// The closure is executed when the thunk is evaluated. If the closure
613		/// panics, the panic payload is converted to a `String` error. If the
614		/// closure returns normally, the value is wrapped in `Ok`.
615		///
616		/// This is a convenience wrapper around [`catch_unwind_with`](TryThunk::catch_unwind_with)
617		/// that uses the default panic payload to string conversion.
618		#[document_signature]
619		///
620		#[document_parameters("The closure that might panic.")]
621		///
622		#[document_returns(
623			"A new `TryThunk` instance where panics are converted to `Err(String)`."
624		)]
625		///
626		#[document_examples]
627		///
628		/// ```
629		/// use fp_library::types::*;
630		///
631		/// let thunk = TryThunk::<i32, String>::catch_unwind(|| {
632		/// 	if true {
633		/// 		panic!("oops")
634		/// 	}
635		/// 	42
636		/// });
637		/// assert_eq!(thunk.evaluate(), Err("oops".to_string()));
638		/// ```
639		pub fn catch_unwind(f: impl FnOnce() -> A + std::panic::UnwindSafe + 'a) -> Self {
640			Self::catch_unwind_with(f, crate::utils::panic_payload_to_string)
641		}
642	}
643
644	#[document_type_parameters(
645		"The lifetime of the computation.",
646		"The type of the success value.",
647		"The type of the error value.",
648		"The memoization configuration."
649	)]
650	impl<'a, A, E, Config> From<Lazy<'a, A, Config>> for TryThunk<'a, A, E>
651	where
652		A: Clone + 'a,
653		E: 'a,
654		Config: LazyConfig,
655	{
656		#[document_signature]
657		#[document_parameters("The lazy value to convert.")]
658		#[document_returns("A new `TryThunk` instance that wraps the lazy value.")]
659		#[document_examples]
660		///
661		/// ```
662		/// use fp_library::types::*;
663		/// let lazy = Lazy::<_, RcLazyConfig>::pure(42);
664		/// let thunk: TryThunk<i32, ()> = TryThunk::from(lazy);
665		/// assert_eq!(thunk.evaluate(), Ok(42));
666		/// ```
667		fn from(memo: Lazy<'a, A, Config>) -> Self {
668			TryThunk::new(move || Ok(memo.evaluate().clone()))
669		}
670	}
671
672	#[document_type_parameters(
673		"The lifetime of the computation.",
674		"The type of the success value.",
675		"The type of the error value.",
676		"The memoization configuration."
677	)]
678	impl<'a, A, E, Config> From<TryLazy<'a, A, E, Config>> for TryThunk<'a, A, E>
679	where
680		A: Clone + 'a,
681		E: Clone + 'a,
682		Config: TryLazyConfig,
683	{
684		/// Converts a [`TryLazy`] value into a [`TryThunk`] by cloning the memoized result.
685		///
686		/// This conversion clones both the success and error values on each evaluation.
687		/// The cost depends on the [`Clone`] implementations of `A` and `E`.
688		#[document_signature]
689		#[document_parameters("The fallible lazy value to convert.")]
690		#[document_returns("A new `TryThunk` instance that wraps the fallible lazy value.")]
691		#[document_examples]
692		///
693		/// ```
694		/// use fp_library::types::*;
695		/// let lazy = TryLazy::<_, _, RcLazyConfig>::new(|| Ok::<i32, ()>(42));
696		/// let thunk = TryThunk::from(lazy);
697		/// assert_eq!(thunk.evaluate(), Ok(42));
698		/// ```
699		fn from(memo: TryLazy<'a, A, E, Config>) -> Self {
700			TryThunk::new(move || memo.evaluate().cloned().map_err(Clone::clone))
701		}
702	}
703
704	#[document_type_parameters(
705		"The lifetime of the computation.",
706		"The type of the success value.",
707		"The type of the error value."
708	)]
709	impl<'a, A: 'a, E: 'a> From<Thunk<'a, A>> for TryThunk<'a, A, E> {
710		#[document_signature]
711		#[document_parameters("The thunk to convert.")]
712		#[document_returns("A new `TryThunk` instance that wraps the thunk.")]
713		#[document_examples]
714		///
715		/// ```
716		/// use fp_library::types::*;
717		/// let thunk = Thunk::new(|| 42);
718		/// let try_thunk: TryThunk<i32, ()> = TryThunk::from(thunk);
719		/// assert_eq!(try_thunk.evaluate(), Ok(42));
720		/// ```
721		fn from(eval: Thunk<'a, A>) -> Self {
722			TryThunk(eval.map(Ok))
723		}
724	}
725
726	#[document_type_parameters(
727		"The lifetime of the computation.",
728		"The type of the success value.",
729		"The type of the error value."
730	)]
731	impl<'a, A: 'a, E: 'a> From<Result<A, E>> for TryThunk<'a, A, E> {
732		#[document_signature]
733		#[document_parameters("The result to convert.")]
734		#[document_returns("A new `TryThunk` instance that produces the result.")]
735		#[document_examples]
736		///
737		/// ```
738		/// use fp_library::types::*;
739		/// let ok_thunk: TryThunk<i32, String> = TryThunk::from(Ok(42));
740		/// assert_eq!(ok_thunk.evaluate(), Ok(42));
741		///
742		/// let err_thunk: TryThunk<i32, String> = TryThunk::from(Err("error".to_string()));
743		/// assert_eq!(err_thunk.evaluate(), Err("error".to_string()));
744		/// ```
745		fn from(result: Result<A, E>) -> Self {
746			TryThunk(Thunk::pure(result))
747		}
748	}
749
750	#[document_type_parameters("The type of the success value.", "The type of the error value.")]
751	impl<A: 'static, E: 'static> From<TryTrampoline<A, E>> for TryThunk<'static, A, E> {
752		/// Converts a [`TryTrampoline`] into a `TryThunk`.
753		///
754		/// The resulting `TryThunk` will evaluate the trampoline when forced.
755		#[document_signature]
756		#[document_parameters("The fallible trampoline to convert.")]
757		#[document_returns("A new `TryThunk` instance that evaluates the trampoline.")]
758		#[document_examples]
759		///
760		/// ```
761		/// use fp_library::types::*;
762		///
763		/// let tramp: TryTrampoline<i32, String> = TryTrampoline::ok(42);
764		/// let thunk: TryThunk<i32, String> = TryThunk::from(tramp);
765		/// assert_eq!(thunk.evaluate(), Ok(42));
766		/// ```
767		fn from(tramp: TryTrampoline<A, E>) -> Self {
768			TryThunk::new(move || tramp.evaluate())
769		}
770	}
771
772	#[document_type_parameters(
773		"The lifetime of the computation.",
774		"The type of the success value.",
775		"The type of the error value."
776	)]
777	impl<'a, A: 'a, E: 'a> From<TrySendThunk<'a, A, E>> for TryThunk<'a, A, E> {
778		/// Converts a [`TrySendThunk`] into a [`TryThunk`] by erasing the `Send` bound.
779		///
780		/// This delegates to the [`SendThunk`](crate::types::SendThunk) to
781		/// [`Thunk`] conversion, which is a zero-cost unsizing coercion: the inner
782		/// `Box<dyn FnOnce() -> Result<A, E> + Send + 'a>` is coerced to
783		/// `Box<dyn FnOnce() -> Result<A, E> + 'a>`.
784		#[document_signature]
785		#[document_parameters("The send try-thunk to convert.")]
786		#[document_returns("A `TryThunk` wrapping the same deferred computation.")]
787		#[document_examples]
788		///
789		/// ```
790		/// use fp_library::types::*;
791		/// let send_thunk: TrySendThunk<i32, ()> = TrySendThunk::pure(42);
792		/// let thunk: TryThunk<i32, ()> = TryThunk::from(send_thunk);
793		/// assert_eq!(thunk.evaluate(), Ok(42));
794		/// ```
795		fn from(send_thunk: TrySendThunk<'a, A, E>) -> Self {
796			TryThunk(Thunk::from(send_thunk.into_inner()))
797		}
798	}
799
800	#[document_type_parameters(
801		"The lifetime of the computation.",
802		"The type of the success value.",
803		"The type of the error value."
804	)]
805	impl<'a, A, E> Deferrable<'a> for TryThunk<'a, A, E>
806	where
807		A: 'a,
808		E: 'a,
809	{
810		/// Creates a `TryThunk` from a computation that produces it.
811		#[document_signature]
812		///
813		#[document_parameters("A thunk that produces the try thunk.")]
814		///
815		#[document_returns("The deferred try thunk.")]
816		///
817		#[document_examples]
818		///
819		/// ```
820		/// use fp_library::{
821		/// 	brands::*,
822		/// 	classes::Deferrable,
823		/// 	functions::*,
824		/// 	types::*,
825		/// };
826		///
827		/// let task: TryThunk<i32, ()> = Deferrable::defer(|| TryThunk::ok(42));
828		/// assert_eq!(task.evaluate(), Ok(42));
829		/// ```
830		fn defer(f: impl FnOnce() -> Self + 'a) -> Self
831		where
832			Self: Sized, {
833			TryThunk::defer(f)
834		}
835	}
836
837	impl_kind! {
838		impl<E: 'static> for TryThunkErrAppliedBrand<E> {
839			#[document_default]
840			type Of<'a, A: 'a>: 'a = TryThunk<'a, A, E>;
841		}
842	}
843
844	#[document_type_parameters("The error type.")]
845	impl<E: 'static> Functor for TryThunkErrAppliedBrand<E> {
846		/// Maps a function over the result of a `TryThunk` computation.
847		#[document_signature]
848		///
849		#[document_type_parameters(
850			"The lifetime of the computation.",
851			"The type of the value inside the `TryThunk`.",
852			"The type of the result of the transformation."
853		)]
854		///
855		#[document_parameters(
856			"The function to apply to the result of the computation.",
857			"The `TryThunk` instance."
858		)]
859		///
860		#[document_returns("A new `TryThunk` instance with the transformed result.")]
861		#[document_examples]
862		///
863		/// ```
864		/// use fp_library::{
865		/// 	brands::*,
866		/// 	functions::*,
867		/// 	types::*,
868		/// };
869		///
870		/// let try_thunk: TryThunk<i32, ()> = pure::<TryThunkErrAppliedBrand<()>, _>(10);
871		/// let mapped = map::<TryThunkErrAppliedBrand<()>, _, _>(|x| x * 2, try_thunk);
872		/// assert_eq!(mapped.evaluate(), Ok(20));
873		/// ```
874		fn map<'a, A: 'a, B: 'a>(
875			func: impl Fn(A) -> B + 'a,
876			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
877		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
878			fa.map(func)
879		}
880	}
881
882	#[document_type_parameters("The error type.")]
883	impl<E: 'static> Pointed for TryThunkErrAppliedBrand<E> {
884		/// Wraps a value in a `TryThunk` context.
885		#[document_signature]
886		///
887		#[document_type_parameters(
888			"The lifetime of the computation.",
889			"The type of the value to wrap."
890		)]
891		///
892		#[document_parameters("The value to wrap.")]
893		///
894		#[document_returns("A new `TryThunk` instance containing the value.")]
895		///
896		#[document_examples]
897		///
898		/// ```
899		/// use fp_library::{
900		/// 	brands::*,
901		/// 	functions::*,
902		/// 	types::*,
903		/// };
904		///
905		/// let try_thunk: TryThunk<i32, ()> = pure::<TryThunkErrAppliedBrand<()>, _>(42);
906		/// assert_eq!(try_thunk.evaluate(), Ok(42));
907		/// ```
908		fn pure<'a, A: 'a>(a: A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>) {
909			TryThunk::ok(a)
910		}
911	}
912
913	#[document_type_parameters("The error type.")]
914	impl<E: 'static> Lift for TryThunkErrAppliedBrand<E> {
915		/// Lifts a binary function into the `TryThunk` context.
916		#[document_signature]
917		///
918		#[document_type_parameters(
919			"The lifetime of the computation.",
920			"The type of the first value.",
921			"The type of the second value.",
922			"The type of the result."
923		)]
924		///
925		#[document_parameters(
926			"The binary function to apply.",
927			"The first `TryThunk`.",
928			"The second `TryThunk`."
929		)]
930		///
931		#[document_returns(
932			"A new `TryThunk` instance containing the result of applying the function."
933		)]
934		#[document_examples]
935		///
936		/// ```
937		/// use fp_library::{
938		/// 	brands::*,
939		/// 	functions::*,
940		/// 	types::*,
941		/// };
942		///
943		/// let eval1: TryThunk<i32, ()> = pure::<TryThunkErrAppliedBrand<()>, _>(10);
944		/// let eval2: TryThunk<i32, ()> = pure::<TryThunkErrAppliedBrand<()>, _>(20);
945		/// let result = lift2::<TryThunkErrAppliedBrand<()>, _, _, _>(|a, b| a + b, eval1, eval2);
946		/// assert_eq!(result.evaluate(), Ok(30));
947		/// ```
948		fn lift2<'a, A, B, C>(
949			func: impl Fn(A, B) -> C + 'a,
950			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
951			fb: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
952		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>)
953		where
954			A: Clone + 'a,
955			B: Clone + 'a,
956			C: 'a, {
957			fa.bind(move |a| fb.map(move |b| func(a, b)))
958		}
959	}
960
961	#[document_type_parameters("The error type.")]
962	impl<E: 'static> ApplyFirst for TryThunkErrAppliedBrand<E> {}
963
964	#[document_type_parameters("The error type.")]
965	impl<E: 'static> ApplySecond for TryThunkErrAppliedBrand<E> {}
966
967	#[document_type_parameters("The error type.")]
968	impl<E: 'static> Semiapplicative for TryThunkErrAppliedBrand<E> {
969		/// Applies a function wrapped in `TryThunk` to a value wrapped in `TryThunk`.
970		#[document_signature]
971		///
972		#[document_type_parameters(
973			"The lifetime of the computation.",
974			"The brand of the cloneable function wrapper.",
975			"The type of the input.",
976			"The type of the result."
977		)]
978		///
979		#[document_parameters(
980			"The `TryThunk` containing the function.",
981			"The `TryThunk` containing the value."
982		)]
983		///
984		#[document_returns(
985			"A new `TryThunk` instance containing the result of applying the function."
986		)]
987		#[document_examples]
988		///
989		/// ```
990		/// use fp_library::{
991		/// 	brands::*,
992		/// 	functions::*,
993		/// 	types::*,
994		/// };
995		///
996		/// let func: TryThunk<_, ()> =
997		/// 	pure::<TryThunkErrAppliedBrand<()>, _>(cloneable_fn_new::<RcFnBrand, _, _>(|x: i32| x * 2));
998		/// let val: TryThunk<_, ()> = pure::<TryThunkErrAppliedBrand<()>, _>(21);
999		/// let result = apply::<RcFnBrand, TryThunkErrAppliedBrand<()>, _, _>(func, val);
1000		/// assert_eq!(result.evaluate(), Ok(42));
1001		/// ```
1002		fn apply<'a, FnBrand: 'a + CloneableFn, A: 'a + Clone, B: 'a>(
1003			ff: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as CloneableFn>::Of<'a, A, B>>),
1004			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1005		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
1006			ff.bind(move |f| {
1007				fa.map(
1008					#[allow(clippy::redundant_closure)] // Required for move semantics
1009					move |a| f(a),
1010				)
1011			})
1012		}
1013	}
1014
1015	#[document_type_parameters("The error type.")]
1016	impl<E: 'static> Semimonad for TryThunkErrAppliedBrand<E> {
1017		/// Chains `TryThunk` computations.
1018		#[document_signature]
1019		///
1020		#[document_type_parameters(
1021			"The lifetime of the computation.",
1022			"The type of the result of the first computation.",
1023			"The type of the result of the new computation."
1024		)]
1025		///
1026		#[document_parameters(
1027			"The first `TryThunk`.",
1028			"The function to apply to the result of the computation."
1029		)]
1030		///
1031		#[document_returns("A new `TryThunk` instance representing the chained computation.")]
1032		#[document_examples]
1033		///
1034		/// ```
1035		/// use fp_library::{
1036		/// 	brands::*,
1037		/// 	functions::*,
1038		/// 	types::*,
1039		/// };
1040		///
1041		/// let try_thunk: TryThunk<i32, ()> = pure::<TryThunkErrAppliedBrand<()>, _>(10);
1042		/// let result = bind::<TryThunkErrAppliedBrand<()>, _, _>(try_thunk, |x| {
1043		/// 	pure::<TryThunkErrAppliedBrand<()>, _>(x * 2)
1044		/// });
1045		/// assert_eq!(result.evaluate(), Ok(20));
1046		/// ```
1047		fn bind<'a, A: 'a, B: 'a>(
1048			ma: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1049			func: impl Fn(A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) + 'a,
1050		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
1051			ma.bind(func)
1052		}
1053	}
1054
1055	#[document_type_parameters("The error type.")]
1056	impl<E: 'static> MonadRec for TryThunkErrAppliedBrand<E> {
1057		/// Performs tail-recursive monadic computation.
1058		#[document_signature]
1059		///
1060		#[document_type_parameters(
1061			"The lifetime of the computation.",
1062			"The type of the initial value and loop state.",
1063			"The type of the result."
1064		)]
1065		///
1066		#[document_parameters("The step function.", "The initial value.")]
1067		///
1068		#[document_returns("The result of the computation.")]
1069		///
1070		#[document_examples]
1071		///
1072		/// ```
1073		/// use {
1074		/// 	core::ops::ControlFlow,
1075		/// 	fp_library::{
1076		/// 		brands::*,
1077		/// 		classes::*,
1078		/// 		functions::*,
1079		/// 		types::*,
1080		/// 	},
1081		/// };
1082		///
1083		/// let result = tail_rec_m::<TryThunkErrAppliedBrand<()>, _, _>(
1084		/// 	|x| {
1085		/// 		pure::<TryThunkErrAppliedBrand<()>, _>(
1086		/// 			if x < 1000 { ControlFlow::Continue(x + 1) } else { ControlFlow::Break(x) },
1087		/// 		)
1088		/// 	},
1089		/// 	0,
1090		/// );
1091		/// assert_eq!(result.evaluate(), Ok(1000));
1092		/// ```
1093		fn tail_rec_m<'a, A: 'a, B: 'a>(
1094			f: impl Fn(
1095				A,
1096			)
1097				-> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, ControlFlow<B, A>>)
1098			+ 'a,
1099			a: A,
1100		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
1101			TryThunk::new(move || {
1102				let mut current = a;
1103				loop {
1104					match f(current).evaluate() {
1105						Ok(ControlFlow::Continue(next)) => current = next,
1106						Ok(ControlFlow::Break(res)) => break Ok(res),
1107						Err(e) => break Err(e),
1108					}
1109				}
1110			})
1111		}
1112	}
1113
1114	#[document_type_parameters("The error type.")]
1115	impl<E: 'static> Foldable for TryThunkErrAppliedBrand<E> {
1116		/// Folds the `TryThunk` from the right.
1117		#[document_signature]
1118		///
1119		#[document_type_parameters(
1120			"The lifetime of the computation.",
1121			"The brand of the cloneable function to use.",
1122			"The type of the elements in the structure.",
1123			"The type of the accumulator."
1124		)]
1125		///
1126		#[document_parameters(
1127			"The function to apply to each element and the accumulator.",
1128			"The initial value of the accumulator.",
1129			"The `TryThunk` to fold."
1130		)]
1131		///
1132		#[document_returns("The final accumulator value.")]
1133		#[document_examples]
1134		///
1135		/// ```
1136		/// use fp_library::{
1137		/// 	brands::*,
1138		/// 	functions::*,
1139		/// 	types::*,
1140		/// };
1141		///
1142		/// let try_thunk: TryThunk<i32, ()> = pure::<TryThunkErrAppliedBrand<()>, _>(10);
1143		/// let result =
1144		/// 	fold_right::<RcFnBrand, TryThunkErrAppliedBrand<()>, _, _>(|a, b| a + b, 5, try_thunk);
1145		/// assert_eq!(result, 15);
1146		/// ```
1147		fn fold_right<'a, FnBrand, A: 'a + Clone, B: 'a>(
1148			func: impl Fn(A, B) -> B + 'a,
1149			initial: B,
1150			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1151		) -> B
1152		where
1153			FnBrand: CloneableFn + 'a, {
1154			match fa.evaluate() {
1155				Ok(a) => func(a, initial),
1156				Err(_) => initial,
1157			}
1158		}
1159
1160		/// Folds the `TryThunk` from the left.
1161		#[document_signature]
1162		///
1163		#[document_type_parameters(
1164			"The lifetime of the computation.",
1165			"The brand of the cloneable function to use.",
1166			"The type of the elements in the structure.",
1167			"The type of the accumulator."
1168		)]
1169		///
1170		#[document_parameters(
1171			"The function to apply to the accumulator and each element.",
1172			"The initial value of the accumulator.",
1173			"The `TryThunk` to fold."
1174		)]
1175		///
1176		#[document_returns("The final accumulator value.")]
1177		#[document_examples]
1178		///
1179		/// ```
1180		/// use fp_library::{
1181		/// 	brands::*,
1182		/// 	functions::*,
1183		/// 	types::*,
1184		/// };
1185		///
1186		/// let try_thunk: TryThunk<i32, ()> = pure::<TryThunkErrAppliedBrand<()>, _>(10);
1187		/// let result =
1188		/// 	fold_left::<RcFnBrand, TryThunkErrAppliedBrand<()>, _, _>(|b, a| b + a, 5, try_thunk);
1189		/// assert_eq!(result, 15);
1190		/// ```
1191		fn fold_left<'a, FnBrand, A: 'a + Clone, B: 'a>(
1192			func: impl Fn(B, A) -> B + 'a,
1193			initial: B,
1194			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1195		) -> B
1196		where
1197			FnBrand: CloneableFn + 'a, {
1198			match fa.evaluate() {
1199				Ok(a) => func(initial, a),
1200				Err(_) => initial,
1201			}
1202		}
1203
1204		/// Maps the value to a monoid and returns it.
1205		#[document_signature]
1206		///
1207		#[document_type_parameters(
1208			"The lifetime of the computation.",
1209			"The brand of the cloneable function to use.",
1210			"The type of the elements in the structure.",
1211			"The type of the monoid."
1212		)]
1213		///
1214		#[document_parameters("The mapping function.", "The TryThunk to fold.")]
1215		///
1216		#[document_returns("The monoid value.")]
1217		///
1218		#[document_examples]
1219		///
1220		/// ```
1221		/// use fp_library::{
1222		/// 	brands::*,
1223		/// 	functions::*,
1224		/// 	types::*,
1225		/// };
1226		///
1227		/// let try_thunk: TryThunk<i32, ()> = pure::<TryThunkErrAppliedBrand<()>, _>(10);
1228		/// let result =
1229		/// 	fold_map::<RcFnBrand, TryThunkErrAppliedBrand<()>, _, _>(|a| a.to_string(), try_thunk);
1230		/// assert_eq!(result, "10");
1231		/// ```
1232		fn fold_map<'a, FnBrand, A: 'a + Clone, M>(
1233			func: impl Fn(A) -> M + 'a,
1234			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1235		) -> M
1236		where
1237			M: Monoid + 'a,
1238			FnBrand: CloneableFn + 'a, {
1239			match fa.evaluate() {
1240				Ok(a) => func(a),
1241				Err(_) => M::empty(),
1242			}
1243		}
1244	}
1245
1246	#[document_type_parameters(
1247		"The lifetime of the computation.",
1248		"The success value type.",
1249		"The error value type."
1250	)]
1251	impl<'a, A: Semigroup + 'a, E: 'a> Semigroup for TryThunk<'a, A, E> {
1252		/// Combines two `TryThunk`s by combining their results.
1253		#[document_signature]
1254		///
1255		#[document_parameters("The first `TryThunk`.", "The second `TryThunk`.")]
1256		///
1257		#[document_returns("A new `TryThunk` containing the combined result.")]
1258		///
1259		#[document_examples]
1260		///
1261		/// ```
1262		/// use fp_library::{
1263		/// 	brands::*,
1264		/// 	classes::*,
1265		/// 	functions::*,
1266		/// 	types::*,
1267		/// };
1268		///
1269		/// let t1: TryThunk<String, ()> = pure::<TryThunkErrAppliedBrand<()>, _>("Hello".to_string());
1270		/// let t2: TryThunk<String, ()> = pure::<TryThunkErrAppliedBrand<()>, _>(" World".to_string());
1271		/// let t3 = append::<_>(t1, t2);
1272		/// assert_eq!(t3.evaluate(), Ok("Hello World".to_string()));
1273		/// ```
1274		fn append(
1275			a: Self,
1276			b: Self,
1277		) -> Self {
1278			TryThunk::new(move || {
1279				let a_val = a.evaluate()?;
1280				let b_val = b.evaluate()?;
1281				Ok(Semigroup::append(a_val, b_val))
1282			})
1283		}
1284	}
1285
1286	#[document_type_parameters(
1287		"The lifetime of the computation.",
1288		"The success value type.",
1289		"The error value type."
1290	)]
1291	impl<'a, A: Monoid + 'a, E: 'a> Monoid for TryThunk<'a, A, E> {
1292		/// Returns the identity `TryThunk`.
1293		#[document_signature]
1294		///
1295		#[document_returns("A `TryThunk` producing the identity value of `A`.")]
1296		///
1297		#[document_examples]
1298		///
1299		/// ```
1300		/// use fp_library::{
1301		/// 	classes::*,
1302		/// 	types::*,
1303		/// };
1304		///
1305		/// let t: TryThunk<String, ()> = TryThunk::empty();
1306		/// assert_eq!(t.evaluate(), Ok("".to_string()));
1307		/// ```
1308		fn empty() -> Self {
1309			TryThunk(Thunk::pure(Ok(Monoid::empty())))
1310		}
1311	}
1312
1313	impl_kind! {
1314		/// HKT branding for the `TryThunk` type.
1315		///
1316		/// The type parameters for `Of` are ordered `E`, then `A` (Error, then Success).
1317		/// This follows the same convention as `ResultBrand`, matching functional
1318		/// programming expectations (like Haskell's `Either e a`) where the success
1319		/// type is the last parameter.
1320		for TryThunkBrand {
1321			type Of<'a, E: 'a, A: 'a>: 'a = TryThunk<'a, A, E>;
1322		}
1323	}
1324
1325	impl Bifunctor for TryThunkBrand {
1326		/// Maps functions over the values in the `TryThunk`.
1327		///
1328		/// This method applies one function to the error value and another to the success value.
1329		#[document_signature]
1330		///
1331		#[document_type_parameters(
1332			"The lifetime of the values.",
1333			"The type of the error value.",
1334			"The type of the mapped error value.",
1335			"The type of the success value.",
1336			"The type of the mapped success value."
1337		)]
1338		///
1339		#[document_parameters(
1340			"The function to apply to the error.",
1341			"The function to apply to the success.",
1342			"The `TryThunk` to map over."
1343		)]
1344		///
1345		#[document_returns("A new `TryThunk` containing the mapped values.")]
1346		#[document_examples]
1347		///
1348		/// ```
1349		/// use fp_library::{
1350		/// 	brands::*,
1351		/// 	classes::bifunctor::*,
1352		/// 	functions::*,
1353		/// 	types::*,
1354		/// };
1355		///
1356		/// let x: TryThunk<i32, i32> = TryThunk::ok(5);
1357		/// assert_eq!(bimap::<TryThunkBrand, _, _, _, _>(|e| e + 1, |s| s * 2, x).evaluate(), Ok(10));
1358		///
1359		/// let y: TryThunk<i32, i32> = TryThunk::err(5);
1360		/// assert_eq!(bimap::<TryThunkBrand, _, _, _, _>(|e| e + 1, |s| s * 2, y).evaluate(), Err(6));
1361		/// ```
1362		fn bimap<'a, A: 'a, B: 'a, C: 'a, D: 'a>(
1363			f: impl Fn(A) -> B + 'a,
1364			g: impl Fn(C) -> D + 'a,
1365			p: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, C>),
1366		) -> Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, B, D>) {
1367			TryThunk(p.0.map(move |result| match result {
1368				Ok(c) => Ok(g(c)),
1369				Err(a) => Err(f(a)),
1370			}))
1371		}
1372	}
1373
1374	impl Bifoldable for TryThunkBrand {
1375		/// Folds a `TryThunk` using two step functions, right-associatively.
1376		///
1377		/// Dispatches to `f` for error values and `g` for success values.
1378		/// The thunk is evaluated to determine which branch to fold.
1379		#[document_signature]
1380		///
1381		#[document_type_parameters(
1382			"The lifetime of the values.",
1383			"The brand of the cloneable function to use.",
1384			"The error type (first position).",
1385			"The success type (second position).",
1386			"The accumulator type."
1387		)]
1388		///
1389		#[document_parameters(
1390			"The step function applied to the error value.",
1391			"The step function applied to the success value.",
1392			"The initial accumulator.",
1393			"The `TryThunk` to fold."
1394		)]
1395		///
1396		#[document_returns("`f(e, z)` for `Err(e)`, or `g(a, z)` for `Ok(a)`.")]
1397		#[document_examples]
1398		///
1399		/// ```
1400		/// use fp_library::{
1401		/// 	brands::*,
1402		/// 	functions::*,
1403		/// 	types::*,
1404		/// };
1405		///
1406		/// assert_eq!(
1407		/// 	bi_fold_right::<RcFnBrand, TryThunkBrand, _, _, _>(
1408		/// 		|e: i32, acc| acc - e,
1409		/// 		|s: i32, acc| acc + s,
1410		/// 		10,
1411		/// 		TryThunk::err(3),
1412		/// 	),
1413		/// 	7
1414		/// );
1415		/// assert_eq!(
1416		/// 	bi_fold_right::<RcFnBrand, TryThunkBrand, _, _, _>(
1417		/// 		|e: i32, acc| acc - e,
1418		/// 		|s: i32, acc| acc + s,
1419		/// 		10,
1420		/// 		TryThunk::ok(5),
1421		/// 	),
1422		/// 	15
1423		/// );
1424		/// ```
1425		fn bi_fold_right<'a, FnBrand: CloneableFn + 'a, A: 'a + Clone, B: 'a + Clone, C: 'a>(
1426			f: impl Fn(A, C) -> C + 'a,
1427			g: impl Fn(B, C) -> C + 'a,
1428			z: C,
1429			p: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
1430		) -> C {
1431			match p.evaluate() {
1432				Err(a) => f(a, z),
1433				Ok(b) => g(b, z),
1434			}
1435		}
1436
1437		/// Folds a `TryThunk` using two step functions, left-associatively.
1438		///
1439		/// Dispatches to `f` for error values and `g` for success values.
1440		/// The thunk is evaluated to determine which branch to fold.
1441		#[document_signature]
1442		///
1443		#[document_type_parameters(
1444			"The lifetime of the values.",
1445			"The brand of the cloneable function to use.",
1446			"The error type (first position).",
1447			"The success type (second position).",
1448			"The accumulator type."
1449		)]
1450		///
1451		#[document_parameters(
1452			"The step function applied to the error value.",
1453			"The step function applied to the success value.",
1454			"The initial accumulator.",
1455			"The `TryThunk` to fold."
1456		)]
1457		///
1458		#[document_returns("`f(z, e)` for `Err(e)`, or `g(z, a)` for `Ok(a)`.")]
1459		#[document_examples]
1460		///
1461		/// ```
1462		/// use fp_library::{
1463		/// 	brands::*,
1464		/// 	functions::*,
1465		/// 	types::*,
1466		/// };
1467		///
1468		/// assert_eq!(
1469		/// 	bi_fold_left::<RcFnBrand, TryThunkBrand, _, _, _>(
1470		/// 		|acc, e: i32| acc - e,
1471		/// 		|acc, s: i32| acc + s,
1472		/// 		10,
1473		/// 		TryThunk::err(3),
1474		/// 	),
1475		/// 	7
1476		/// );
1477		/// assert_eq!(
1478		/// 	bi_fold_left::<RcFnBrand, TryThunkBrand, _, _, _>(
1479		/// 		|acc, e: i32| acc - e,
1480		/// 		|acc, s: i32| acc + s,
1481		/// 		10,
1482		/// 		TryThunk::ok(5),
1483		/// 	),
1484		/// 	15
1485		/// );
1486		/// ```
1487		fn bi_fold_left<'a, FnBrand: CloneableFn + 'a, A: 'a + Clone, B: 'a + Clone, C: 'a>(
1488			f: impl Fn(C, A) -> C + 'a,
1489			g: impl Fn(C, B) -> C + 'a,
1490			z: C,
1491			p: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
1492		) -> C {
1493			match p.evaluate() {
1494				Err(a) => f(z, a),
1495				Ok(b) => g(z, b),
1496			}
1497		}
1498
1499		/// Maps a `TryThunk`'s value to a monoid using two functions and returns the result.
1500		///
1501		/// Dispatches to `f` for error values and `g` for success values,
1502		/// returning the monoid value.
1503		#[document_signature]
1504		///
1505		#[document_type_parameters(
1506			"The lifetime of the values.",
1507			"The brand of the cloneable function to use.",
1508			"The error type (first position).",
1509			"The success type (second position).",
1510			"The monoid type."
1511		)]
1512		///
1513		#[document_parameters(
1514			"The function mapping the error to the monoid.",
1515			"The function mapping the success to the monoid.",
1516			"The `TryThunk` to fold."
1517		)]
1518		///
1519		#[document_returns("`f(e)` for `Err(e)`, or `g(a)` for `Ok(a)`.")]
1520		#[document_examples]
1521		///
1522		/// ```
1523		/// use fp_library::{
1524		/// 	brands::*,
1525		/// 	functions::*,
1526		/// 	types::*,
1527		/// };
1528		///
1529		/// assert_eq!(
1530		/// 	bi_fold_map::<RcFnBrand, TryThunkBrand, _, _, _>(
1531		/// 		|e: i32| e.to_string(),
1532		/// 		|s: i32| s.to_string(),
1533		/// 		TryThunk::err(3),
1534		/// 	),
1535		/// 	"3".to_string()
1536		/// );
1537		/// assert_eq!(
1538		/// 	bi_fold_map::<RcFnBrand, TryThunkBrand, _, _, _>(
1539		/// 		|e: i32| e.to_string(),
1540		/// 		|s: i32| s.to_string(),
1541		/// 		TryThunk::ok(5),
1542		/// 	),
1543		/// 	"5".to_string()
1544		/// );
1545		/// ```
1546		fn bi_fold_map<'a, FnBrand: CloneableFn + 'a, A: 'a + Clone, B: 'a + Clone, M>(
1547			f: impl Fn(A) -> M + 'a,
1548			g: impl Fn(B) -> M + 'a,
1549			p: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
1550		) -> M
1551		where
1552			M: Monoid + 'a, {
1553			match p.evaluate() {
1554				Err(a) => f(a),
1555				Ok(b) => g(b),
1556			}
1557		}
1558	}
1559
1560	impl_kind! {
1561		/// HKT branding for `TryThunk` with the success type `A` fixed.
1562		///
1563		/// This is the "dual-channel" encoding for `TryThunk`:
1564		/// - [`TryThunkErrAppliedBrand<E>`] fixes the error type and is polymorphic over `Ok` values,
1565		///   giving a standard `Functor`/`Monad` that maps and chains success values.
1566		/// - `TryThunkOkAppliedBrand<A>` fixes the success type and is polymorphic over `Err` values,
1567		///   giving a `Functor`/`Monad` that maps and chains error values.
1568		///
1569		/// Together they allow the same `TryThunk<'a, A, E>` to participate in HKT abstractions
1570		/// on either channel. For example, `pure::<TryThunkErrAppliedBrand<E>, _>(x)` produces
1571		/// `Ok(x)`, while `pure::<TryThunkOkAppliedBrand<A>, _>(e)` produces `Err(e)`.
1572		impl<A: 'static> for TryThunkOkAppliedBrand<A> {
1573			#[document_default]
1574			type Of<'a, E: 'a>: 'a = TryThunk<'a, A, E>;
1575		}
1576	}
1577
1578	#[document_type_parameters("The success type.")]
1579	impl<A: 'static> Functor for TryThunkOkAppliedBrand<A> {
1580		/// Maps a function over the error value in the `TryThunk`.
1581		#[document_signature]
1582		///
1583		#[document_type_parameters(
1584			"The lifetime of the computation.",
1585			"The type of the error value inside the `TryThunk`.",
1586			"The type of the result of the transformation."
1587		)]
1588		///
1589		#[document_parameters("The function to apply to the error.", "The `TryThunk` instance.")]
1590		///
1591		#[document_returns("A new `TryThunk` instance with the transformed error.")]
1592		///
1593		#[document_examples]
1594		///
1595		/// ```
1596		/// use fp_library::{
1597		/// 	brands::*,
1598		/// 	functions::*,
1599		/// 	types::*,
1600		/// };
1601		///
1602		/// let try_thunk: TryThunk<i32, i32> = pure::<TryThunkOkAppliedBrand<i32>, _>(10);
1603		/// let mapped = map::<TryThunkOkAppliedBrand<i32>, _, _>(|x| x * 2, try_thunk);
1604		/// assert_eq!(mapped.evaluate(), Err(20));
1605		/// ```
1606		fn map<'a, E: 'a, E2: 'a>(
1607			func: impl Fn(E) -> E2 + 'a,
1608			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
1609		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
1610			fa.map_err(func)
1611		}
1612	}
1613
1614	#[document_type_parameters("The success type.")]
1615	impl<A: 'static> Pointed for TryThunkOkAppliedBrand<A> {
1616		/// Wraps a value in a `TryThunk` context (as error).
1617		#[document_signature]
1618		///
1619		#[document_type_parameters(
1620			"The lifetime of the computation.",
1621			"The type of the value to wrap."
1622		)]
1623		///
1624		#[document_parameters("The value to wrap.")]
1625		///
1626		#[document_returns("A new `TryThunk` instance containing the value as an error.")]
1627		///
1628		#[document_examples]
1629		///
1630		/// ```
1631		/// use fp_library::{
1632		/// 	brands::*,
1633		/// 	functions::*,
1634		/// 	types::*,
1635		/// };
1636		///
1637		/// let try_thunk: TryThunk<i32, i32> = pure::<TryThunkOkAppliedBrand<i32>, _>(42);
1638		/// assert_eq!(try_thunk.evaluate(), Err(42));
1639		/// ```
1640		fn pure<'a, E: 'a>(e: E) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>) {
1641			TryThunk::err(e)
1642		}
1643	}
1644
1645	#[document_type_parameters("The success type.")]
1646	impl<A: 'static> Lift for TryThunkOkAppliedBrand<A> {
1647		/// Lifts a binary function into the `TryThunk` context (over error).
1648		///
1649		/// # Evaluation strategy
1650		///
1651		/// This implementation uses **fail-last** semantics: both `fa` and `fb` are
1652		/// evaluated before the results are inspected. If either side is `Ok`, the
1653		/// `Ok` value is returned and the function is never called. This contrasts
1654		/// with the monadic [`bind`](TryThunk::bind) path, which is **fail-fast**
1655		/// and short-circuits on the first `Ok` without evaluating the second thunk.
1656		#[document_signature]
1657		///
1658		#[document_type_parameters(
1659			"The lifetime of the computation.",
1660			"The type of the first error value.",
1661			"The type of the second error value.",
1662			"The type of the result error value."
1663		)]
1664		///
1665		#[document_parameters(
1666			"The binary function to apply to the errors.",
1667			"The first `TryThunk`.",
1668			"The second `TryThunk`."
1669		)]
1670		///
1671		#[document_returns(
1672			"A new `TryThunk` instance containing the result of applying the function to the errors."
1673		)]
1674		#[document_examples]
1675		///
1676		/// ```
1677		/// use fp_library::{
1678		/// 	brands::*,
1679		/// 	functions::*,
1680		/// 	types::*,
1681		/// };
1682		///
1683		/// let eval1: TryThunk<i32, i32> = pure::<TryThunkOkAppliedBrand<i32>, _>(10);
1684		/// let eval2: TryThunk<i32, i32> = pure::<TryThunkOkAppliedBrand<i32>, _>(20);
1685		/// let result = lift2::<TryThunkOkAppliedBrand<i32>, _, _, _>(|a, b| a + b, eval1, eval2);
1686		/// assert_eq!(result.evaluate(), Err(30));
1687		/// ```
1688		fn lift2<'a, E1, E2, E3>(
1689			func: impl Fn(E1, E2) -> E3 + 'a,
1690			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E1>),
1691			fb: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>),
1692		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E3>)
1693		where
1694			E1: Clone + 'a,
1695			E2: Clone + 'a,
1696			E3: 'a, {
1697			TryThunk::new(move || match (fa.evaluate(), fb.evaluate()) {
1698				(Err(e1), Err(e2)) => Err(func(e1, e2)),
1699				(Ok(a), _) => Ok(a),
1700				(_, Ok(a)) => Ok(a),
1701			})
1702		}
1703	}
1704
1705	#[document_type_parameters("The success type.")]
1706	impl<A: 'static> ApplyFirst for TryThunkOkAppliedBrand<A> {}
1707
1708	#[document_type_parameters("The success type.")]
1709	impl<A: 'static> ApplySecond for TryThunkOkAppliedBrand<A> {}
1710
1711	#[document_type_parameters("The success type.")]
1712	impl<A: 'static> Semiapplicative for TryThunkOkAppliedBrand<A> {
1713		/// Applies a function wrapped in `TryThunk` (as error) to a value wrapped in `TryThunk` (as error).
1714		///
1715		/// # Evaluation strategy
1716		///
1717		/// This implementation uses **fail-last** semantics: both `ff` and `fa` are
1718		/// evaluated before the results are inspected. If either side is `Ok`, the
1719		/// `Ok` value is returned. This contrasts with the monadic
1720		/// [`bind`](TryThunk::bind) path, which is **fail-fast** and short-circuits
1721		/// on the first `Ok` without evaluating the second thunk.
1722		#[document_signature]
1723		///
1724		#[document_type_parameters(
1725			"The lifetime of the computation.",
1726			"The brand of the cloneable function wrapper.",
1727			"The type of the input error.",
1728			"The type of the result error."
1729		)]
1730		///
1731		#[document_parameters(
1732			"The `TryThunk` containing the function (in Err).",
1733			"The `TryThunk` containing the value (in Err)."
1734		)]
1735		///
1736		#[document_returns(
1737			"A new `TryThunk` instance containing the result of applying the function."
1738		)]
1739		#[document_examples]
1740		///
1741		/// ```
1742		/// use fp_library::{
1743		/// 	brands::*,
1744		/// 	functions::*,
1745		/// 	types::*,
1746		/// };
1747		///
1748		/// let func: TryThunk<i32, _> =
1749		/// 	pure::<TryThunkOkAppliedBrand<i32>, _>(cloneable_fn_new::<RcFnBrand, _, _>(|x: i32| x * 2));
1750		/// let val: TryThunk<i32, _> = pure::<TryThunkOkAppliedBrand<i32>, _>(21);
1751		/// let result = apply::<RcFnBrand, TryThunkOkAppliedBrand<i32>, _, _>(func, val);
1752		/// assert_eq!(result.evaluate(), Err(42));
1753		/// ```
1754		fn apply<'a, FnBrand: 'a + CloneableFn, E1: 'a + Clone, E2: 'a>(
1755			ff: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as CloneableFn>::Of<'a, E1, E2>>),
1756			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E1>),
1757		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
1758			TryThunk::new(move || match (ff.evaluate(), fa.evaluate()) {
1759				(Err(f), Err(e)) => Err(f(e)),
1760				(Ok(a), _) => Ok(a),
1761				(_, Ok(a)) => Ok(a),
1762			})
1763		}
1764	}
1765
1766	#[document_type_parameters("The success type.")]
1767	impl<A: 'static> Semimonad for TryThunkOkAppliedBrand<A> {
1768		/// Chains `TryThunk` computations (over error).
1769		#[document_signature]
1770		///
1771		#[document_type_parameters(
1772			"The lifetime of the computation.",
1773			"The type of the result of the first computation (error).",
1774			"The type of the result of the new computation (error)."
1775		)]
1776		///
1777		#[document_parameters(
1778			"The first `TryThunk`.",
1779			"The function to apply to the error result of the computation."
1780		)]
1781		///
1782		#[document_returns("A new `TryThunk` instance representing the chained computation.")]
1783		#[document_examples]
1784		///
1785		/// ```
1786		/// use fp_library::{
1787		/// 	brands::*,
1788		/// 	functions::*,
1789		/// 	types::*,
1790		/// };
1791		///
1792		/// let try_thunk: TryThunk<i32, i32> = pure::<TryThunkOkAppliedBrand<i32>, _>(10);
1793		/// let result = bind::<TryThunkOkAppliedBrand<i32>, _, _>(try_thunk, |x| {
1794		/// 	pure::<TryThunkOkAppliedBrand<i32>, _>(x * 2)
1795		/// });
1796		/// assert_eq!(result.evaluate(), Err(20));
1797		/// ```
1798		fn bind<'a, E1: 'a, E2: 'a>(
1799			ma: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E1>),
1800			func: impl Fn(E1) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) + 'a,
1801		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
1802			TryThunk::new(move || match ma.evaluate() {
1803				Ok(a) => Ok(a),
1804				Err(e) => func(e).evaluate(),
1805			})
1806		}
1807	}
1808
1809	#[document_type_parameters("The success type.")]
1810	impl<A: 'static> MonadRec for TryThunkOkAppliedBrand<A> {
1811		/// Performs tail-recursive monadic computation over the error channel.
1812		///
1813		/// The step function returns `TryThunk<A, ControlFlow<E2, E>>`. The loop
1814		/// continues while the error is `ControlFlow::Continue`, terminates with the
1815		/// final error on `ControlFlow::Break`, and short-circuits on `Ok`.
1816		#[document_signature]
1817		///
1818		#[document_type_parameters(
1819			"The lifetime of the computation.",
1820			"The type of the initial value and loop state.",
1821			"The type of the result."
1822		)]
1823		///
1824		#[document_parameters("The step function.", "The initial value.")]
1825		///
1826		#[document_returns("The result of the computation.")]
1827		///
1828		#[document_examples]
1829		///
1830		/// ```
1831		/// use {
1832		/// 	core::ops::ControlFlow,
1833		/// 	fp_library::{
1834		/// 		brands::*,
1835		/// 		classes::*,
1836		/// 		functions::*,
1837		/// 		types::*,
1838		/// 	},
1839		/// };
1840		///
1841		/// let result = tail_rec_m::<TryThunkOkAppliedBrand<i32>, _, _>(
1842		/// 	|x| {
1843		/// 		pure::<TryThunkOkAppliedBrand<i32>, _>(
1844		/// 			if x < 1000 { ControlFlow::Continue(x + 1) } else { ControlFlow::Break(x) },
1845		/// 		)
1846		/// 	},
1847		/// 	0,
1848		/// );
1849		/// assert_eq!(result.evaluate(), Err(1000));
1850		/// ```
1851		fn tail_rec_m<'a, E: 'a, E2: 'a>(
1852			f: impl Fn(
1853				E,
1854			)
1855				-> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, ControlFlow<E2, E>>)
1856			+ 'a,
1857			e: E,
1858		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
1859			TryThunk::new(move || {
1860				let mut current = e;
1861				loop {
1862					match f(current).evaluate() {
1863						Err(ControlFlow::Continue(next)) => current = next,
1864						Err(ControlFlow::Break(res)) => break Err(res),
1865						Ok(a) => break Ok(a),
1866					}
1867				}
1868			})
1869		}
1870	}
1871
1872	#[document_type_parameters("The success type.")]
1873	impl<A: 'static> Foldable for TryThunkOkAppliedBrand<A> {
1874		/// Folds the `TryThunk` from the right (over error).
1875		#[document_signature]
1876		///
1877		#[document_type_parameters(
1878			"The lifetime of the computation.",
1879			"The brand of the cloneable function to use.",
1880			"The type of the elements in the structure.",
1881			"The type of the accumulator."
1882		)]
1883		///
1884		#[document_parameters(
1885			"The function to apply to each element and the accumulator.",
1886			"The initial value of the accumulator.",
1887			"The `TryThunk` to fold."
1888		)]
1889		///
1890		#[document_returns("The final accumulator value.")]
1891		#[document_examples]
1892		///
1893		/// ```
1894		/// use fp_library::{
1895		/// 	brands::*,
1896		/// 	functions::*,
1897		/// 	types::*,
1898		/// };
1899		///
1900		/// let try_thunk: TryThunk<i32, i32> = pure::<TryThunkOkAppliedBrand<i32>, _>(10);
1901		/// let result =
1902		/// 	fold_right::<RcFnBrand, TryThunkOkAppliedBrand<i32>, _, _>(|a, b| a + b, 5, try_thunk);
1903		/// assert_eq!(result, 15);
1904		/// ```
1905		fn fold_right<'a, FnBrand, E: 'a + Clone, B: 'a>(
1906			func: impl Fn(E, B) -> B + 'a,
1907			initial: B,
1908			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
1909		) -> B
1910		where
1911			FnBrand: CloneableFn + 'a, {
1912			match fa.evaluate() {
1913				Err(e) => func(e, initial),
1914				Ok(_) => initial,
1915			}
1916		}
1917
1918		/// Folds the `TryThunk` from the left (over error).
1919		#[document_signature]
1920		///
1921		#[document_type_parameters(
1922			"The lifetime of the computation.",
1923			"The brand of the cloneable function to use.",
1924			"The type of the elements in the structure.",
1925			"The type of the accumulator."
1926		)]
1927		///
1928		#[document_parameters(
1929			"The function to apply to the accumulator and each element.",
1930			"The initial value of the accumulator.",
1931			"The `TryThunk` to fold."
1932		)]
1933		///
1934		#[document_returns("The final accumulator value.")]
1935		#[document_examples]
1936		///
1937		/// ```
1938		/// use fp_library::{
1939		/// 	brands::*,
1940		/// 	functions::*,
1941		/// 	types::*,
1942		/// };
1943		///
1944		/// let try_thunk: TryThunk<i32, i32> = pure::<TryThunkOkAppliedBrand<i32>, _>(10);
1945		/// let result =
1946		/// 	fold_left::<RcFnBrand, TryThunkOkAppliedBrand<i32>, _, _>(|b, a| b + a, 5, try_thunk);
1947		/// assert_eq!(result, 15);
1948		/// ```
1949		fn fold_left<'a, FnBrand, E: 'a + Clone, B: 'a>(
1950			func: impl Fn(B, E) -> B + 'a,
1951			initial: B,
1952			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
1953		) -> B
1954		where
1955			FnBrand: CloneableFn + 'a, {
1956			match fa.evaluate() {
1957				Err(e) => func(initial, e),
1958				Ok(_) => initial,
1959			}
1960		}
1961
1962		/// Maps the value to a monoid and returns it (over error).
1963		#[document_signature]
1964		///
1965		#[document_type_parameters(
1966			"The lifetime of the computation.",
1967			"The brand of the cloneable function to use.",
1968			"The type of the elements in the structure.",
1969			"The type of the monoid."
1970		)]
1971		///
1972		#[document_parameters("The mapping function.", "The TryThunk to fold.")]
1973		///
1974		#[document_returns("The monoid value.")]
1975		///
1976		#[document_examples]
1977		///
1978		/// ```
1979		/// use fp_library::{
1980		/// 	brands::*,
1981		/// 	functions::*,
1982		/// 	types::*,
1983		/// };
1984		///
1985		/// let try_thunk: TryThunk<i32, i32> = pure::<TryThunkOkAppliedBrand<i32>, _>(10);
1986		/// let result =
1987		/// 	fold_map::<RcFnBrand, TryThunkOkAppliedBrand<i32>, _, _>(|a| a.to_string(), try_thunk);
1988		/// assert_eq!(result, "10");
1989		/// ```
1990		fn fold_map<'a, FnBrand, E: 'a + Clone, M>(
1991			func: impl Fn(E) -> M + 'a,
1992			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
1993		) -> M
1994		where
1995			M: Monoid + 'a,
1996			FnBrand: CloneableFn + 'a, {
1997			match fa.evaluate() {
1998				Err(e) => func(e),
1999				Ok(_) => M::empty(),
2000			}
2001		}
2002	}
2003
2004	#[document_type_parameters(
2005		"The lifetime of the computation.",
2006		"The type of the success value.",
2007		"The type of the error value."
2008	)]
2009	#[document_parameters("The try-thunk to format.")]
2010	impl<'a, A, E> fmt::Debug for TryThunk<'a, A, E> {
2011		/// Formats the try-thunk without evaluating it.
2012		#[document_signature]
2013		#[document_parameters("The formatter.")]
2014		#[document_returns("The formatting result.")]
2015		#[document_examples]
2016		///
2017		/// ```
2018		/// use fp_library::types::*;
2019		/// let thunk = TryThunk::new(|| Ok::<i32, ()>(42));
2020		/// assert_eq!(format!("{:?}", thunk), "TryThunk(<unevaluated>)");
2021		/// ```
2022		fn fmt(
2023			&self,
2024			f: &mut fmt::Formatter<'_>,
2025		) -> fmt::Result {
2026			f.write_str("TryThunk(<unevaluated>)")
2027		}
2028	}
2029
2030	#[document_type_parameters("The error type.")]
2031	impl<E: 'static> WithIndex for TryThunkErrAppliedBrand<E> {
2032		type Index = ();
2033	}
2034
2035	#[document_type_parameters("The error type.")]
2036	impl<E: 'static> FunctorWithIndex for TryThunkErrAppliedBrand<E> {
2037		/// Maps a function over the success value in the `TryThunk`, providing the index `()`.
2038		#[document_signature]
2039		#[document_type_parameters(
2040			"The lifetime of the computation.",
2041			"The type of the success value inside the `TryThunk`.",
2042			"The type of the result of applying the function."
2043		)]
2044		#[document_parameters(
2045			"The function to apply to the value and its index.",
2046			"The `TryThunk` to map over."
2047		)]
2048		#[document_returns(
2049			"A new `TryThunk` containing the result of applying the function, or the original error."
2050		)]
2051		#[document_examples]
2052		///
2053		/// ```
2054		/// use fp_library::{
2055		/// 	brands::TryThunkErrAppliedBrand,
2056		/// 	classes::functor_with_index::FunctorWithIndex,
2057		/// 	types::*,
2058		/// };
2059		///
2060		/// let x: TryThunk<i32, ()> = TryThunk::pure(5);
2061		/// let y = <TryThunkErrAppliedBrand<()> as FunctorWithIndex>::map_with_index(|_, i| i * 2, x);
2062		/// assert_eq!(y.evaluate(), Ok(10));
2063		/// ```
2064		fn map_with_index<'a, A: 'a, B: 'a>(
2065			f: impl Fn((), A) -> B + 'a,
2066			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
2067		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
2068			fa.map(move |a| f((), a))
2069		}
2070	}
2071
2072	#[document_type_parameters("The error type.")]
2073	impl<E: 'static> FoldableWithIndex for TryThunkErrAppliedBrand<E> {
2074		/// Folds the `TryThunk` using a monoid, providing the index `()`.
2075		#[document_signature]
2076		#[document_type_parameters(
2077			"The lifetime of the computation.",
2078			"The type of the success value inside the `TryThunk`.",
2079			"The monoid type."
2080		)]
2081		#[document_parameters(
2082			"The function to apply to the value and its index.",
2083			"The `TryThunk` to fold."
2084		)]
2085		#[document_returns("The monoid value.")]
2086		#[document_examples]
2087		///
2088		/// ```
2089		/// use fp_library::{
2090		/// 	brands::TryThunkErrAppliedBrand,
2091		/// 	classes::foldable_with_index::FoldableWithIndex,
2092		/// 	types::*,
2093		/// };
2094		///
2095		/// let x: TryThunk<i32, ()> = TryThunk::pure(5);
2096		/// let y = <TryThunkErrAppliedBrand<()> as FoldableWithIndex>::fold_map_with_index(
2097		/// 	|_, i: i32| i.to_string(),
2098		/// 	x,
2099		/// );
2100		/// assert_eq!(y, "5".to_string());
2101		/// ```
2102		fn fold_map_with_index<'a, A: 'a + Clone, R: Monoid>(
2103			f: impl Fn((), A) -> R + 'a,
2104			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
2105		) -> R {
2106			match fa.evaluate() {
2107				Ok(a) => f((), a),
2108				Err(_) => R::empty(),
2109			}
2110		}
2111	}
2112
2113	#[document_type_parameters("The success type.")]
2114	impl<A: 'static> WithIndex for TryThunkOkAppliedBrand<A> {
2115		type Index = ();
2116	}
2117
2118	#[document_type_parameters("The success type.")]
2119	impl<A: 'static> FunctorWithIndex for TryThunkOkAppliedBrand<A> {
2120		/// Maps a function over the error value in the `TryThunk`, providing the index `()`.
2121		#[document_signature]
2122		#[document_type_parameters(
2123			"The lifetime of the computation.",
2124			"The type of the error value inside the `TryThunk`.",
2125			"The type of the result of applying the function."
2126		)]
2127		#[document_parameters(
2128			"The function to apply to the error and its index.",
2129			"The `TryThunk` to map over."
2130		)]
2131		#[document_returns(
2132			"A new `TryThunk` containing the original success or the transformed error."
2133		)]
2134		#[document_examples]
2135		///
2136		/// ```
2137		/// use fp_library::{
2138		/// 	brands::TryThunkOkAppliedBrand,
2139		/// 	classes::functor_with_index::FunctorWithIndex,
2140		/// 	types::*,
2141		/// };
2142		///
2143		/// let x: TryThunk<i32, i32> = TryThunk::err(5);
2144		/// let y = <TryThunkOkAppliedBrand<i32> as FunctorWithIndex>::map_with_index(|_, e| e * 2, x);
2145		/// assert_eq!(y.evaluate(), Err(10));
2146		/// ```
2147		fn map_with_index<'a, E: 'a, E2: 'a>(
2148			f: impl Fn((), E) -> E2 + 'a,
2149			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
2150		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
2151			fa.map_err(move |e| f((), e))
2152		}
2153	}
2154
2155	#[document_type_parameters("The success type.")]
2156	impl<A: 'static> FoldableWithIndex for TryThunkOkAppliedBrand<A> {
2157		/// Folds the `TryThunk` over the error using a monoid, providing the index `()`.
2158		#[document_signature]
2159		#[document_type_parameters(
2160			"The lifetime of the computation.",
2161			"The type of the error value inside the `TryThunk`.",
2162			"The monoid type."
2163		)]
2164		#[document_parameters(
2165			"The function to apply to the error and its index.",
2166			"The `TryThunk` to fold."
2167		)]
2168		#[document_returns("The monoid value.")]
2169		#[document_examples]
2170		///
2171		/// ```
2172		/// use fp_library::{
2173		/// 	brands::TryThunkOkAppliedBrand,
2174		/// 	classes::foldable_with_index::FoldableWithIndex,
2175		/// 	types::*,
2176		/// };
2177		///
2178		/// let x: TryThunk<i32, i32> = TryThunk::err(5);
2179		/// let y = <TryThunkOkAppliedBrand<i32> as FoldableWithIndex>::fold_map_with_index(
2180		/// 	|_, e: i32| e.to_string(),
2181		/// 	x,
2182		/// );
2183		/// assert_eq!(y, "5".to_string());
2184		/// ```
2185		fn fold_map_with_index<'a, E: 'a + Clone, R: Monoid>(
2186			f: impl Fn((), E) -> R + 'a,
2187			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
2188		) -> R {
2189			match fa.evaluate() {
2190				Err(e) => f((), e),
2191				Ok(_) => R::empty(),
2192			}
2193		}
2194	}
2195}
2196pub use inner::*;
2197
2198#[cfg(test)]
2199mod tests {
2200	use {
2201		super::*,
2202		crate::types::Thunk,
2203		quickcheck_macros::quickcheck,
2204	};
2205
2206	/// Tests success path.
2207	///
2208	/// Verifies that `TryThunk::ok` creates a successful computation.
2209	#[test]
2210	fn test_success() {
2211		let try_thunk: TryThunk<i32, ()> = TryThunk::ok(42);
2212		assert_eq!(try_thunk.evaluate(), Ok(42));
2213	}
2214
2215	/// Tests that `TryThunk::pure` creates a successful computation.
2216	#[test]
2217	fn test_pure() {
2218		let try_thunk: TryThunk<i32, ()> = TryThunk::pure(42);
2219		assert_eq!(try_thunk.evaluate(), Ok(42));
2220	}
2221
2222	/// Tests failure path.
2223	///
2224	/// Verifies that `TryThunk::err` creates a failed computation.
2225	#[test]
2226	fn test_failure() {
2227		let try_thunk: TryThunk<i32, &str> = TryThunk::err("error");
2228		assert_eq!(try_thunk.evaluate(), Err("error"));
2229	}
2230
2231	/// Tests `TryThunk::map`.
2232	///
2233	/// Verifies that `map` transforms the success value.
2234	#[test]
2235	fn test_map() {
2236		let try_thunk: TryThunk<i32, ()> = TryThunk::ok(21).map(|x| x * 2);
2237		assert_eq!(try_thunk.evaluate(), Ok(42));
2238	}
2239
2240	/// Tests `TryThunk::map_err`.
2241	///
2242	/// Verifies that `map_err` transforms the error value.
2243	#[test]
2244	fn test_map_err() {
2245		let try_thunk: TryThunk<i32, i32> = TryThunk::err(21).map_err(|x| x * 2);
2246		assert_eq!(try_thunk.evaluate(), Err(42));
2247	}
2248
2249	/// Tests `TryThunk::bind`.
2250	///
2251	/// Verifies that `bind` chains computations.
2252	#[test]
2253	fn test_bind() {
2254		let try_thunk: TryThunk<i32, ()> = TryThunk::ok(21).bind(|x| TryThunk::ok(x * 2));
2255		assert_eq!(try_thunk.evaluate(), Ok(42));
2256	}
2257
2258	/// Tests borrowing in TryThunk.
2259	///
2260	/// Verifies that `TryThunk` can capture references.
2261	#[test]
2262	fn test_borrowing() {
2263		let x = 42;
2264		let try_thunk: TryThunk<&i32, ()> = TryThunk::new(|| Ok(&x));
2265		assert_eq!(try_thunk.evaluate(), Ok(&42));
2266	}
2267
2268	/// Tests `TryThunk::bind` failure propagation.
2269	///
2270	/// Verifies that if the first computation fails, the second one is not executed.
2271	#[test]
2272	fn test_bind_failure() {
2273		let try_thunk = TryThunk::<i32, &str>::err("error").bind(|x| TryThunk::ok(x * 2));
2274		assert_eq!(try_thunk.evaluate(), Err("error"));
2275	}
2276
2277	/// Tests `TryThunk::map` failure propagation.
2278	///
2279	/// Verifies that `map` is not executed if the computation fails.
2280	#[test]
2281	fn test_map_failure() {
2282		let try_thunk = TryThunk::<i32, &str>::err("error").map(|x| x * 2);
2283		assert_eq!(try_thunk.evaluate(), Err("error"));
2284	}
2285
2286	/// Tests `TryThunk::map_err` success propagation.
2287	///
2288	/// Verifies that `map_err` is not executed if the computation succeeds.
2289	#[test]
2290	fn test_map_err_success() {
2291		let try_thunk = TryThunk::<i32, &str>::pure(42).map_err(|_| "new error");
2292		assert_eq!(try_thunk.evaluate(), Ok(42));
2293	}
2294
2295	/// Tests `From<Lazy>`.
2296	#[test]
2297	fn test_try_thunk_from_memo() {
2298		use crate::types::RcLazy;
2299		let memo = RcLazy::new(|| 42);
2300		let try_thunk: TryThunk<i32, ()> = TryThunk::from(memo);
2301		assert_eq!(try_thunk.evaluate(), Ok(42));
2302	}
2303
2304	/// Tests `From<TryLazy>`.
2305	#[test]
2306	fn test_try_thunk_from_try_memo() {
2307		use crate::types::RcTryLazy;
2308		let memo = RcTryLazy::new(|| Ok(42));
2309		let try_thunk: TryThunk<i32, ()> = TryThunk::from(memo);
2310		assert_eq!(try_thunk.evaluate(), Ok(42));
2311	}
2312
2313	/// Tests `Thunk::into_try`.
2314	///
2315	/// Verifies that `From<Thunk>` converts a `Thunk` into a `TryThunk` that succeeds.
2316	#[test]
2317	fn test_try_thunk_from_eval() {
2318		let eval = Thunk::pure(42);
2319		let try_thunk: TryThunk<i32, ()> = TryThunk::from(eval);
2320		assert_eq!(try_thunk.evaluate(), Ok(42));
2321	}
2322
2323	/// Tests `TryThunk::defer`.
2324	#[test]
2325	fn test_defer() {
2326		let try_thunk: TryThunk<i32, ()> = TryThunk::defer(|| TryThunk::ok(42));
2327		assert_eq!(try_thunk.evaluate(), Ok(42));
2328	}
2329
2330	/// Tests `TryThunk::catch`.
2331	///
2332	/// Verifies that `catch` recovers from failure.
2333	#[test]
2334	fn test_catch() {
2335		let try_thunk: TryThunk<i32, &str> = TryThunk::err("error").catch(|_| TryThunk::ok(42));
2336		assert_eq!(try_thunk.evaluate(), Ok(42));
2337	}
2338
2339	/// Tests `TryThunk::catch_with`.
2340	///
2341	/// Verifies that `catch_with` recovers from failure using a different error type.
2342	#[test]
2343	fn test_catch_with() {
2344		let recovered: TryThunk<i32, i32> =
2345			TryThunk::<i32, &str>::err("error").catch_with(|_| TryThunk::err(42));
2346		assert_eq!(recovered.evaluate(), Err(42));
2347
2348		let ok: TryThunk<i32, i32> = TryThunk::<i32, &str>::ok(1).catch_with(|_| TryThunk::err(42));
2349		assert_eq!(ok.evaluate(), Ok(1));
2350	}
2351
2352	/// Tests `TryThunkErrAppliedBrand` (Functor over Success).
2353	#[test]
2354	fn test_try_thunk_with_err_brand() {
2355		use crate::{
2356			brands::*,
2357			functions::*,
2358		};
2359
2360		// Functor (map over success)
2361		let try_thunk: TryThunk<i32, ()> = TryThunk::ok(10);
2362		let mapped = map::<TryThunkErrAppliedBrand<()>, _, _>(|x| x * 2, try_thunk);
2363		assert_eq!(mapped.evaluate(), Ok(20));
2364
2365		// Pointed (pure -> ok)
2366		let try_thunk: TryThunk<i32, ()> = pure::<TryThunkErrAppliedBrand<()>, _>(42);
2367		assert_eq!(try_thunk.evaluate(), Ok(42));
2368
2369		// Semimonad (bind over success)
2370		let try_thunk: TryThunk<i32, ()> = TryThunk::ok(10);
2371		let bound = bind::<TryThunkErrAppliedBrand<()>, _, _>(try_thunk, |x| {
2372			pure::<TryThunkErrAppliedBrand<()>, _>(x * 2)
2373		});
2374		assert_eq!(bound.evaluate(), Ok(20));
2375
2376		// Foldable (fold over success)
2377		let try_thunk: TryThunk<i32, ()> = TryThunk::ok(10);
2378		let folded = fold_right::<RcFnBrand, TryThunkErrAppliedBrand<()>, _, _>(
2379			|x, acc| x + acc,
2380			5,
2381			try_thunk,
2382		);
2383		assert_eq!(folded, 15);
2384	}
2385
2386	/// Tests `Bifunctor` for `TryThunkBrand`.
2387	#[test]
2388	fn test_bifunctor() {
2389		use crate::{
2390			brands::*,
2391			classes::bifunctor::*,
2392		};
2393
2394		let x: TryThunk<i32, i32> = TryThunk::ok(5);
2395		assert_eq!(bimap::<TryThunkBrand, _, _, _, _>(|e| e + 1, |s| s * 2, x).evaluate(), Ok(10));
2396
2397		let y: TryThunk<i32, i32> = TryThunk::err(5);
2398		assert_eq!(bimap::<TryThunkBrand, _, _, _, _>(|e| e + 1, |s| s * 2, y).evaluate(), Err(6));
2399	}
2400
2401	/// Tests `Bifoldable` for `TryThunkBrand` with `bi_fold_right`.
2402	///
2403	/// Verifies that error values are folded with `f` and success values with `g`.
2404	#[test]
2405	fn test_bifoldable_right() {
2406		use crate::{
2407			brands::*,
2408			functions::*,
2409		};
2410
2411		// Error case: f(3, 10) = 10 - 3 = 7
2412		assert_eq!(
2413			bi_fold_right::<RcFnBrand, TryThunkBrand, _, _, _>(
2414				|e: i32, acc| acc - e,
2415				|s: i32, acc| acc + s,
2416				10,
2417				TryThunk::err(3),
2418			),
2419			7
2420		);
2421
2422		// Success case: g(5, 10) = 10 + 5 = 15
2423		assert_eq!(
2424			bi_fold_right::<RcFnBrand, TryThunkBrand, _, _, _>(
2425				|e: i32, acc| acc - e,
2426				|s: i32, acc| acc + s,
2427				10,
2428				TryThunk::ok(5),
2429			),
2430			15
2431		);
2432	}
2433
2434	/// Tests `Bifoldable` for `TryThunkBrand` with `bi_fold_left`.
2435	///
2436	/// Verifies left-associative folding over both error and success values.
2437	#[test]
2438	fn test_bifoldable_left() {
2439		use crate::{
2440			brands::*,
2441			functions::*,
2442		};
2443
2444		assert_eq!(
2445			bi_fold_left::<RcFnBrand, TryThunkBrand, _, _, _>(
2446				|acc, e: i32| acc - e,
2447				|acc, s: i32| acc + s,
2448				10,
2449				TryThunk::err(3),
2450			),
2451			7
2452		);
2453
2454		assert_eq!(
2455			bi_fold_left::<RcFnBrand, TryThunkBrand, _, _, _>(
2456				|acc, e: i32| acc - e,
2457				|acc, s: i32| acc + s,
2458				10,
2459				TryThunk::ok(5),
2460			),
2461			15
2462		);
2463	}
2464
2465	/// Tests `Bifoldable` for `TryThunkBrand` with `bi_fold_map`.
2466	///
2467	/// Verifies that both error and success values can be mapped to a monoid.
2468	#[test]
2469	fn test_bifoldable_map() {
2470		use crate::{
2471			brands::*,
2472			functions::*,
2473		};
2474
2475		assert_eq!(
2476			bi_fold_map::<RcFnBrand, TryThunkBrand, _, _, _>(
2477				|e: i32| e.to_string(),
2478				|s: i32| s.to_string(),
2479				TryThunk::err(3),
2480			),
2481			"3".to_string()
2482		);
2483
2484		assert_eq!(
2485			bi_fold_map::<RcFnBrand, TryThunkBrand, _, _, _>(
2486				|e: i32| e.to_string(),
2487				|s: i32| s.to_string(),
2488				TryThunk::ok(5),
2489			),
2490			"5".to_string()
2491		);
2492	}
2493
2494	/// Tests `MonadRec` for `TryThunkOkAppliedBrand` (tail recursion over error).
2495	///
2496	/// Verifies that the loop continues on `ControlFlow::Continue` and terminates on `ControlFlow::Break`.
2497	#[test]
2498	fn test_monad_rec_ok_applied() {
2499		use {
2500			crate::{
2501				brands::*,
2502				functions::*,
2503			},
2504			core::ops::ControlFlow,
2505		};
2506
2507		let result = tail_rec_m::<TryThunkOkAppliedBrand<i32>, _, _>(
2508			|x| {
2509				pure::<TryThunkOkAppliedBrand<i32>, _>(
2510					if x < 100 { ControlFlow::Continue(x + 1) } else { ControlFlow::Break(x) },
2511				)
2512			},
2513			0,
2514		);
2515		assert_eq!(result.evaluate(), Err(100));
2516	}
2517
2518	/// Tests `MonadRec` for `TryThunkOkAppliedBrand` short-circuits on `Ok`.
2519	///
2520	/// Verifies that encountering an `Ok` value terminates the loop immediately.
2521	#[test]
2522	fn test_monad_rec_ok_applied_short_circuit() {
2523		use {
2524			crate::{
2525				brands::*,
2526				functions::*,
2527			},
2528			core::ops::ControlFlow,
2529		};
2530
2531		let result = tail_rec_m::<TryThunkOkAppliedBrand<i32>, _, _>(
2532			|x: i32| {
2533				if x == 5 {
2534					TryThunk::ok(42)
2535				} else {
2536					pure::<TryThunkOkAppliedBrand<i32>, _>(ControlFlow::<i32, i32>::Continue(x + 1))
2537				}
2538			},
2539			0,
2540		);
2541		assert_eq!(result.evaluate(), Ok(42));
2542	}
2543
2544	/// Tests `catch_unwind` on `TryThunk`.
2545	///
2546	/// Verifies that panics are caught and converted to `Err(String)`.
2547	#[test]
2548	fn test_catch_unwind() {
2549		let thunk = TryThunk::<i32, String>::catch_unwind(|| {
2550			if true {
2551				panic!("oops")
2552			}
2553			42
2554		});
2555		assert_eq!(thunk.evaluate(), Err("oops".to_string()));
2556	}
2557
2558	/// Tests `catch_unwind` on `TryThunk` with a non-panicking closure.
2559	///
2560	/// Verifies that a successful closure wraps the value in `Ok`.
2561	#[test]
2562	fn test_catch_unwind_success() {
2563		let thunk = TryThunk::<i32, String>::catch_unwind(|| 42);
2564		assert_eq!(thunk.evaluate(), Ok(42));
2565	}
2566
2567	/// Tests `TryThunkOkAppliedBrand` (Functor over Error).
2568	#[test]
2569	fn test_try_thunk_with_ok_brand() {
2570		use crate::{
2571			brands::*,
2572			functions::*,
2573		};
2574
2575		// Functor (map over error)
2576		let try_thunk: TryThunk<i32, i32> = TryThunk::err(10);
2577		let mapped = map::<TryThunkOkAppliedBrand<i32>, _, _>(|x| x * 2, try_thunk);
2578		assert_eq!(mapped.evaluate(), Err(20));
2579
2580		// Pointed (pure -> err)
2581		let try_thunk: TryThunk<i32, i32> = pure::<TryThunkOkAppliedBrand<i32>, _>(42);
2582		assert_eq!(try_thunk.evaluate(), Err(42));
2583
2584		// Semimonad (bind over error)
2585		let try_thunk: TryThunk<i32, i32> = TryThunk::err(10);
2586		let bound = bind::<TryThunkOkAppliedBrand<i32>, _, _>(try_thunk, |x| {
2587			pure::<TryThunkOkAppliedBrand<i32>, _>(x * 2)
2588		});
2589		assert_eq!(bound.evaluate(), Err(20));
2590
2591		// Foldable (fold over error)
2592		let try_thunk: TryThunk<i32, i32> = TryThunk::err(10);
2593		let folded = fold_right::<RcFnBrand, TryThunkOkAppliedBrand<i32>, _, _>(
2594			|x, acc| x + acc,
2595			5,
2596			try_thunk,
2597		);
2598		assert_eq!(folded, 15);
2599	}
2600
2601	/// Tests `From<Result>` with `Ok`.
2602	///
2603	/// Verifies that converting an `Ok` result produces a successful `TryThunk`.
2604	#[test]
2605	fn test_try_thunk_from_result_ok() {
2606		let try_thunk: TryThunk<i32, String> = TryThunk::from(Ok(42));
2607		assert_eq!(try_thunk.evaluate(), Ok(42));
2608	}
2609
2610	/// Tests `From<Result>` with `Err`.
2611	///
2612	/// Verifies that converting an `Err` result produces a failed `TryThunk`.
2613	#[test]
2614	fn test_try_thunk_from_result_err() {
2615		let try_thunk: TryThunk<i32, String> = TryThunk::from(Err("error".to_string()));
2616		assert_eq!(try_thunk.evaluate(), Err("error".to_string()));
2617	}
2618
2619	/// Tests `From<TrySendThunk>` with `Ok`.
2620	///
2621	/// Verifies that converting a successful `TrySendThunk` produces a successful `TryThunk`.
2622	#[test]
2623	fn test_try_thunk_from_try_send_thunk_ok() {
2624		use crate::types::TrySendThunk;
2625		let send: TrySendThunk<i32, ()> = TrySendThunk::pure(42);
2626		let thunk: TryThunk<i32, ()> = TryThunk::from(send);
2627		assert_eq!(thunk.evaluate(), Ok(42));
2628	}
2629
2630	/// Tests `From<TrySendThunk>` with `Err`.
2631	///
2632	/// Verifies that converting a failed `TrySendThunk` produces a failed `TryThunk`.
2633	#[test]
2634	fn test_try_thunk_from_try_send_thunk_err() {
2635		use crate::types::TrySendThunk;
2636		let send: TrySendThunk<i32, String> = TrySendThunk::err("fail".to_string());
2637		let thunk: TryThunk<i32, String> = TryThunk::from(send);
2638		assert_eq!(thunk.evaluate(), Err("fail".to_string()));
2639	}
2640
2641	// QuickCheck Law Tests
2642
2643	// Functor Laws (via HKT, TryThunkErrAppliedBrand)
2644
2645	/// Functor identity: `map(id, t).evaluate() == t.evaluate()`.
2646	#[quickcheck]
2647	fn functor_identity(x: i32) -> bool {
2648		use crate::{
2649			brands::*,
2650			functions::*,
2651		};
2652		let t: TryThunk<i32, i32> = TryThunk::ok(x);
2653		map::<TryThunkErrAppliedBrand<i32>, _, _>(|a| a, t).evaluate() == Ok(x)
2654	}
2655
2656	/// Functor composition: `map(f . g, t) == map(f, map(g, t))`.
2657	#[quickcheck]
2658	fn functor_composition(x: i32) -> bool {
2659		use crate::{
2660			brands::*,
2661			functions::*,
2662		};
2663		let f = |a: i32| a.wrapping_add(1);
2664		let g = |a: i32| a.wrapping_mul(2);
2665		let lhs =
2666			map::<TryThunkErrAppliedBrand<i32>, _, _>(move |a| f(g(a)), TryThunk::ok(x)).evaluate();
2667		let rhs = map::<TryThunkErrAppliedBrand<i32>, _, _>(
2668			f,
2669			map::<TryThunkErrAppliedBrand<i32>, _, _>(g, TryThunk::ok(x)),
2670		)
2671		.evaluate();
2672		lhs == rhs
2673	}
2674
2675	// Monad Laws (via HKT, TryThunkErrAppliedBrand)
2676
2677	/// Monad left identity: `pure(a).bind(f) == f(a)` (for Ok values).
2678	#[quickcheck]
2679	fn monad_left_identity(a: i32) -> bool {
2680		use crate::{
2681			brands::*,
2682			functions::*,
2683		};
2684		let f = |x: i32| pure::<TryThunkErrAppliedBrand<i32>, _>(x.wrapping_mul(2));
2685		let lhs = bind::<TryThunkErrAppliedBrand<i32>, _, _>(
2686			pure::<TryThunkErrAppliedBrand<i32>, _>(a),
2687			f,
2688		)
2689		.evaluate();
2690		let rhs = f(a).evaluate();
2691		lhs == rhs
2692	}
2693
2694	/// Monad right identity: `t.bind(pure) == t`.
2695	#[quickcheck]
2696	fn monad_right_identity(x: i32) -> bool {
2697		use crate::{
2698			brands::*,
2699			functions::*,
2700		};
2701		let lhs = bind::<TryThunkErrAppliedBrand<i32>, _, _>(
2702			pure::<TryThunkErrAppliedBrand<i32>, _>(x),
2703			pure::<TryThunkErrAppliedBrand<i32>, _>,
2704		)
2705		.evaluate();
2706		lhs == Ok(x)
2707	}
2708
2709	/// Monad associativity: `m.bind(f).bind(g) == m.bind(|a| f(a).bind(g))`.
2710	#[quickcheck]
2711	fn monad_associativity(x: i32) -> bool {
2712		use crate::{
2713			brands::*,
2714			functions::*,
2715		};
2716		let f = |a: i32| pure::<TryThunkErrAppliedBrand<i32>, _>(a.wrapping_add(1));
2717		let g = |a: i32| pure::<TryThunkErrAppliedBrand<i32>, _>(a.wrapping_mul(3));
2718		let m: TryThunk<i32, i32> = TryThunk::ok(x);
2719		let m2: TryThunk<i32, i32> = TryThunk::ok(x);
2720		let lhs = bind::<TryThunkErrAppliedBrand<i32>, _, _>(
2721			bind::<TryThunkErrAppliedBrand<i32>, _, _>(m, f),
2722			g,
2723		)
2724		.evaluate();
2725		let rhs = bind::<TryThunkErrAppliedBrand<i32>, _, _>(m2, move |a| {
2726			bind::<TryThunkErrAppliedBrand<i32>, _, _>(f(a), g)
2727		})
2728		.evaluate();
2729		lhs == rhs
2730	}
2731
2732	/// Error short-circuit: `TryThunk::err(e).bind(f).evaluate() == Err(e)`.
2733	#[quickcheck]
2734	fn error_short_circuit(e: i32) -> bool {
2735		let t: TryThunk<i32, i32> = TryThunk::err(e);
2736		t.bind(|x| TryThunk::ok(x.wrapping_add(1))).evaluate() == Err(e)
2737	}
2738
2739	/// Tests `TryThunk::lift2` with two successful values.
2740	///
2741	/// Verifies that `lift2` combines results from both computations.
2742	#[test]
2743	fn test_lift2_ok_ok() {
2744		let t1: TryThunk<i32, String> = TryThunk::ok(10);
2745		let t2: TryThunk<i32, String> = TryThunk::ok(20);
2746		let t3 = t1.lift2(t2, |a, b| a + b);
2747		assert_eq!(t3.evaluate(), Ok(30));
2748	}
2749
2750	/// Tests `TryThunk::lift2` short-circuits on first error.
2751	///
2752	/// Verifies that if the first computation fails, the second is not evaluated.
2753	#[test]
2754	fn test_lift2_err_ok() {
2755		let t1: TryThunk<i32, String> = TryThunk::err("first".to_string());
2756		let t2: TryThunk<i32, String> = TryThunk::ok(20);
2757		let t3 = t1.lift2(t2, |a, b| a + b);
2758		assert_eq!(t3.evaluate(), Err("first".to_string()));
2759	}
2760
2761	/// Tests `TryThunk::lift2` propagates second error.
2762	///
2763	/// Verifies that if the second computation fails, the error is propagated.
2764	#[test]
2765	fn test_lift2_ok_err() {
2766		let t1: TryThunk<i32, String> = TryThunk::ok(10);
2767		let t2: TryThunk<i32, String> = TryThunk::err("second".to_string());
2768		let t3 = t1.lift2(t2, |a, b| a + b);
2769		assert_eq!(t3.evaluate(), Err("second".to_string()));
2770	}
2771
2772	/// Tests `TryThunk::then` with two successful values.
2773	///
2774	/// Verifies that `then` discards the first result and returns the second.
2775	#[test]
2776	fn test_then_ok_ok() {
2777		let t1: TryThunk<i32, String> = TryThunk::ok(10);
2778		let t2: TryThunk<i32, String> = TryThunk::ok(20);
2779		let t3 = t1.then(t2);
2780		assert_eq!(t3.evaluate(), Ok(20));
2781	}
2782
2783	/// Tests `TryThunk::then` short-circuits on first error.
2784	///
2785	/// Verifies that if the first computation fails, the second is not evaluated.
2786	#[test]
2787	fn test_then_err_ok() {
2788		let t1: TryThunk<i32, String> = TryThunk::err("first".to_string());
2789		let t2: TryThunk<i32, String> = TryThunk::ok(20);
2790		let t3 = t1.then(t2);
2791		assert_eq!(t3.evaluate(), Err("first".to_string()));
2792	}
2793
2794	/// Tests `TryThunk::then` propagates second error.
2795	///
2796	/// Verifies that if the second computation fails, the error is propagated.
2797	#[test]
2798	fn test_then_ok_err() {
2799		let t1: TryThunk<i32, String> = TryThunk::ok(10);
2800		let t2: TryThunk<i32, String> = TryThunk::err("second".to_string());
2801		let t3 = t1.then(t2);
2802		assert_eq!(t3.evaluate(), Err("second".to_string()));
2803	}
2804
2805	/// Tests `TryThunk::into_rc_try_lazy` basic usage.
2806	///
2807	/// Verifies that converting a thunk produces a lazy value with the same result.
2808	#[test]
2809	fn test_into_rc_try_lazy() {
2810		let thunk: TryThunk<i32, ()> = TryThunk::ok(42);
2811		let lazy = thunk.into_rc_try_lazy();
2812		assert_eq!(lazy.evaluate(), Ok(&42));
2813	}
2814
2815	/// Tests `TryThunk::into_rc_try_lazy` caching behavior.
2816	///
2817	/// Verifies that the memoized value is computed only once.
2818	#[test]
2819	fn test_into_rc_try_lazy_caching() {
2820		use std::{
2821			cell::RefCell,
2822			rc::Rc,
2823		};
2824
2825		let counter = Rc::new(RefCell::new(0));
2826		let counter_clone = counter.clone();
2827		let thunk: TryThunk<i32, ()> = TryThunk::new(move || {
2828			*counter_clone.borrow_mut() += 1;
2829			Ok(42)
2830		});
2831		let lazy = thunk.into_rc_try_lazy();
2832
2833		assert_eq!(*counter.borrow(), 0);
2834		assert_eq!(lazy.evaluate(), Ok(&42));
2835		assert_eq!(*counter.borrow(), 1);
2836		assert_eq!(lazy.evaluate(), Ok(&42));
2837		assert_eq!(*counter.borrow(), 1);
2838	}
2839
2840	/// Tests `TryThunk::into_arc_try_lazy` basic usage.
2841	///
2842	/// Verifies that converting a thunk produces a thread-safe lazy value.
2843	#[test]
2844	fn test_into_arc_try_lazy() {
2845		let thunk: TryThunk<i32, ()> = TryThunk::ok(42);
2846		let lazy = thunk.into_arc_try_lazy();
2847		assert_eq!(lazy.evaluate(), Ok(&42));
2848	}
2849
2850	/// Tests `TryThunk::into_arc_try_lazy` is Send and Sync.
2851	///
2852	/// Verifies that the resulting `ArcTryLazy` can be shared across threads.
2853	#[test]
2854	fn test_into_arc_try_lazy_send_sync() {
2855		use std::thread;
2856
2857		let thunk: TryThunk<i32, String> = TryThunk::ok(42);
2858		let lazy = thunk.into_arc_try_lazy();
2859		let lazy_clone = lazy.clone();
2860
2861		let handle = thread::spawn(move || {
2862			assert_eq!(lazy_clone.evaluate(), Ok(&42));
2863		});
2864
2865		assert_eq!(lazy.evaluate(), Ok(&42));
2866		handle.join().unwrap();
2867	}
2868
2869	/// Tests `TryThunk::catch_unwind_with` with a panicking closure.
2870	///
2871	/// Verifies that the custom handler converts the panic payload.
2872	#[test]
2873	fn test_catch_unwind_with_panic() {
2874		let thunk = TryThunk::<i32, i32>::catch_unwind_with(
2875			|| {
2876				if true {
2877					panic!("oops")
2878				}
2879				42
2880			},
2881			|_payload| -1,
2882		);
2883		assert_eq!(thunk.evaluate(), Err(-1));
2884	}
2885
2886	/// Tests `TryThunk::catch_unwind_with` with a non-panicking closure.
2887	///
2888	/// Verifies that a successful closure wraps the value in `Ok`.
2889	#[test]
2890	fn test_catch_unwind_with_success() {
2891		let thunk = TryThunk::<i32, i32>::catch_unwind_with(|| 42, |_payload| -1);
2892		assert_eq!(thunk.evaluate(), Ok(42));
2893	}
2894
2895	// 7.3: Bifunctor law QuickCheck tests for TryThunkBrand
2896
2897	/// Bifunctor identity: `bimap(id, id, t) == t`.
2898	#[quickcheck]
2899	fn bifunctor_identity_ok(x: i32) -> bool {
2900		use crate::{
2901			brands::*,
2902			classes::bifunctor::*,
2903		};
2904		let t: TryThunk<i32, i32> = TryThunk::ok(x);
2905		bimap::<TryThunkBrand, _, _, _, _>(|e| e, |a| a, t).evaluate() == Ok(x)
2906	}
2907
2908	/// Bifunctor identity on error path: `bimap(id, id, err(e)) == err(e)`.
2909	#[quickcheck]
2910	fn bifunctor_identity_err(e: i32) -> bool {
2911		use crate::{
2912			brands::*,
2913			classes::bifunctor::*,
2914		};
2915		let t: TryThunk<i32, i32> = TryThunk::err(e);
2916		bimap::<TryThunkBrand, _, _, _, _>(|e| e, |a| a, t).evaluate() == Err(e)
2917	}
2918
2919	/// Bifunctor composition: `bimap(f1 . f2, g1 . g2, t) == bimap(f1, g1, bimap(f2, g2, t))`.
2920	#[quickcheck]
2921	fn bifunctor_composition_ok(x: i32) -> bool {
2922		use crate::{
2923			brands::*,
2924			classes::bifunctor::*,
2925		};
2926		let f1 = |a: i32| a.wrapping_add(1);
2927		let f2 = |a: i32| a.wrapping_mul(2);
2928		let g1 = |a: i32| a.wrapping_add(10);
2929		let g2 = |a: i32| a.wrapping_mul(3);
2930
2931		let lhs = bimap::<TryThunkBrand, _, _, _, _>(
2932			move |e| f1(f2(e)),
2933			move |a| g1(g2(a)),
2934			TryThunk::ok(x),
2935		)
2936		.evaluate();
2937		let rhs = bimap::<TryThunkBrand, _, _, _, _>(
2938			f1,
2939			g1,
2940			bimap::<TryThunkBrand, _, _, _, _>(f2, g2, TryThunk::ok(x)),
2941		)
2942		.evaluate();
2943		lhs == rhs
2944	}
2945
2946	/// Bifunctor composition on error path.
2947	#[quickcheck]
2948	fn bifunctor_composition_err(e: i32) -> bool {
2949		use crate::{
2950			brands::*,
2951			classes::bifunctor::*,
2952		};
2953		let f1 = |a: i32| a.wrapping_add(1);
2954		let f2 = |a: i32| a.wrapping_mul(2);
2955		let g1 = |a: i32| a.wrapping_add(10);
2956		let g2 = |a: i32| a.wrapping_mul(3);
2957
2958		let lhs = bimap::<TryThunkBrand, _, _, _, _>(
2959			move |e| f1(f2(e)),
2960			move |a| g1(g2(a)),
2961			TryThunk::err(e),
2962		)
2963		.evaluate();
2964		let rhs = bimap::<TryThunkBrand, _, _, _, _>(
2965			f1,
2966			g1,
2967			bimap::<TryThunkBrand, _, _, _, _>(f2, g2, TryThunk::err(e)),
2968		)
2969		.evaluate();
2970		lhs == rhs
2971	}
2972
2973	// 7.4: Error-channel monad law QuickCheck tests via TryThunkOkAppliedBrand
2974
2975	/// Error-channel monad left identity: `pure(a).bind(f) == f(a)`.
2976	#[quickcheck]
2977	fn error_monad_left_identity(a: i32) -> bool {
2978		use crate::{
2979			brands::*,
2980			functions::*,
2981		};
2982		let f = |x: i32| pure::<TryThunkOkAppliedBrand<i32>, _>(x.wrapping_mul(2));
2983		let lhs =
2984			bind::<TryThunkOkAppliedBrand<i32>, _, _>(pure::<TryThunkOkAppliedBrand<i32>, _>(a), f)
2985				.evaluate();
2986		let rhs = f(a).evaluate();
2987		lhs == rhs
2988	}
2989
2990	/// Error-channel monad right identity: `m.bind(pure) == m`.
2991	#[quickcheck]
2992	fn error_monad_right_identity(x: i32) -> bool {
2993		use crate::{
2994			brands::*,
2995			functions::*,
2996		};
2997		let lhs = bind::<TryThunkOkAppliedBrand<i32>, _, _>(
2998			pure::<TryThunkOkAppliedBrand<i32>, _>(x),
2999			pure::<TryThunkOkAppliedBrand<i32>, _>,
3000		)
3001		.evaluate();
3002		lhs == Err(x)
3003	}
3004
3005	/// Error-channel monad associativity: `m.bind(f).bind(g) == m.bind(|a| f(a).bind(g))`.
3006	#[quickcheck]
3007	fn error_monad_associativity(x: i32) -> bool {
3008		use crate::{
3009			brands::*,
3010			functions::*,
3011		};
3012		let f = |a: i32| pure::<TryThunkOkAppliedBrand<i32>, _>(a.wrapping_add(1));
3013		let g = |a: i32| pure::<TryThunkOkAppliedBrand<i32>, _>(a.wrapping_mul(3));
3014		let m: TryThunk<i32, i32> = TryThunk::err(x);
3015		let m2: TryThunk<i32, i32> = TryThunk::err(x);
3016		let lhs = bind::<TryThunkOkAppliedBrand<i32>, _, _>(
3017			bind::<TryThunkOkAppliedBrand<i32>, _, _>(m, f),
3018			g,
3019		)
3020		.evaluate();
3021		let rhs = bind::<TryThunkOkAppliedBrand<i32>, _, _>(m2, move |a| {
3022			bind::<TryThunkOkAppliedBrand<i32>, _, _>(f(a), g)
3023		})
3024		.evaluate();
3025		lhs == rhs
3026	}
3027
3028	// 7.5: Semigroup/Monoid law QuickCheck tests for TryThunk
3029
3030	/// Semigroup associativity for TryThunk: `append(append(a, b), c) == append(a, append(b, c))`.
3031	#[quickcheck]
3032	fn try_thunk_semigroup_associativity(
3033		a: String,
3034		b: String,
3035		c: String,
3036	) -> bool {
3037		use crate::classes::semigroup::append;
3038
3039		let ta: TryThunk<String, ()> = TryThunk::ok(a.clone());
3040		let tb: TryThunk<String, ()> = TryThunk::ok(b.clone());
3041		let tc: TryThunk<String, ()> = TryThunk::ok(c.clone());
3042		let ta2: TryThunk<String, ()> = TryThunk::ok(a);
3043		let tb2: TryThunk<String, ()> = TryThunk::ok(b);
3044		let tc2: TryThunk<String, ()> = TryThunk::ok(c);
3045		let lhs = append(append(ta, tb), tc).evaluate();
3046		let rhs = append(ta2, append(tb2, tc2)).evaluate();
3047		lhs == rhs
3048	}
3049
3050	/// Monoid left identity for TryThunk: `append(empty(), a) == a`.
3051	#[quickcheck]
3052	fn try_thunk_monoid_left_identity(x: String) -> bool {
3053		use crate::classes::{
3054			monoid::empty,
3055			semigroup::append,
3056		};
3057
3058		let a: TryThunk<String, ()> = TryThunk::ok(x.clone());
3059		let lhs: TryThunk<String, ()> = append(empty(), a);
3060		lhs.evaluate() == Ok(x)
3061	}
3062
3063	/// Monoid right identity for TryThunk: `append(a, empty()) == a`.
3064	#[quickcheck]
3065	fn try_thunk_monoid_right_identity(x: String) -> bool {
3066		use crate::classes::{
3067			monoid::empty,
3068			semigroup::append,
3069		};
3070
3071		let a: TryThunk<String, ()> = TryThunk::ok(x.clone());
3072		let rhs: TryThunk<String, ()> = append(a, empty());
3073		rhs.evaluate() == Ok(x)
3074	}
3075
3076	// 7.6: Thread safety test for TryThunk::into_arc_try_lazy
3077
3078	/// Tests that `TryThunk::into_arc_try_lazy` produces a thread-safe value
3079	/// and all threads see the same cached result.
3080	#[test]
3081	fn test_into_arc_try_lazy_thread_safety() {
3082		use std::{
3083			sync::{
3084				Arc,
3085				atomic::{
3086					AtomicUsize,
3087					Ordering,
3088				},
3089			},
3090			thread,
3091		};
3092
3093		let counter = Arc::new(AtomicUsize::new(0));
3094		let counter_clone = Arc::clone(&counter);
3095		let thunk: TryThunk<i32, String> = TryThunk::new(move || {
3096			counter_clone.fetch_add(1, Ordering::SeqCst);
3097			Ok(42)
3098		});
3099		let lazy = thunk.into_arc_try_lazy();
3100
3101		let handles: Vec<_> = (0 .. 8)
3102			.map(|_| {
3103				let lazy_clone = lazy.clone();
3104				thread::spawn(move || {
3105					assert_eq!(lazy_clone.evaluate(), Ok(&42));
3106				})
3107			})
3108			.collect();
3109
3110		assert_eq!(lazy.evaluate(), Ok(&42));
3111		for h in handles {
3112			h.join().unwrap();
3113		}
3114
3115		// The closure should have been invoked exactly once.
3116		assert_eq!(counter.load(Ordering::SeqCst), 1);
3117	}
3118
3119	/// Tests `Semigroup::append` short-circuits when the first operand is `Err`.
3120	///
3121	/// Verifies that the second operand is never evaluated when the first fails.
3122	#[test]
3123	fn test_semigroup_append_first_err_short_circuits() {
3124		use {
3125			crate::classes::semigroup::append,
3126			std::cell::Cell,
3127		};
3128
3129		let counter = Cell::new(0u32);
3130		let t1: TryThunk<String, &str> = TryThunk::err("first failed");
3131		let t2: TryThunk<String, &str> = TryThunk::new(|| {
3132			counter.set(counter.get() + 1);
3133			Ok("second".to_string())
3134		});
3135		let result = append(t1, t2);
3136		assert_eq!(result.evaluate(), Err("first failed"));
3137		assert_eq!(counter.get(), 0, "second operand should not have been evaluated");
3138	}
3139
3140	/// Tests `Semigroup::append` propagates the error when the second operand fails.
3141	///
3142	/// Verifies that when the first operand succeeds but the second fails, the error
3143	/// from the second operand is returned.
3144	#[test]
3145	fn test_semigroup_append_second_err_propagates() {
3146		use crate::classes::semigroup::append;
3147
3148		let t1: TryThunk<String, &str> = TryThunk::pure("hello".to_string());
3149		let t2: TryThunk<String, &str> = TryThunk::err("second failed");
3150		let result = append(t1, t2);
3151		assert_eq!(result.evaluate(), Err("second failed"));
3152	}
3153}