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