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. The corresponding brands are [`TryThunkBrand`](crate::brands::TryThunkBrand) (bifunctor), [`TryThunkErrAppliedBrand`](crate::brands::TryThunkErrAppliedBrand) (functor over the success value), and [`TryThunkOkAppliedBrand`](crate::brands::TryThunkOkAppliedBrand) (functor over the error value).
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		#[multi_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		/// 	classes::semiapplicative::apply as explicit_apply,
1125		/// 	functions::*,
1126		/// 	types::*,
1127		/// };
1128		///
1129		/// let func: TryThunk<_, ()> =
1130		/// 	pure::<TryThunkErrAppliedBrand<()>, _>(lift_fn_new::<RcFnBrand, _, _>(|x: i32| x * 2));
1131		/// let val: TryThunk<_, ()> = pure::<TryThunkErrAppliedBrand<()>, _>(21);
1132		/// let result = explicit_apply::<RcFnBrand, TryThunkErrAppliedBrand<()>, _, _>(func, val);
1133		/// assert_eq!(result.evaluate(), Ok(42));
1134		/// ```
1135		fn apply<'a, FnBrand: 'a + CloneFn, A: 'a + Clone, B: 'a>(
1136			ff: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as CloneFn>::Of<'a, A, B>>),
1137			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1138		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
1139			ff.bind(move |f| {
1140				fa.map(
1141					#[expect(clippy::redundant_closure, reason = "Required for move semantics")]
1142					move |a| f(a),
1143				)
1144			})
1145		}
1146	}
1147
1148	#[document_type_parameters("The error type.")]
1149	impl<E: 'static> Semimonad for TryThunkErrAppliedBrand<E> {
1150		/// Chains `TryThunk` computations.
1151		#[document_signature]
1152		///
1153		#[document_type_parameters(
1154			"The lifetime of the computation.",
1155			"The type of the result of the first computation.",
1156			"The type of the result of the new computation."
1157		)]
1158		///
1159		#[document_parameters(
1160			"The first `TryThunk`.",
1161			"The function to apply to the result of the computation."
1162		)]
1163		///
1164		#[document_returns("A new `TryThunk` instance representing the chained computation.")]
1165		#[document_examples]
1166		///
1167		/// ```
1168		/// use fp_library::{
1169		/// 	brands::*,
1170		/// 	functions::*,
1171		/// 	types::*,
1172		/// };
1173		///
1174		/// let try_thunk: TryThunk<i32, ()> = pure::<TryThunkErrAppliedBrand<()>, _>(10);
1175		/// let result = explicit::bind::<TryThunkErrAppliedBrand<()>, _, _, _, _>(try_thunk, |x| {
1176		/// 	pure::<TryThunkErrAppliedBrand<()>, _>(x * 2)
1177		/// });
1178		/// assert_eq!(result.evaluate(), Ok(20));
1179		/// ```
1180		fn bind<'a, A: 'a, B: 'a>(
1181			ma: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1182			func: impl Fn(A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) + 'a,
1183		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
1184			ma.bind(func)
1185		}
1186	}
1187
1188	#[document_type_parameters("The error type.")]
1189	impl<E: 'static> MonadRec for TryThunkErrAppliedBrand<E> {
1190		/// Performs tail-recursive monadic computation.
1191		#[document_signature]
1192		///
1193		#[document_type_parameters(
1194			"The lifetime of the computation.",
1195			"The type of the initial value and loop state.",
1196			"The type of the result."
1197		)]
1198		///
1199		#[document_parameters("The step function.", "The initial value.")]
1200		///
1201		#[document_returns("The result of the computation.")]
1202		///
1203		#[document_examples]
1204		///
1205		/// ```
1206		/// use {
1207		/// 	core::ops::ControlFlow,
1208		/// 	fp_library::{
1209		/// 		brands::*,
1210		/// 		classes::*,
1211		/// 		functions::*,
1212		/// 		types::*,
1213		/// 	},
1214		/// };
1215		///
1216		/// let result = tail_rec_m::<TryThunkErrAppliedBrand<()>, _, _>(
1217		/// 	|x| {
1218		/// 		pure::<TryThunkErrAppliedBrand<()>, _>(
1219		/// 			if x < 1000 { ControlFlow::Continue(x + 1) } else { ControlFlow::Break(x) },
1220		/// 		)
1221		/// 	},
1222		/// 	0,
1223		/// );
1224		/// assert_eq!(result.evaluate(), Ok(1000));
1225		/// ```
1226		fn tail_rec_m<'a, A: 'a, B: 'a>(
1227			f: impl Fn(
1228				A,
1229			)
1230				-> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, ControlFlow<B, A>>)
1231			+ 'a,
1232			a: A,
1233		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
1234			TryThunk::new(move || {
1235				let mut current = a;
1236				loop {
1237					match f(current).evaluate() {
1238						Ok(ControlFlow::Continue(next)) => current = next,
1239						Ok(ControlFlow::Break(res)) => break Ok(res),
1240						Err(e) => break Err(e),
1241					}
1242				}
1243			})
1244		}
1245	}
1246
1247	#[document_type_parameters("The error type.")]
1248	impl<E: 'static> Foldable for TryThunkErrAppliedBrand<E> {
1249		/// Folds the `TryThunk` from the right.
1250		#[document_signature]
1251		///
1252		#[document_type_parameters(
1253			"The lifetime of the computation.",
1254			"The brand of the cloneable function to use.",
1255			"The type of the elements in the structure.",
1256			"The type of the accumulator."
1257		)]
1258		///
1259		#[document_parameters(
1260			"The function to apply to each element and the accumulator.",
1261			"The initial value of the accumulator.",
1262			"The `TryThunk` to fold."
1263		)]
1264		///
1265		#[document_returns("The final accumulator value.")]
1266		#[document_examples]
1267		///
1268		/// ```
1269		/// use fp_library::{
1270		/// 	brands::*,
1271		/// 	functions::*,
1272		/// 	types::*,
1273		/// };
1274		///
1275		/// let try_thunk: TryThunk<i32, ()> = pure::<TryThunkErrAppliedBrand<()>, _>(10);
1276		/// let result = explicit::fold_right::<RcFnBrand, TryThunkErrAppliedBrand<()>, _, _, _, _>(
1277		/// 	|a, b| a + b,
1278		/// 	5,
1279		/// 	try_thunk,
1280		/// );
1281		/// assert_eq!(result, 15);
1282		/// ```
1283		fn fold_right<'a, FnBrand, A: 'a + Clone, B: 'a>(
1284			func: impl Fn(A, B) -> B + 'a,
1285			initial: B,
1286			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1287		) -> B
1288		where
1289			FnBrand: CloneFn + 'a, {
1290			match fa.evaluate() {
1291				Ok(a) => func(a, initial),
1292				Err(_) => initial,
1293			}
1294		}
1295
1296		/// Folds the `TryThunk` from the left.
1297		#[document_signature]
1298		///
1299		#[document_type_parameters(
1300			"The lifetime of the computation.",
1301			"The brand of the cloneable function to use.",
1302			"The type of the elements in the structure.",
1303			"The type of the accumulator."
1304		)]
1305		///
1306		#[document_parameters(
1307			"The function to apply to the accumulator and each element.",
1308			"The initial value of the accumulator.",
1309			"The `TryThunk` to fold."
1310		)]
1311		///
1312		#[document_returns("The final accumulator value.")]
1313		#[document_examples]
1314		///
1315		/// ```
1316		/// use fp_library::{
1317		/// 	brands::*,
1318		/// 	functions::*,
1319		/// 	types::*,
1320		/// };
1321		///
1322		/// let try_thunk: TryThunk<i32, ()> = pure::<TryThunkErrAppliedBrand<()>, _>(10);
1323		/// let result = explicit::fold_left::<RcFnBrand, TryThunkErrAppliedBrand<()>, _, _, _, _>(
1324		/// 	|b, a| b + a,
1325		/// 	5,
1326		/// 	try_thunk,
1327		/// );
1328		/// assert_eq!(result, 15);
1329		/// ```
1330		fn fold_left<'a, FnBrand, A: 'a + Clone, B: 'a>(
1331			func: impl Fn(B, A) -> B + 'a,
1332			initial: B,
1333			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1334		) -> B
1335		where
1336			FnBrand: CloneFn + 'a, {
1337			match fa.evaluate() {
1338				Ok(a) => func(initial, a),
1339				Err(_) => initial,
1340			}
1341		}
1342
1343		/// Maps the value to a monoid and returns it.
1344		#[document_signature]
1345		///
1346		#[document_type_parameters(
1347			"The lifetime of the computation.",
1348			"The brand of the cloneable function to use.",
1349			"The type of the elements in the structure.",
1350			"The type of the monoid."
1351		)]
1352		///
1353		#[document_parameters("The mapping function.", "The TryThunk to fold.")]
1354		///
1355		#[document_returns("The monoid value.")]
1356		///
1357		#[document_examples]
1358		///
1359		/// ```
1360		/// use fp_library::{
1361		/// 	brands::*,
1362		/// 	functions::*,
1363		/// 	types::*,
1364		/// };
1365		///
1366		/// let try_thunk: TryThunk<i32, ()> = pure::<TryThunkErrAppliedBrand<()>, _>(10);
1367		/// let result = explicit::fold_map::<RcFnBrand, TryThunkErrAppliedBrand<()>, _, _, _, _>(
1368		/// 	|a: i32| a.to_string(),
1369		/// 	try_thunk,
1370		/// );
1371		/// assert_eq!(result, "10");
1372		/// ```
1373		fn fold_map<'a, FnBrand, A: 'a + Clone, M>(
1374			func: impl Fn(A) -> M + 'a,
1375			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
1376		) -> M
1377		where
1378			M: Monoid + 'a,
1379			FnBrand: CloneFn + 'a, {
1380			match fa.evaluate() {
1381				Ok(a) => func(a),
1382				Err(_) => M::empty(),
1383			}
1384		}
1385	}
1386
1387	#[document_type_parameters(
1388		"The lifetime of the computation.",
1389		"The success value type.",
1390		"The error value type."
1391	)]
1392	impl<'a, A: Semigroup + 'a, E: 'a> Semigroup for TryThunk<'a, A, E> {
1393		/// Combines two `TryThunk`s by combining their results.
1394		#[document_signature]
1395		///
1396		#[document_parameters("The first `TryThunk`.", "The second `TryThunk`.")]
1397		///
1398		#[document_returns("A new `TryThunk` containing the combined result.")]
1399		///
1400		#[document_examples]
1401		///
1402		/// ```
1403		/// use fp_library::{
1404		/// 	brands::*,
1405		/// 	classes::*,
1406		/// 	functions::*,
1407		/// 	types::*,
1408		/// };
1409		///
1410		/// let t1: TryThunk<String, ()> = pure::<TryThunkErrAppliedBrand<()>, _>("Hello".to_string());
1411		/// let t2: TryThunk<String, ()> = pure::<TryThunkErrAppliedBrand<()>, _>(" World".to_string());
1412		/// let t3 = append::<_>(t1, t2);
1413		/// assert_eq!(t3.evaluate(), Ok("Hello World".to_string()));
1414		/// ```
1415		fn append(
1416			a: Self,
1417			b: Self,
1418		) -> Self {
1419			TryThunk::new(move || {
1420				let a_val = a.evaluate()?;
1421				let b_val = b.evaluate()?;
1422				Ok(Semigroup::append(a_val, b_val))
1423			})
1424		}
1425	}
1426
1427	#[document_type_parameters(
1428		"The lifetime of the computation.",
1429		"The success value type.",
1430		"The error value type."
1431	)]
1432	impl<'a, A: Monoid + 'a, E: 'a> Monoid for TryThunk<'a, A, E> {
1433		/// Returns the identity `TryThunk`.
1434		#[document_signature]
1435		///
1436		#[document_returns("A `TryThunk` producing the identity value of `A`.")]
1437		///
1438		#[document_examples]
1439		///
1440		/// ```
1441		/// use fp_library::{
1442		/// 	classes::*,
1443		/// 	types::*,
1444		/// };
1445		///
1446		/// let t: TryThunk<String, ()> = TryThunk::empty();
1447		/// assert_eq!(t.evaluate(), Ok("".to_string()));
1448		/// ```
1449		fn empty() -> Self {
1450			TryThunk(Thunk::pure(Ok(Monoid::empty())))
1451		}
1452	}
1453
1454	impl_kind! {
1455		/// HKT branding for the `TryThunk` type.
1456		///
1457		/// The type parameters for `Of` are ordered `E`, then `A` (Error, then Success).
1458		/// This follows the same convention as `ResultBrand`, matching functional
1459		/// programming expectations (like Haskell's `Either e a`) where the success
1460		/// type is the last parameter.
1461		for TryThunkBrand {
1462			type Of<'a, E: 'a, A: 'a>: 'a = TryThunk<'a, A, E>;
1463		}
1464	}
1465
1466	impl Bifunctor for TryThunkBrand {
1467		/// Maps functions over the values in the `TryThunk`.
1468		///
1469		/// This method applies one function to the error value and another to the success value.
1470		#[document_signature]
1471		///
1472		#[document_type_parameters(
1473			"The lifetime of the values.",
1474			"The type of the error value.",
1475			"The type of the mapped error value.",
1476			"The type of the success value.",
1477			"The type of the mapped success value."
1478		)]
1479		///
1480		#[document_parameters(
1481			"The function to apply to the error.",
1482			"The function to apply to the success.",
1483			"The `TryThunk` to map over."
1484		)]
1485		///
1486		#[document_returns("A new `TryThunk` containing the mapped values.")]
1487		#[document_examples]
1488		///
1489		/// ```
1490		/// use fp_library::{
1491		/// 	brands::*,
1492		/// 	functions::*,
1493		/// 	types::*,
1494		/// };
1495		///
1496		/// let x: TryThunk<i32, i32> = TryThunk::ok(5);
1497		/// assert_eq!(
1498		/// 	explicit::bimap::<TryThunkBrand, _, _, _, _, _, _>((|e| e + 1, |s| s * 2), x).evaluate(),
1499		/// 	Ok(10)
1500		/// );
1501		///
1502		/// let y: TryThunk<i32, i32> = TryThunk::err(5);
1503		/// assert_eq!(
1504		/// 	explicit::bimap::<TryThunkBrand, _, _, _, _, _, _>((|e| e + 1, |s| s * 2), y).evaluate(),
1505		/// 	Err(6)
1506		/// );
1507		/// ```
1508		fn bimap<'a, A: 'a, B: 'a, C: 'a, D: 'a>(
1509			f: impl Fn(A) -> B + 'a,
1510			g: impl Fn(C) -> D + 'a,
1511			p: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, C>),
1512		) -> Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, B, D>) {
1513			TryThunk(p.0.map(move |result| match result {
1514				Ok(c) => Ok(g(c)),
1515				Err(a) => Err(f(a)),
1516			}))
1517		}
1518	}
1519
1520	impl Bifoldable for TryThunkBrand {
1521		/// Folds a `TryThunk` using two step functions, right-associatively.
1522		///
1523		/// Dispatches to `f` for error values and `g` for success values.
1524		/// The thunk is evaluated to determine which branch to fold.
1525		#[document_signature]
1526		///
1527		#[document_type_parameters(
1528			"The lifetime of the values.",
1529			"The brand of the cloneable function to use.",
1530			"The error type (first position).",
1531			"The success type (second position).",
1532			"The accumulator type."
1533		)]
1534		///
1535		#[document_parameters(
1536			"The step function applied to the error value.",
1537			"The step function applied to the success value.",
1538			"The initial accumulator.",
1539			"The `TryThunk` to fold."
1540		)]
1541		///
1542		#[document_returns("`f(e, z)` for `Err(e)`, or `g(a, z)` for `Ok(a)`.")]
1543		#[document_examples]
1544		///
1545		/// ```
1546		/// use fp_library::{
1547		/// 	brands::*,
1548		/// 	functions::*,
1549		/// 	types::*,
1550		/// };
1551		///
1552		/// assert_eq!(
1553		/// 	explicit::bi_fold_right::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
1554		/// 		(|e: i32, acc| acc - e, |s: i32, acc| acc + s),
1555		/// 		10,
1556		/// 		TryThunk::err(3),
1557		/// 	),
1558		/// 	7
1559		/// );
1560		/// assert_eq!(
1561		/// 	explicit::bi_fold_right::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
1562		/// 		(|e: i32, acc| acc - e, |s: i32, acc| acc + s),
1563		/// 		10,
1564		/// 		TryThunk::ok(5),
1565		/// 	),
1566		/// 	15
1567		/// );
1568		/// ```
1569		fn bi_fold_right<'a, FnBrand: CloneFn + 'a, A: 'a + Clone, B: 'a + Clone, C: 'a>(
1570			f: impl Fn(A, C) -> C + 'a,
1571			g: impl Fn(B, C) -> C + 'a,
1572			z: C,
1573			p: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
1574		) -> C {
1575			match p.evaluate() {
1576				Err(a) => f(a, z),
1577				Ok(b) => g(b, z),
1578			}
1579		}
1580
1581		/// Folds a `TryThunk` using two step functions, left-associatively.
1582		///
1583		/// Dispatches to `f` for error values and `g` for success values.
1584		/// The thunk is evaluated to determine which branch to fold.
1585		#[document_signature]
1586		///
1587		#[document_type_parameters(
1588			"The lifetime of the values.",
1589			"The brand of the cloneable function to use.",
1590			"The error type (first position).",
1591			"The success type (second position).",
1592			"The accumulator type."
1593		)]
1594		///
1595		#[document_parameters(
1596			"The step function applied to the error value.",
1597			"The step function applied to the success value.",
1598			"The initial accumulator.",
1599			"The `TryThunk` to fold."
1600		)]
1601		///
1602		#[document_returns("`f(z, e)` for `Err(e)`, or `g(z, a)` for `Ok(a)`.")]
1603		#[document_examples]
1604		///
1605		/// ```
1606		/// use fp_library::{
1607		/// 	brands::*,
1608		/// 	functions::*,
1609		/// 	types::*,
1610		/// };
1611		///
1612		/// assert_eq!(
1613		/// 	explicit::bi_fold_left::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
1614		/// 		(|acc, e: i32| acc - e, |acc, s: i32| acc + s),
1615		/// 		10,
1616		/// 		TryThunk::err(3),
1617		/// 	),
1618		/// 	7
1619		/// );
1620		/// assert_eq!(
1621		/// 	explicit::bi_fold_left::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
1622		/// 		(|acc, e: i32| acc - e, |acc, s: i32| acc + s),
1623		/// 		10,
1624		/// 		TryThunk::ok(5),
1625		/// 	),
1626		/// 	15
1627		/// );
1628		/// ```
1629		fn bi_fold_left<'a, FnBrand: CloneFn + 'a, A: 'a + Clone, B: 'a + Clone, C: 'a>(
1630			f: impl Fn(C, A) -> C + 'a,
1631			g: impl Fn(C, B) -> C + 'a,
1632			z: C,
1633			p: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
1634		) -> C {
1635			match p.evaluate() {
1636				Err(a) => f(z, a),
1637				Ok(b) => g(z, b),
1638			}
1639		}
1640
1641		/// Maps a `TryThunk`'s value to a monoid using two functions and returns the result.
1642		///
1643		/// Dispatches to `f` for error values and `g` for success values,
1644		/// returning the monoid value.
1645		#[document_signature]
1646		///
1647		#[document_type_parameters(
1648			"The lifetime of the values.",
1649			"The brand of the cloneable function to use.",
1650			"The error type (first position).",
1651			"The success type (second position).",
1652			"The monoid type."
1653		)]
1654		///
1655		#[document_parameters(
1656			"The function mapping the error to the monoid.",
1657			"The function mapping the success to the monoid.",
1658			"The `TryThunk` to fold."
1659		)]
1660		///
1661		#[document_returns("`f(e)` for `Err(e)`, or `g(a)` for `Ok(a)`.")]
1662		#[document_examples]
1663		///
1664		/// ```
1665		/// use fp_library::{
1666		/// 	brands::*,
1667		/// 	functions::*,
1668		/// 	types::*,
1669		/// };
1670		///
1671		/// assert_eq!(
1672		/// 	explicit::bi_fold_map::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
1673		/// 		(|e: i32| e.to_string(), |s: i32| s.to_string()),
1674		/// 		TryThunk::err(3),
1675		/// 	),
1676		/// 	"3".to_string()
1677		/// );
1678		/// assert_eq!(
1679		/// 	explicit::bi_fold_map::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
1680		/// 		(|e: i32| e.to_string(), |s: i32| s.to_string()),
1681		/// 		TryThunk::ok(5),
1682		/// 	),
1683		/// 	"5".to_string()
1684		/// );
1685		/// ```
1686		fn bi_fold_map<'a, FnBrand: CloneFn + 'a, A: 'a + Clone, B: 'a + Clone, M>(
1687			f: impl Fn(A) -> M + 'a,
1688			g: impl Fn(B) -> M + 'a,
1689			p: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
1690		) -> M
1691		where
1692			M: Monoid + 'a, {
1693			match p.evaluate() {
1694				Err(a) => f(a),
1695				Ok(b) => g(b),
1696			}
1697		}
1698	}
1699
1700	impl_kind! {
1701		#[multi_brand]
1702		/// HKT branding for `TryThunk` with the success type `A` fixed.
1703		///
1704		/// This is the "dual-channel" encoding for `TryThunk`:
1705		/// - [`TryThunkErrAppliedBrand<E>`] fixes the error type and is polymorphic over `Ok` values,
1706		///   giving a standard `Functor`/`Monad` that maps and chains success values.
1707		/// - `TryThunkOkAppliedBrand<A>` fixes the success type and is polymorphic over `Err` values,
1708		///   giving a `Functor`/`Monad` that maps and chains error values.
1709		///
1710		/// Together they allow the same `TryThunk<'a, A, E>` to participate in HKT abstractions
1711		/// on either channel. For example, `pure::<TryThunkErrAppliedBrand<E>, _>(x)` produces
1712		/// `Ok(x)`, while `pure::<TryThunkOkAppliedBrand<A>, _>(e)` produces `Err(e)`.
1713		impl<A: 'static> for TryThunkOkAppliedBrand<A> {
1714			#[document_default]
1715			type Of<'a, E: 'a>: 'a = TryThunk<'a, A, E>;
1716		}
1717	}
1718
1719	#[document_type_parameters("The success type.")]
1720	impl<A: 'static> Functor for TryThunkOkAppliedBrand<A> {
1721		/// Maps a function over the error value in the `TryThunk`.
1722		#[document_signature]
1723		///
1724		#[document_type_parameters(
1725			"The lifetime of the computation.",
1726			"The type of the error value inside the `TryThunk`.",
1727			"The type of the result of the transformation."
1728		)]
1729		///
1730		#[document_parameters("The function to apply to the error.", "The `TryThunk` instance.")]
1731		///
1732		#[document_returns("A new `TryThunk` instance with the transformed error.")]
1733		///
1734		#[document_examples]
1735		///
1736		/// ```
1737		/// use fp_library::{
1738		/// 	brands::*,
1739		/// 	functions::*,
1740		/// 	types::*,
1741		/// };
1742		///
1743		/// let try_thunk: TryThunk<i32, i32> = pure::<TryThunkOkAppliedBrand<i32>, _>(10);
1744		/// let mapped = explicit::map::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(|x| x * 2, try_thunk);
1745		/// assert_eq!(mapped.evaluate(), Err(20));
1746		/// ```
1747		fn map<'a, E: 'a, E2: 'a>(
1748			func: impl Fn(E) -> E2 + 'a,
1749			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
1750		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
1751			fa.map_err(func)
1752		}
1753	}
1754
1755	#[document_type_parameters("The success type.")]
1756	impl<A: 'static> Pointed for TryThunkOkAppliedBrand<A> {
1757		/// Wraps a value in a `TryThunk` context (as error).
1758		#[document_signature]
1759		///
1760		#[document_type_parameters(
1761			"The lifetime of the computation.",
1762			"The type of the value to wrap."
1763		)]
1764		///
1765		#[document_parameters("The value to wrap.")]
1766		///
1767		#[document_returns("A new `TryThunk` instance containing the value as an error.")]
1768		///
1769		#[document_examples]
1770		///
1771		/// ```
1772		/// use fp_library::{
1773		/// 	brands::*,
1774		/// 	functions::*,
1775		/// 	types::*,
1776		/// };
1777		///
1778		/// let try_thunk: TryThunk<i32, i32> = pure::<TryThunkOkAppliedBrand<i32>, _>(42);
1779		/// assert_eq!(try_thunk.evaluate(), Err(42));
1780		/// ```
1781		fn pure<'a, E: 'a>(e: E) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>) {
1782			TryThunk::err(e)
1783		}
1784	}
1785
1786	#[document_type_parameters("The success type.")]
1787	impl<A: 'static> Lift for TryThunkOkAppliedBrand<A> {
1788		/// Lifts a binary function into the `TryThunk` context (over error).
1789		///
1790		/// # Evaluation strategy
1791		///
1792		/// This implementation uses **fail-fast** semantics, consistent with
1793		/// [`bind`](TryThunk::bind): `fa` is evaluated first, and if it is `Ok`,
1794		/// the result is returned immediately without evaluating `fb`. If `fa` is
1795		/// `Err`, `fb` is evaluated next; if `fb` is `Ok`, that `Ok` is returned.
1796		/// The function is only called when both sides are `Err`.
1797		#[document_signature]
1798		///
1799		#[document_type_parameters(
1800			"The lifetime of the computation.",
1801			"The type of the first error value.",
1802			"The type of the second error value.",
1803			"The type of the result error value."
1804		)]
1805		///
1806		#[document_parameters(
1807			"The binary function to apply to the errors.",
1808			"The first `TryThunk`.",
1809			"The second `TryThunk`."
1810		)]
1811		///
1812		#[document_returns(
1813			"A new `TryThunk` instance containing the result of applying the function to the errors."
1814		)]
1815		#[document_examples]
1816		///
1817		/// ```
1818		/// use fp_library::{
1819		/// 	brands::*,
1820		/// 	functions::*,
1821		/// 	types::*,
1822		/// };
1823		///
1824		/// let eval1: TryThunk<i32, i32> = pure::<TryThunkOkAppliedBrand<i32>, _>(10);
1825		/// let eval2: TryThunk<i32, i32> = pure::<TryThunkOkAppliedBrand<i32>, _>(20);
1826		/// let result = explicit::lift2::<TryThunkOkAppliedBrand<i32>, _, _, _, _, _, _>(
1827		/// 	|a, b| a + b,
1828		/// 	eval1,
1829		/// 	eval2,
1830		/// );
1831		/// assert_eq!(result.evaluate(), Err(30));
1832		/// ```
1833		fn lift2<'a, E1, E2, E3>(
1834			func: impl Fn(E1, E2) -> E3 + 'a,
1835			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E1>),
1836			fb: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>),
1837		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E3>)
1838		where
1839			E1: Clone + 'a,
1840			E2: Clone + 'a,
1841			E3: 'a, {
1842			TryThunk::new(move || match fa.evaluate() {
1843				Ok(a) => Ok(a),
1844				Err(e1) => match fb.evaluate() {
1845					Ok(a) => Ok(a),
1846					Err(e2) => Err(func(e1, e2)),
1847				},
1848			})
1849		}
1850	}
1851
1852	#[document_type_parameters("The success type.")]
1853	impl<A: 'static> ApplyFirst for TryThunkOkAppliedBrand<A> {}
1854
1855	#[document_type_parameters("The success type.")]
1856	impl<A: 'static> ApplySecond for TryThunkOkAppliedBrand<A> {}
1857
1858	#[document_type_parameters("The success type.")]
1859	impl<A: 'static> Semiapplicative for TryThunkOkAppliedBrand<A> {
1860		/// Applies a function wrapped in `TryThunk` (as error) to a value wrapped in `TryThunk` (as error).
1861		///
1862		/// # Evaluation strategy
1863		///
1864		/// This implementation uses **fail-fast** semantics, consistent with
1865		/// [`bind`](TryThunk::bind): `ff` is evaluated first, and if it is `Ok`,
1866		/// the result is returned immediately without evaluating `fa`. If `ff` is
1867		/// `Err`, `fa` is evaluated next; if `fa` is `Ok`, that `Ok` is returned.
1868		/// The function is only applied when both sides are `Err`.
1869		#[document_signature]
1870		///
1871		#[document_type_parameters(
1872			"The lifetime of the computation.",
1873			"The brand of the cloneable function wrapper.",
1874			"The type of the input error.",
1875			"The type of the result error."
1876		)]
1877		///
1878		#[document_parameters(
1879			"The `TryThunk` containing the function (in Err).",
1880			"The `TryThunk` containing the value (in Err)."
1881		)]
1882		///
1883		#[document_returns(
1884			"A new `TryThunk` instance containing the result of applying the function."
1885		)]
1886		#[document_examples]
1887		///
1888		/// ```
1889		/// use fp_library::{
1890		/// 	brands::*,
1891		/// 	classes::semiapplicative::apply as explicit_apply,
1892		/// 	functions::*,
1893		/// 	types::*,
1894		/// };
1895		///
1896		/// let func: TryThunk<i32, _> =
1897		/// 	pure::<TryThunkOkAppliedBrand<i32>, _>(lift_fn_new::<RcFnBrand, _, _>(|x: i32| x * 2));
1898		/// let val: TryThunk<i32, _> = pure::<TryThunkOkAppliedBrand<i32>, _>(21);
1899		/// let result = explicit_apply::<RcFnBrand, TryThunkOkAppliedBrand<i32>, _, _>(func, val);
1900		/// assert_eq!(result.evaluate(), Err(42));
1901		/// ```
1902		fn apply<'a, FnBrand: 'a + CloneFn, E1: 'a + Clone, E2: 'a>(
1903			ff: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as CloneFn>::Of<'a, E1, E2>>),
1904			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E1>),
1905		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
1906			TryThunk::new(move || match ff.evaluate() {
1907				Ok(a) => Ok(a),
1908				Err(f) => match fa.evaluate() {
1909					Ok(a) => Ok(a),
1910					Err(e) => Err(f(e)),
1911				},
1912			})
1913		}
1914	}
1915
1916	#[document_type_parameters("The success type.")]
1917	impl<A: 'static> Semimonad for TryThunkOkAppliedBrand<A> {
1918		/// Chains `TryThunk` computations (over error).
1919		#[document_signature]
1920		///
1921		#[document_type_parameters(
1922			"The lifetime of the computation.",
1923			"The type of the result of the first computation (error).",
1924			"The type of the result of the new computation (error)."
1925		)]
1926		///
1927		#[document_parameters(
1928			"The first `TryThunk`.",
1929			"The function to apply to the error result of the computation."
1930		)]
1931		///
1932		#[document_returns("A new `TryThunk` instance representing the chained computation.")]
1933		#[document_examples]
1934		///
1935		/// ```
1936		/// use fp_library::{
1937		/// 	brands::*,
1938		/// 	functions::*,
1939		/// 	types::*,
1940		/// };
1941		///
1942		/// let try_thunk: TryThunk<i32, i32> = pure::<TryThunkOkAppliedBrand<i32>, _>(10);
1943		/// let result = explicit::bind::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(try_thunk, |x| {
1944		/// 	pure::<TryThunkOkAppliedBrand<i32>, _>(x * 2)
1945		/// });
1946		/// assert_eq!(result.evaluate(), Err(20));
1947		/// ```
1948		fn bind<'a, E1: 'a, E2: 'a>(
1949			ma: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E1>),
1950			func: impl Fn(E1) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) + 'a,
1951		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
1952			TryThunk::new(move || match ma.evaluate() {
1953				Ok(a) => Ok(a),
1954				Err(e) => func(e).evaluate(),
1955			})
1956		}
1957	}
1958
1959	#[document_type_parameters("The success type.")]
1960	impl<A: 'static> MonadRec for TryThunkOkAppliedBrand<A> {
1961		/// Performs tail-recursive monadic computation over the error channel.
1962		///
1963		/// The step function returns `TryThunk<A, ControlFlow<E2, E>>`. The loop
1964		/// continues while the error is `ControlFlow::Continue`, terminates with the
1965		/// final error on `ControlFlow::Break`, and short-circuits on `Ok`.
1966		#[document_signature]
1967		///
1968		#[document_type_parameters(
1969			"The lifetime of the computation.",
1970			"The type of the initial value and loop state.",
1971			"The type of the result."
1972		)]
1973		///
1974		#[document_parameters("The step function.", "The initial value.")]
1975		///
1976		#[document_returns("The result of the computation.")]
1977		///
1978		#[document_examples]
1979		///
1980		/// ```
1981		/// use {
1982		/// 	core::ops::ControlFlow,
1983		/// 	fp_library::{
1984		/// 		brands::*,
1985		/// 		classes::*,
1986		/// 		functions::*,
1987		/// 		types::*,
1988		/// 	},
1989		/// };
1990		///
1991		/// let result = tail_rec_m::<TryThunkOkAppliedBrand<i32>, _, _>(
1992		/// 	|x| {
1993		/// 		pure::<TryThunkOkAppliedBrand<i32>, _>(
1994		/// 			if x < 1000 { ControlFlow::Continue(x + 1) } else { ControlFlow::Break(x) },
1995		/// 		)
1996		/// 	},
1997		/// 	0,
1998		/// );
1999		/// assert_eq!(result.evaluate(), Err(1000));
2000		/// ```
2001		fn tail_rec_m<'a, E: 'a, E2: 'a>(
2002			f: impl Fn(
2003				E,
2004			)
2005				-> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, ControlFlow<E2, E>>)
2006			+ 'a,
2007			e: E,
2008		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
2009			TryThunk::new(move || {
2010				let mut current = e;
2011				loop {
2012					match f(current).evaluate() {
2013						Err(ControlFlow::Continue(next)) => current = next,
2014						Err(ControlFlow::Break(res)) => break Err(res),
2015						Ok(a) => break Ok(a),
2016					}
2017				}
2018			})
2019		}
2020	}
2021
2022	#[document_type_parameters("The success type.")]
2023	impl<A: 'static> Foldable for TryThunkOkAppliedBrand<A> {
2024		/// Folds the `TryThunk` from the right (over error).
2025		#[document_signature]
2026		///
2027		#[document_type_parameters(
2028			"The lifetime of the computation.",
2029			"The brand of the cloneable function to use.",
2030			"The type of the elements in the structure.",
2031			"The type of the accumulator."
2032		)]
2033		///
2034		#[document_parameters(
2035			"The function to apply to each element and the accumulator.",
2036			"The initial value of the accumulator.",
2037			"The `TryThunk` to fold."
2038		)]
2039		///
2040		#[document_returns("The final accumulator value.")]
2041		#[document_examples]
2042		///
2043		/// ```
2044		/// use fp_library::{
2045		/// 	brands::*,
2046		/// 	functions::*,
2047		/// 	types::*,
2048		/// };
2049		///
2050		/// let try_thunk: TryThunk<i32, i32> = pure::<TryThunkOkAppliedBrand<i32>, _>(10);
2051		/// let result = explicit::fold_right::<RcFnBrand, TryThunkOkAppliedBrand<i32>, _, _, _, _>(
2052		/// 	|a, b| a + b,
2053		/// 	5,
2054		/// 	try_thunk,
2055		/// );
2056		/// assert_eq!(result, 15);
2057		/// ```
2058		fn fold_right<'a, FnBrand, E: 'a + Clone, B: 'a>(
2059			func: impl Fn(E, B) -> B + 'a,
2060			initial: B,
2061			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
2062		) -> B
2063		where
2064			FnBrand: CloneFn + 'a, {
2065			match fa.evaluate() {
2066				Err(e) => func(e, initial),
2067				Ok(_) => initial,
2068			}
2069		}
2070
2071		/// Folds the `TryThunk` from the left (over error).
2072		#[document_signature]
2073		///
2074		#[document_type_parameters(
2075			"The lifetime of the computation.",
2076			"The brand of the cloneable function to use.",
2077			"The type of the elements in the structure.",
2078			"The type of the accumulator."
2079		)]
2080		///
2081		#[document_parameters(
2082			"The function to apply to the accumulator and each element.",
2083			"The initial value of the accumulator.",
2084			"The `TryThunk` to fold."
2085		)]
2086		///
2087		#[document_returns("The final accumulator value.")]
2088		#[document_examples]
2089		///
2090		/// ```
2091		/// use fp_library::{
2092		/// 	brands::*,
2093		/// 	functions::*,
2094		/// 	types::*,
2095		/// };
2096		///
2097		/// let try_thunk: TryThunk<i32, i32> = pure::<TryThunkOkAppliedBrand<i32>, _>(10);
2098		/// let result = explicit::fold_left::<RcFnBrand, TryThunkOkAppliedBrand<i32>, _, _, _, _>(
2099		/// 	|b, a| b + a,
2100		/// 	5,
2101		/// 	try_thunk,
2102		/// );
2103		/// assert_eq!(result, 15);
2104		/// ```
2105		fn fold_left<'a, FnBrand, E: 'a + Clone, B: 'a>(
2106			func: impl Fn(B, E) -> B + 'a,
2107			initial: B,
2108			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
2109		) -> B
2110		where
2111			FnBrand: CloneFn + 'a, {
2112			match fa.evaluate() {
2113				Err(e) => func(initial, e),
2114				Ok(_) => initial,
2115			}
2116		}
2117
2118		/// Maps the value to a monoid and returns it (over error).
2119		#[document_signature]
2120		///
2121		#[document_type_parameters(
2122			"The lifetime of the computation.",
2123			"The brand of the cloneable function to use.",
2124			"The type of the elements in the structure.",
2125			"The type of the monoid."
2126		)]
2127		///
2128		#[document_parameters("The mapping function.", "The TryThunk to fold.")]
2129		///
2130		#[document_returns("The monoid value.")]
2131		///
2132		#[document_examples]
2133		///
2134		/// ```
2135		/// use fp_library::{
2136		/// 	brands::*,
2137		/// 	functions::*,
2138		/// 	types::*,
2139		/// };
2140		///
2141		/// let try_thunk: TryThunk<i32, i32> = pure::<TryThunkOkAppliedBrand<i32>, _>(10);
2142		/// let result = explicit::fold_map::<RcFnBrand, TryThunkOkAppliedBrand<i32>, _, _, _, _>(
2143		/// 	|a: i32| a.to_string(),
2144		/// 	try_thunk,
2145		/// );
2146		/// assert_eq!(result, "10");
2147		/// ```
2148		fn fold_map<'a, FnBrand, E: 'a + Clone, M>(
2149			func: impl Fn(E) -> M + 'a,
2150			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
2151		) -> M
2152		where
2153			M: Monoid + 'a,
2154			FnBrand: CloneFn + 'a, {
2155			match fa.evaluate() {
2156				Err(e) => func(e),
2157				Ok(_) => M::empty(),
2158			}
2159		}
2160	}
2161
2162	#[document_type_parameters(
2163		"The lifetime of the computation.",
2164		"The type of the success value.",
2165		"The type of the error value."
2166	)]
2167	#[document_parameters("The try-thunk to format.")]
2168	impl<'a, A, E> fmt::Debug for TryThunk<'a, A, E> {
2169		/// Formats the try-thunk without evaluating it.
2170		#[document_signature]
2171		#[document_parameters("The formatter.")]
2172		#[document_returns("The formatting result.")]
2173		#[document_examples]
2174		///
2175		/// ```
2176		/// use fp_library::types::*;
2177		/// let thunk = TryThunk::new(|| Ok::<i32, ()>(42));
2178		/// assert_eq!(format!("{:?}", thunk), "TryThunk(<unevaluated>)");
2179		/// ```
2180		fn fmt(
2181			&self,
2182			f: &mut fmt::Formatter<'_>,
2183		) -> fmt::Result {
2184			f.write_str("TryThunk(<unevaluated>)")
2185		}
2186	}
2187
2188	#[document_type_parameters("The error type.")]
2189	impl<E: 'static> WithIndex for TryThunkErrAppliedBrand<E> {
2190		type Index = ();
2191	}
2192
2193	#[document_type_parameters("The error type.")]
2194	impl<E: 'static> FunctorWithIndex for TryThunkErrAppliedBrand<E> {
2195		/// Maps a function over the success value in the `TryThunk`, providing the index `()`.
2196		#[document_signature]
2197		#[document_type_parameters(
2198			"The lifetime of the computation.",
2199			"The type of the success value inside the `TryThunk`.",
2200			"The type of the result of applying the function."
2201		)]
2202		#[document_parameters(
2203			"The function to apply to the value and its index.",
2204			"The `TryThunk` to map over."
2205		)]
2206		#[document_returns(
2207			"A new `TryThunk` containing the result of applying the function, or the original error."
2208		)]
2209		#[document_examples]
2210		///
2211		/// ```
2212		/// use fp_library::{
2213		/// 	brands::TryThunkErrAppliedBrand,
2214		/// 	classes::functor_with_index::FunctorWithIndex,
2215		/// 	types::*,
2216		/// };
2217		///
2218		/// let x: TryThunk<i32, ()> = TryThunk::pure(5);
2219		/// let y = <TryThunkErrAppliedBrand<()> as FunctorWithIndex>::map_with_index(|_, i| i * 2, x);
2220		/// assert_eq!(y.evaluate(), Ok(10));
2221		/// ```
2222		fn map_with_index<'a, A: 'a, B: 'a>(
2223			f: impl Fn((), A) -> B + 'a,
2224			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
2225		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
2226			fa.map(move |a| f((), a))
2227		}
2228	}
2229
2230	#[document_type_parameters("The error type.")]
2231	impl<E: 'static> FoldableWithIndex for TryThunkErrAppliedBrand<E> {
2232		/// Folds the `TryThunk` using a monoid, providing the index `()`.
2233		#[document_signature]
2234		#[document_type_parameters(
2235			"The lifetime of the computation.",
2236			"The brand of the cloneable function to use.",
2237			"The type of the success value inside the `TryThunk`.",
2238			"The monoid type."
2239		)]
2240		#[document_parameters(
2241			"The function to apply to the value and its index.",
2242			"The `TryThunk` to fold."
2243		)]
2244		#[document_returns("The monoid value.")]
2245		#[document_examples]
2246		///
2247		/// ```
2248		/// use fp_library::{
2249		/// 	brands::*,
2250		/// 	classes::foldable_with_index::FoldableWithIndex,
2251		/// 	types::*,
2252		/// };
2253		///
2254		/// let x: TryThunk<i32, ()> = TryThunk::pure(5);
2255		/// let y = <TryThunkErrAppliedBrand<()> as FoldableWithIndex>::fold_map_with_index::<
2256		/// 	RcFnBrand,
2257		/// 	_,
2258		/// 	_,
2259		/// >(|_, i: i32| i.to_string(), x);
2260		/// assert_eq!(y, "5".to_string());
2261		/// ```
2262		fn fold_map_with_index<'a, FnBrand, A: 'a + Clone, R: Monoid>(
2263			f: impl Fn((), A) -> R + 'a,
2264			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
2265		) -> R
2266		where
2267			FnBrand: LiftFn + 'a, {
2268			match fa.evaluate() {
2269				Ok(a) => f((), a),
2270				Err(_) => R::empty(),
2271			}
2272		}
2273	}
2274
2275	#[document_type_parameters("The success type.")]
2276	impl<A: 'static> WithIndex for TryThunkOkAppliedBrand<A> {
2277		type Index = ();
2278	}
2279
2280	#[document_type_parameters("The success type.")]
2281	impl<A: 'static> FunctorWithIndex for TryThunkOkAppliedBrand<A> {
2282		/// Maps a function over the error value in the `TryThunk`, providing the index `()`.
2283		#[document_signature]
2284		#[document_type_parameters(
2285			"The lifetime of the computation.",
2286			"The type of the error value inside the `TryThunk`.",
2287			"The type of the result of applying the function."
2288		)]
2289		#[document_parameters(
2290			"The function to apply to the error and its index.",
2291			"The `TryThunk` to map over."
2292		)]
2293		#[document_returns(
2294			"A new `TryThunk` containing the original success or the transformed error."
2295		)]
2296		#[document_examples]
2297		///
2298		/// ```
2299		/// use fp_library::{
2300		/// 	brands::TryThunkOkAppliedBrand,
2301		/// 	classes::functor_with_index::FunctorWithIndex,
2302		/// 	types::*,
2303		/// };
2304		///
2305		/// let x: TryThunk<i32, i32> = TryThunk::err(5);
2306		/// let y = <TryThunkOkAppliedBrand<i32> as FunctorWithIndex>::map_with_index(|_, e| e * 2, x);
2307		/// assert_eq!(y.evaluate(), Err(10));
2308		/// ```
2309		fn map_with_index<'a, E: 'a, E2: 'a>(
2310			f: impl Fn((), E) -> E2 + 'a,
2311			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
2312		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E2>) {
2313			fa.map_err(move |e| f((), e))
2314		}
2315	}
2316
2317	#[document_type_parameters("The success type.")]
2318	impl<A: 'static> FoldableWithIndex for TryThunkOkAppliedBrand<A> {
2319		/// Folds the `TryThunk` over the error using a monoid, providing the index `()`.
2320		#[document_signature]
2321		#[document_type_parameters(
2322			"The lifetime of the computation.",
2323			"The brand of the cloneable function to use.",
2324			"The type of the error value inside the `TryThunk`.",
2325			"The monoid type."
2326		)]
2327		#[document_parameters(
2328			"The function to apply to the error and its index.",
2329			"The `TryThunk` to fold."
2330		)]
2331		#[document_returns("The monoid value.")]
2332		#[document_examples]
2333		///
2334		/// ```
2335		/// use fp_library::{
2336		/// 	brands::*,
2337		/// 	classes::foldable_with_index::FoldableWithIndex,
2338		/// 	types::*,
2339		/// };
2340		///
2341		/// let x: TryThunk<i32, i32> = TryThunk::err(5);
2342		/// let y = <TryThunkOkAppliedBrand<i32> as FoldableWithIndex>::fold_map_with_index::<
2343		/// 	RcFnBrand,
2344		/// 	_,
2345		/// 	_,
2346		/// >(|_, e: i32| e.to_string(), x);
2347		/// assert_eq!(y, "5".to_string());
2348		/// ```
2349		fn fold_map_with_index<'a, FnBrand, E: 'a + Clone, R: Monoid>(
2350			f: impl Fn((), E) -> R + 'a,
2351			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
2352		) -> R
2353		where
2354			FnBrand: LiftFn + 'a, {
2355			match fa.evaluate() {
2356				Err(e) => f((), e),
2357				Ok(_) => R::empty(),
2358			}
2359		}
2360	}
2361}
2362pub use inner::*;
2363
2364#[cfg(test)]
2365#[expect(
2366	clippy::unwrap_used,
2367	clippy::panic,
2368	reason = "Tests use panicking operations for brevity and clarity"
2369)]
2370mod tests {
2371	use {
2372		super::*,
2373		crate::types::Thunk,
2374		quickcheck_macros::quickcheck,
2375	};
2376
2377	/// Tests success path.
2378	///
2379	/// Verifies that `TryThunk::ok` creates a successful computation.
2380	#[test]
2381	fn test_success() {
2382		let try_thunk: TryThunk<i32, ()> = TryThunk::ok(42);
2383		assert_eq!(try_thunk.evaluate(), Ok(42));
2384	}
2385
2386	/// Tests that `TryThunk::pure` creates a successful computation.
2387	#[test]
2388	fn test_pure() {
2389		let try_thunk: TryThunk<i32, ()> = TryThunk::pure(42);
2390		assert_eq!(try_thunk.evaluate(), Ok(42));
2391	}
2392
2393	/// Tests failure path.
2394	///
2395	/// Verifies that `TryThunk::err` creates a failed computation.
2396	#[test]
2397	fn test_failure() {
2398		let try_thunk: TryThunk<i32, &str> = TryThunk::err("error");
2399		assert_eq!(try_thunk.evaluate(), Err("error"));
2400	}
2401
2402	/// Tests `TryThunk::map`.
2403	///
2404	/// Verifies that `map` transforms the success value.
2405	#[test]
2406	fn test_map() {
2407		let try_thunk: TryThunk<i32, ()> = TryThunk::ok(21).map(|x| x * 2);
2408		assert_eq!(try_thunk.evaluate(), Ok(42));
2409	}
2410
2411	/// Tests `TryThunk::map_err`.
2412	///
2413	/// Verifies that `map_err` transforms the error value.
2414	#[test]
2415	fn test_map_err() {
2416		let try_thunk: TryThunk<i32, i32> = TryThunk::err(21).map_err(|x| x * 2);
2417		assert_eq!(try_thunk.evaluate(), Err(42));
2418	}
2419
2420	/// Tests `TryThunk::bind`.
2421	///
2422	/// Verifies that `bind` chains computations.
2423	#[test]
2424	fn test_bind() {
2425		let try_thunk: TryThunk<i32, ()> = TryThunk::ok(21).bind(|x| TryThunk::ok(x * 2));
2426		assert_eq!(try_thunk.evaluate(), Ok(42));
2427	}
2428
2429	/// Tests borrowing in TryThunk.
2430	///
2431	/// Verifies that `TryThunk` can capture references.
2432	#[test]
2433	fn test_borrowing() {
2434		let x = 42;
2435		let try_thunk: TryThunk<&i32, ()> = TryThunk::new(|| Ok(&x));
2436		assert_eq!(try_thunk.evaluate(), Ok(&42));
2437	}
2438
2439	/// Tests `TryThunk::bind` failure propagation.
2440	///
2441	/// Verifies that if the first computation fails, the second one is not executed.
2442	#[test]
2443	fn test_bind_failure() {
2444		let try_thunk = TryThunk::<i32, &str>::err("error").bind(|x| TryThunk::ok(x * 2));
2445		assert_eq!(try_thunk.evaluate(), Err("error"));
2446	}
2447
2448	/// Tests `TryThunk::map` failure propagation.
2449	///
2450	/// Verifies that `map` is not executed if the computation fails.
2451	#[test]
2452	fn test_map_failure() {
2453		let try_thunk = TryThunk::<i32, &str>::err("error").map(|x| x * 2);
2454		assert_eq!(try_thunk.evaluate(), Err("error"));
2455	}
2456
2457	/// Tests `TryThunk::map_err` success propagation.
2458	///
2459	/// Verifies that `map_err` is not executed if the computation succeeds.
2460	#[test]
2461	fn test_map_err_success() {
2462		let try_thunk = TryThunk::<i32, &str>::pure(42).map_err(|_| "new error");
2463		assert_eq!(try_thunk.evaluate(), Ok(42));
2464	}
2465
2466	/// Tests `From<Lazy>`.
2467	#[test]
2468	fn test_try_thunk_from_memo() {
2469		use crate::types::RcLazy;
2470		let memo = RcLazy::new(|| 42);
2471		let try_thunk: TryThunk<i32, ()> = TryThunk::from(memo);
2472		assert_eq!(try_thunk.evaluate(), Ok(42));
2473	}
2474
2475	/// Tests `From<TryLazy>`.
2476	#[test]
2477	fn test_try_thunk_from_try_memo() {
2478		use crate::types::RcTryLazy;
2479		let memo = RcTryLazy::new(|| Ok(42));
2480		let try_thunk: TryThunk<i32, ()> = TryThunk::from(memo);
2481		assert_eq!(try_thunk.evaluate(), Ok(42));
2482	}
2483
2484	/// Tests `Thunk::into_try`.
2485	///
2486	/// Verifies that `From<Thunk>` converts a `Thunk` into a `TryThunk` that succeeds.
2487	#[test]
2488	fn test_try_thunk_from_eval() {
2489		let eval = Thunk::pure(42);
2490		let try_thunk: TryThunk<i32, ()> = TryThunk::from(eval);
2491		assert_eq!(try_thunk.evaluate(), Ok(42));
2492	}
2493
2494	/// Tests `TryThunk::defer`.
2495	#[test]
2496	fn test_defer() {
2497		let try_thunk: TryThunk<i32, ()> = TryThunk::defer(|| TryThunk::ok(42));
2498		assert_eq!(try_thunk.evaluate(), Ok(42));
2499	}
2500
2501	/// Tests `TryThunk::catch`.
2502	///
2503	/// Verifies that `catch` recovers from failure.
2504	#[test]
2505	fn test_catch() {
2506		let try_thunk: TryThunk<i32, &str> = TryThunk::err("error").catch(|_| TryThunk::ok(42));
2507		assert_eq!(try_thunk.evaluate(), Ok(42));
2508	}
2509
2510	/// Tests `TryThunk::catch_with`.
2511	///
2512	/// Verifies that `catch_with` recovers from failure using a different error type.
2513	#[test]
2514	fn test_catch_with() {
2515		let recovered: TryThunk<i32, i32> =
2516			TryThunk::<i32, &str>::err("error").catch_with(|_| TryThunk::err(42));
2517		assert_eq!(recovered.evaluate(), Err(42));
2518
2519		let ok: TryThunk<i32, i32> = TryThunk::<i32, &str>::ok(1).catch_with(|_| TryThunk::err(42));
2520		assert_eq!(ok.evaluate(), Ok(1));
2521	}
2522
2523	/// Tests `TryThunkErrAppliedBrand` (Functor over Success).
2524	#[test]
2525	fn test_try_thunk_with_err_brand() {
2526		use crate::{
2527			brands::*,
2528			functions::*,
2529		};
2530
2531		// Functor (map over success)
2532		let try_thunk: TryThunk<i32, ()> = TryThunk::ok(10);
2533		let mapped = explicit::map::<TryThunkErrAppliedBrand<()>, _, _, _, _>(|x| x * 2, try_thunk);
2534		assert_eq!(mapped.evaluate(), Ok(20));
2535
2536		// Pointed (pure -> ok)
2537		let try_thunk: TryThunk<i32, ()> = pure::<TryThunkErrAppliedBrand<()>, _>(42);
2538		assert_eq!(try_thunk.evaluate(), Ok(42));
2539
2540		// Semimonad (bind over success)
2541		let try_thunk: TryThunk<i32, ()> = TryThunk::ok(10);
2542		let bound = explicit::bind::<TryThunkErrAppliedBrand<()>, _, _, _, _>(try_thunk, |x| {
2543			pure::<TryThunkErrAppliedBrand<()>, _>(x * 2)
2544		});
2545		assert_eq!(bound.evaluate(), Ok(20));
2546
2547		// Foldable (fold over success)
2548		let try_thunk: TryThunk<i32, ()> = TryThunk::ok(10);
2549		let folded = explicit::fold_right::<RcFnBrand, TryThunkErrAppliedBrand<()>, _, _, _, _>(
2550			|x, acc| x + acc,
2551			5,
2552			try_thunk,
2553		);
2554		assert_eq!(folded, 15);
2555	}
2556
2557	/// Tests `Bifunctor` for `TryThunkBrand`.
2558	#[test]
2559	fn test_bifunctor() {
2560		use crate::{
2561			brands::*,
2562			classes::bifunctor::*,
2563		};
2564
2565		let x: TryThunk<i32, i32> = TryThunk::ok(5);
2566		assert_eq!(bimap::<TryThunkBrand, _, _, _, _>(|e| e + 1, |s| s * 2, x).evaluate(), Ok(10));
2567
2568		let y: TryThunk<i32, i32> = TryThunk::err(5);
2569		assert_eq!(bimap::<TryThunkBrand, _, _, _, _>(|e| e + 1, |s| s * 2, y).evaluate(), Err(6));
2570	}
2571
2572	/// Tests `Bifoldable` for `TryThunkBrand` with `bi_fold_right`.
2573	///
2574	/// Verifies that error values are folded with `f` and success values with `g`.
2575	#[test]
2576	fn test_bifoldable_right() {
2577		use crate::{
2578			brands::*,
2579			functions::*,
2580		};
2581
2582		// Error case: f(3, 10) = 10 - 3 = 7
2583		assert_eq!(
2584			explicit::bi_fold_right::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
2585				(|e: i32, acc| acc - e, |s: i32, acc| acc + s),
2586				10,
2587				TryThunk::err(3),
2588			),
2589			7
2590		);
2591
2592		// Success case: g(5, 10) = 10 + 5 = 15
2593		assert_eq!(
2594			explicit::bi_fold_right::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
2595				(|e: i32, acc| acc - e, |s: i32, acc| acc + s),
2596				10,
2597				TryThunk::ok(5),
2598			),
2599			15
2600		);
2601	}
2602
2603	/// Tests `Bifoldable` for `TryThunkBrand` with `bi_fold_left`.
2604	///
2605	/// Verifies left-associative folding over both error and success values.
2606	#[test]
2607	fn test_bifoldable_left() {
2608		use crate::{
2609			brands::*,
2610			functions::*,
2611		};
2612
2613		assert_eq!(
2614			explicit::bi_fold_left::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
2615				(|acc, e: i32| acc - e, |acc, s: i32| acc + s),
2616				10,
2617				TryThunk::err(3),
2618			),
2619			7
2620		);
2621
2622		assert_eq!(
2623			explicit::bi_fold_left::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
2624				(|acc, e: i32| acc - e, |acc, s: i32| acc + s),
2625				10,
2626				TryThunk::ok(5),
2627			),
2628			15
2629		);
2630	}
2631
2632	/// Tests `Bifoldable` for `TryThunkBrand` with `bi_fold_map`.
2633	///
2634	/// Verifies that both error and success values can be mapped to a monoid.
2635	#[test]
2636	fn test_bifoldable_map() {
2637		use crate::{
2638			brands::*,
2639			functions::*,
2640		};
2641
2642		assert_eq!(
2643			explicit::bi_fold_map::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
2644				(|e: i32| e.to_string(), |s: i32| s.to_string()),
2645				TryThunk::err(3),
2646			),
2647			"3".to_string()
2648		);
2649
2650		assert_eq!(
2651			explicit::bi_fold_map::<RcFnBrand, TryThunkBrand, _, _, _, _, _>(
2652				(|e: i32| e.to_string(), |s: i32| s.to_string()),
2653				TryThunk::ok(5),
2654			),
2655			"5".to_string()
2656		);
2657	}
2658
2659	/// Tests `MonadRec` for `TryThunkOkAppliedBrand` (tail recursion over error).
2660	///
2661	/// Verifies that the loop continues on `ControlFlow::Continue` and terminates on `ControlFlow::Break`.
2662	#[test]
2663	fn test_monad_rec_ok_applied() {
2664		use {
2665			crate::{
2666				brands::*,
2667				functions::*,
2668			},
2669			core::ops::ControlFlow,
2670		};
2671
2672		let result = tail_rec_m::<TryThunkOkAppliedBrand<i32>, _, _>(
2673			|x| {
2674				pure::<TryThunkOkAppliedBrand<i32>, _>(
2675					if x < 100 { ControlFlow::Continue(x + 1) } else { ControlFlow::Break(x) },
2676				)
2677			},
2678			0,
2679		);
2680		assert_eq!(result.evaluate(), Err(100));
2681	}
2682
2683	/// Tests `MonadRec` for `TryThunkOkAppliedBrand` short-circuits on `Ok`.
2684	///
2685	/// Verifies that encountering an `Ok` value terminates the loop immediately.
2686	#[test]
2687	fn test_monad_rec_ok_applied_short_circuit() {
2688		use {
2689			crate::{
2690				brands::*,
2691				functions::*,
2692			},
2693			core::ops::ControlFlow,
2694		};
2695
2696		let result = tail_rec_m::<TryThunkOkAppliedBrand<i32>, _, _>(
2697			|x: i32| {
2698				if x == 5 {
2699					TryThunk::ok(42)
2700				} else {
2701					pure::<TryThunkOkAppliedBrand<i32>, _>(ControlFlow::<i32, i32>::Continue(x + 1))
2702				}
2703			},
2704			0,
2705		);
2706		assert_eq!(result.evaluate(), Ok(42));
2707	}
2708
2709	/// Tests `catch_unwind` on `TryThunk`.
2710	///
2711	/// Verifies that panics are caught and converted to `Err(String)`.
2712	#[test]
2713	fn test_catch_unwind() {
2714		let thunk = TryThunk::<i32, String>::catch_unwind(|| {
2715			if true {
2716				panic!("oops")
2717			}
2718			42
2719		});
2720		assert_eq!(thunk.evaluate(), Err("oops".to_string()));
2721	}
2722
2723	/// Tests `catch_unwind` on `TryThunk` with a non-panicking closure.
2724	///
2725	/// Verifies that a successful closure wraps the value in `Ok`.
2726	#[test]
2727	fn test_catch_unwind_success() {
2728		let thunk = TryThunk::<i32, String>::catch_unwind(|| 42);
2729		assert_eq!(thunk.evaluate(), Ok(42));
2730	}
2731
2732	/// Tests `TryThunkOkAppliedBrand` (Functor over Error).
2733	#[test]
2734	fn test_try_thunk_with_ok_brand() {
2735		use crate::{
2736			brands::*,
2737			functions::*,
2738		};
2739
2740		// Functor (map over error)
2741		let try_thunk: TryThunk<i32, i32> = TryThunk::err(10);
2742		let mapped = explicit::map::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(|x| x * 2, try_thunk);
2743		assert_eq!(mapped.evaluate(), Err(20));
2744
2745		// Pointed (pure -> err)
2746		let try_thunk: TryThunk<i32, i32> = pure::<TryThunkOkAppliedBrand<i32>, _>(42);
2747		assert_eq!(try_thunk.evaluate(), Err(42));
2748
2749		// Semimonad (bind over error)
2750		let try_thunk: TryThunk<i32, i32> = TryThunk::err(10);
2751		let bound = explicit::bind::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(try_thunk, |x| {
2752			pure::<TryThunkOkAppliedBrand<i32>, _>(x * 2)
2753		});
2754		assert_eq!(bound.evaluate(), Err(20));
2755
2756		// Foldable (fold over error)
2757		let try_thunk: TryThunk<i32, i32> = TryThunk::err(10);
2758		let folded = explicit::fold_right::<RcFnBrand, TryThunkOkAppliedBrand<i32>, _, _, _, _>(
2759			|x, acc| x + acc,
2760			5,
2761			try_thunk,
2762		);
2763		assert_eq!(folded, 15);
2764	}
2765
2766	/// Tests `From<Result>` with `Ok`.
2767	///
2768	/// Verifies that converting an `Ok` result produces a successful `TryThunk`.
2769	#[test]
2770	fn test_try_thunk_from_result_ok() {
2771		let try_thunk: TryThunk<i32, String> = TryThunk::from(Ok(42));
2772		assert_eq!(try_thunk.evaluate(), Ok(42));
2773	}
2774
2775	/// Tests `From<Result>` with `Err`.
2776	///
2777	/// Verifies that converting an `Err` result produces a failed `TryThunk`.
2778	#[test]
2779	fn test_try_thunk_from_result_err() {
2780		let try_thunk: TryThunk<i32, String> = TryThunk::from(Err("error".to_string()));
2781		assert_eq!(try_thunk.evaluate(), Err("error".to_string()));
2782	}
2783
2784	/// Tests `From<TrySendThunk>` with `Ok`.
2785	///
2786	/// Verifies that converting a successful `TrySendThunk` produces a successful `TryThunk`.
2787	#[test]
2788	fn test_try_thunk_from_try_send_thunk_ok() {
2789		use crate::types::TrySendThunk;
2790		let send: TrySendThunk<i32, ()> = TrySendThunk::pure(42);
2791		let thunk: TryThunk<i32, ()> = TryThunk::from(send);
2792		assert_eq!(thunk.evaluate(), Ok(42));
2793	}
2794
2795	/// Tests `From<TrySendThunk>` with `Err`.
2796	///
2797	/// Verifies that converting a failed `TrySendThunk` produces a failed `TryThunk`.
2798	#[test]
2799	fn test_try_thunk_from_try_send_thunk_err() {
2800		use crate::types::TrySendThunk;
2801		let send: TrySendThunk<i32, String> = TrySendThunk::err("fail".to_string());
2802		let thunk: TryThunk<i32, String> = TryThunk::from(send);
2803		assert_eq!(thunk.evaluate(), Err("fail".to_string()));
2804	}
2805
2806	// QuickCheck Law Tests
2807
2808	// Functor Laws (via HKT, TryThunkErrAppliedBrand)
2809
2810	/// Functor identity: `map(id, t).evaluate() == t.evaluate()`.
2811	#[quickcheck]
2812	fn functor_identity(x: i32) -> bool {
2813		use crate::{
2814			brands::*,
2815			functions::*,
2816		};
2817		let t: TryThunk<i32, i32> = TryThunk::ok(x);
2818		explicit::map::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(|a| a, t).evaluate() == Ok(x)
2819	}
2820
2821	/// Functor composition: `map(f . g, t) == map(f, map(g, t))`.
2822	#[quickcheck]
2823	fn functor_composition(x: i32) -> bool {
2824		use crate::{
2825			brands::*,
2826			functions::*,
2827		};
2828		let f = |a: i32| a.wrapping_add(1);
2829		let g = |a: i32| a.wrapping_mul(2);
2830		let lhs = explicit::map::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(
2831			move |a| f(g(a)),
2832			TryThunk::ok(x),
2833		)
2834		.evaluate();
2835		let rhs = explicit::map::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(
2836			f,
2837			explicit::map::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(g, TryThunk::ok(x)),
2838		)
2839		.evaluate();
2840		lhs == rhs
2841	}
2842
2843	// Monad Laws (via HKT, TryThunkErrAppliedBrand)
2844
2845	/// Monad left identity: `pure(a).bind(f) == f(a)` (for Ok values).
2846	#[quickcheck]
2847	fn monad_left_identity(a: i32) -> bool {
2848		use crate::{
2849			brands::*,
2850			functions::*,
2851		};
2852		let f = |x: i32| pure::<TryThunkErrAppliedBrand<i32>, _>(x.wrapping_mul(2));
2853		let lhs = explicit::bind::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(
2854			pure::<TryThunkErrAppliedBrand<i32>, _>(a),
2855			f,
2856		)
2857		.evaluate();
2858		let rhs = f(a).evaluate();
2859		lhs == rhs
2860	}
2861
2862	/// Monad right identity: `t.bind(pure) == t`.
2863	#[quickcheck]
2864	fn monad_right_identity(x: i32) -> bool {
2865		use crate::{
2866			brands::*,
2867			functions::*,
2868		};
2869		let lhs = explicit::bind::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(
2870			pure::<TryThunkErrAppliedBrand<i32>, _>(x),
2871			pure::<TryThunkErrAppliedBrand<i32>, _>,
2872		)
2873		.evaluate();
2874		lhs == Ok(x)
2875	}
2876
2877	/// Monad associativity: `m.bind(f).bind(g) == m.bind(|a| f(a).bind(g))`.
2878	#[quickcheck]
2879	fn monad_associativity(x: i32) -> bool {
2880		use crate::{
2881			brands::*,
2882			functions::*,
2883		};
2884		let f = |a: i32| pure::<TryThunkErrAppliedBrand<i32>, _>(a.wrapping_add(1));
2885		let g = |a: i32| pure::<TryThunkErrAppliedBrand<i32>, _>(a.wrapping_mul(3));
2886		let m: TryThunk<i32, i32> = TryThunk::ok(x);
2887		let m2: TryThunk<i32, i32> = TryThunk::ok(x);
2888		let lhs = explicit::bind::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(
2889			explicit::bind::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(m, f),
2890			g,
2891		)
2892		.evaluate();
2893		let rhs = explicit::bind::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(m2, move |a| {
2894			explicit::bind::<TryThunkErrAppliedBrand<i32>, _, _, _, _>(f(a), g)
2895		})
2896		.evaluate();
2897		lhs == rhs
2898	}
2899
2900	/// Error short-circuit: `TryThunk::err(e).bind(f).evaluate() == Err(e)`.
2901	#[quickcheck]
2902	fn error_short_circuit(e: i32) -> bool {
2903		let t: TryThunk<i32, i32> = TryThunk::err(e);
2904		t.bind(|x| TryThunk::ok(x.wrapping_add(1))).evaluate() == Err(e)
2905	}
2906
2907	/// Tests `TryThunk::lift2` with two successful values.
2908	///
2909	/// Verifies that `lift2` combines results from both computations.
2910	#[test]
2911	fn test_lift2_ok_ok() {
2912		let t1: TryThunk<i32, String> = TryThunk::ok(10);
2913		let t2: TryThunk<i32, String> = TryThunk::ok(20);
2914		let t3 = t1.lift2(t2, |a, b| a + b);
2915		assert_eq!(t3.evaluate(), Ok(30));
2916	}
2917
2918	/// Tests `TryThunk::lift2` short-circuits on first error.
2919	///
2920	/// Verifies that if the first computation fails, the second is not evaluated.
2921	#[test]
2922	fn test_lift2_err_ok() {
2923		let t1: TryThunk<i32, String> = TryThunk::err("first".to_string());
2924		let t2: TryThunk<i32, String> = TryThunk::ok(20);
2925		let t3 = t1.lift2(t2, |a, b| a + b);
2926		assert_eq!(t3.evaluate(), Err("first".to_string()));
2927	}
2928
2929	/// Tests `TryThunk::lift2` propagates second error.
2930	///
2931	/// Verifies that if the second computation fails, the error is propagated.
2932	#[test]
2933	fn test_lift2_ok_err() {
2934		let t1: TryThunk<i32, String> = TryThunk::ok(10);
2935		let t2: TryThunk<i32, String> = TryThunk::err("second".to_string());
2936		let t3 = t1.lift2(t2, |a, b| a + b);
2937		assert_eq!(t3.evaluate(), Err("second".to_string()));
2938	}
2939
2940	/// Tests `TryThunk::then` with two successful values.
2941	///
2942	/// Verifies that `then` discards the first result and returns the second.
2943	#[test]
2944	fn test_then_ok_ok() {
2945		let t1: TryThunk<i32, String> = TryThunk::ok(10);
2946		let t2: TryThunk<i32, String> = TryThunk::ok(20);
2947		let t3 = t1.then(t2);
2948		assert_eq!(t3.evaluate(), Ok(20));
2949	}
2950
2951	/// Tests `TryThunk::then` short-circuits on first error.
2952	///
2953	/// Verifies that if the first computation fails, the second is not evaluated.
2954	#[test]
2955	fn test_then_err_ok() {
2956		let t1: TryThunk<i32, String> = TryThunk::err("first".to_string());
2957		let t2: TryThunk<i32, String> = TryThunk::ok(20);
2958		let t3 = t1.then(t2);
2959		assert_eq!(t3.evaluate(), Err("first".to_string()));
2960	}
2961
2962	/// Tests `TryThunk::then` propagates second error.
2963	///
2964	/// Verifies that if the second computation fails, the error is propagated.
2965	#[test]
2966	fn test_then_ok_err() {
2967		let t1: TryThunk<i32, String> = TryThunk::ok(10);
2968		let t2: TryThunk<i32, String> = TryThunk::err("second".to_string());
2969		let t3 = t1.then(t2);
2970		assert_eq!(t3.evaluate(), Err("second".to_string()));
2971	}
2972
2973	/// Tests `TryThunk::into_rc_try_lazy` basic usage.
2974	///
2975	/// Verifies that converting a thunk produces a lazy value with the same result.
2976	#[test]
2977	fn test_into_rc_try_lazy() {
2978		let thunk: TryThunk<i32, ()> = TryThunk::ok(42);
2979		let lazy = thunk.into_rc_try_lazy();
2980		assert_eq!(lazy.evaluate(), Ok(&42));
2981	}
2982
2983	/// Tests `TryThunk::into_rc_try_lazy` caching behavior.
2984	///
2985	/// Verifies that the memoized value is computed only once.
2986	#[test]
2987	fn test_into_rc_try_lazy_caching() {
2988		use std::{
2989			cell::RefCell,
2990			rc::Rc,
2991		};
2992
2993		let counter = Rc::new(RefCell::new(0));
2994		let counter_clone = counter.clone();
2995		let thunk: TryThunk<i32, ()> = TryThunk::new(move || {
2996			*counter_clone.borrow_mut() += 1;
2997			Ok(42)
2998		});
2999		let lazy = thunk.into_rc_try_lazy();
3000
3001		assert_eq!(*counter.borrow(), 0);
3002		assert_eq!(lazy.evaluate(), Ok(&42));
3003		assert_eq!(*counter.borrow(), 1);
3004		assert_eq!(lazy.evaluate(), Ok(&42));
3005		assert_eq!(*counter.borrow(), 1);
3006	}
3007
3008	/// Tests `TryThunk::into_arc_try_lazy` basic usage.
3009	///
3010	/// Verifies that converting a thunk produces a thread-safe lazy value.
3011	#[test]
3012	fn test_into_arc_try_lazy() {
3013		let thunk: TryThunk<i32, ()> = TryThunk::ok(42);
3014		let lazy = thunk.into_arc_try_lazy();
3015		assert_eq!(lazy.evaluate(), Ok(&42));
3016	}
3017
3018	/// Tests `TryThunk::into_arc_try_lazy` is Send and Sync.
3019	///
3020	/// Verifies that the resulting `ArcTryLazy` can be shared across threads.
3021	#[test]
3022	fn test_into_arc_try_lazy_send_sync() {
3023		use std::thread;
3024
3025		let thunk: TryThunk<i32, String> = TryThunk::ok(42);
3026		let lazy = thunk.into_arc_try_lazy();
3027		let lazy_clone = lazy.clone();
3028
3029		let handle = thread::spawn(move || {
3030			assert_eq!(lazy_clone.evaluate(), Ok(&42));
3031		});
3032
3033		assert_eq!(lazy.evaluate(), Ok(&42));
3034		handle.join().unwrap();
3035	}
3036
3037	/// Tests `TryThunk::catch_unwind_with` with a panicking closure.
3038	///
3039	/// Verifies that the custom handler converts the panic payload.
3040	#[test]
3041	fn test_catch_unwind_with_panic() {
3042		let thunk = TryThunk::<i32, i32>::catch_unwind_with(
3043			|| {
3044				if true {
3045					panic!("oops")
3046				}
3047				42
3048			},
3049			|_payload| -1,
3050		);
3051		assert_eq!(thunk.evaluate(), Err(-1));
3052	}
3053
3054	/// Tests `TryThunk::catch_unwind_with` with a non-panicking closure.
3055	///
3056	/// Verifies that a successful closure wraps the value in `Ok`.
3057	#[test]
3058	fn test_catch_unwind_with_success() {
3059		let thunk = TryThunk::<i32, i32>::catch_unwind_with(|| 42, |_payload| -1);
3060		assert_eq!(thunk.evaluate(), Ok(42));
3061	}
3062
3063	// 7.3: Bifunctor law QuickCheck tests for TryThunkBrand
3064
3065	/// Bifunctor identity: `bimap(id, id, t) == t`.
3066	#[quickcheck]
3067	fn bifunctor_identity_ok(x: i32) -> bool {
3068		use crate::{
3069			brands::*,
3070			classes::bifunctor::*,
3071		};
3072		let t: TryThunk<i32, i32> = TryThunk::ok(x);
3073		bimap::<TryThunkBrand, _, _, _, _>(|e| e, |a| a, t).evaluate() == Ok(x)
3074	}
3075
3076	/// Bifunctor identity on error path: `bimap(id, id, err(e)) == err(e)`.
3077	#[quickcheck]
3078	fn bifunctor_identity_err(e: i32) -> bool {
3079		use crate::{
3080			brands::*,
3081			classes::bifunctor::*,
3082		};
3083		let t: TryThunk<i32, i32> = TryThunk::err(e);
3084		bimap::<TryThunkBrand, _, _, _, _>(|e| e, |a| a, t).evaluate() == Err(e)
3085	}
3086
3087	/// Bifunctor composition: `bimap(f1 . f2, g1 . g2, t) == bimap(f1, g1, bimap(f2, g2, t))`.
3088	#[quickcheck]
3089	fn bifunctor_composition_ok(x: i32) -> bool {
3090		use crate::{
3091			brands::*,
3092			classes::bifunctor::*,
3093		};
3094		let f1 = |a: i32| a.wrapping_add(1);
3095		let f2 = |a: i32| a.wrapping_mul(2);
3096		let g1 = |a: i32| a.wrapping_add(10);
3097		let g2 = |a: i32| a.wrapping_mul(3);
3098
3099		let lhs = bimap::<TryThunkBrand, _, _, _, _>(
3100			move |e| f1(f2(e)),
3101			move |a| g1(g2(a)),
3102			TryThunk::ok(x),
3103		)
3104		.evaluate();
3105		let rhs = bimap::<TryThunkBrand, _, _, _, _>(
3106			f1,
3107			g1,
3108			bimap::<TryThunkBrand, _, _, _, _>(f2, g2, TryThunk::ok(x)),
3109		)
3110		.evaluate();
3111		lhs == rhs
3112	}
3113
3114	/// Bifunctor composition on error path.
3115	#[quickcheck]
3116	fn bifunctor_composition_err(e: i32) -> bool {
3117		use crate::{
3118			brands::*,
3119			classes::bifunctor::*,
3120		};
3121		let f1 = |a: i32| a.wrapping_add(1);
3122		let f2 = |a: i32| a.wrapping_mul(2);
3123		let g1 = |a: i32| a.wrapping_add(10);
3124		let g2 = |a: i32| a.wrapping_mul(3);
3125
3126		let lhs = bimap::<TryThunkBrand, _, _, _, _>(
3127			move |e| f1(f2(e)),
3128			move |a| g1(g2(a)),
3129			TryThunk::err(e),
3130		)
3131		.evaluate();
3132		let rhs = bimap::<TryThunkBrand, _, _, _, _>(
3133			f1,
3134			g1,
3135			bimap::<TryThunkBrand, _, _, _, _>(f2, g2, TryThunk::err(e)),
3136		)
3137		.evaluate();
3138		lhs == rhs
3139	}
3140
3141	// 7.4: Error-channel monad law QuickCheck tests via TryThunkOkAppliedBrand
3142
3143	/// Error-channel monad left identity: `pure(a).bind(f) == f(a)`.
3144	#[quickcheck]
3145	fn error_monad_left_identity(a: i32) -> bool {
3146		use crate::{
3147			brands::*,
3148			functions::*,
3149		};
3150		let f = |x: i32| pure::<TryThunkOkAppliedBrand<i32>, _>(x.wrapping_mul(2));
3151		let lhs = explicit::bind::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(
3152			pure::<TryThunkOkAppliedBrand<i32>, _>(a),
3153			f,
3154		)
3155		.evaluate();
3156		let rhs = f(a).evaluate();
3157		lhs == rhs
3158	}
3159
3160	/// Error-channel monad right identity: `m.bind(pure) == m`.
3161	#[quickcheck]
3162	fn error_monad_right_identity(x: i32) -> bool {
3163		use crate::{
3164			brands::*,
3165			functions::*,
3166		};
3167		let lhs = explicit::bind::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(
3168			pure::<TryThunkOkAppliedBrand<i32>, _>(x),
3169			pure::<TryThunkOkAppliedBrand<i32>, _>,
3170		)
3171		.evaluate();
3172		lhs == Err(x)
3173	}
3174
3175	/// Error-channel monad associativity: `m.bind(f).bind(g) == m.bind(|a| f(a).bind(g))`.
3176	#[quickcheck]
3177	fn error_monad_associativity(x: i32) -> bool {
3178		use crate::{
3179			brands::*,
3180			functions::*,
3181		};
3182		let f = |a: i32| pure::<TryThunkOkAppliedBrand<i32>, _>(a.wrapping_add(1));
3183		let g = |a: i32| pure::<TryThunkOkAppliedBrand<i32>, _>(a.wrapping_mul(3));
3184		let m: TryThunk<i32, i32> = TryThunk::err(x);
3185		let m2: TryThunk<i32, i32> = TryThunk::err(x);
3186		let lhs = explicit::bind::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(
3187			explicit::bind::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(m, f),
3188			g,
3189		)
3190		.evaluate();
3191		let rhs = explicit::bind::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(m2, move |a| {
3192			explicit::bind::<TryThunkOkAppliedBrand<i32>, _, _, _, _>(f(a), g)
3193		})
3194		.evaluate();
3195		lhs == rhs
3196	}
3197
3198	// 7.5: Semigroup/Monoid law QuickCheck tests for TryThunk
3199
3200	/// Semigroup associativity for TryThunk: `append(append(a, b), c) == append(a, append(b, c))`.
3201	#[quickcheck]
3202	fn try_thunk_semigroup_associativity(
3203		a: String,
3204		b: String,
3205		c: String,
3206	) -> bool {
3207		use crate::classes::semigroup::append;
3208
3209		let ta: TryThunk<String, ()> = TryThunk::ok(a.clone());
3210		let tb: TryThunk<String, ()> = TryThunk::ok(b.clone());
3211		let tc: TryThunk<String, ()> = TryThunk::ok(c.clone());
3212		let ta2: TryThunk<String, ()> = TryThunk::ok(a);
3213		let tb2: TryThunk<String, ()> = TryThunk::ok(b);
3214		let tc2: TryThunk<String, ()> = TryThunk::ok(c);
3215		let lhs = append(append(ta, tb), tc).evaluate();
3216		let rhs = append(ta2, append(tb2, tc2)).evaluate();
3217		lhs == rhs
3218	}
3219
3220	/// Monoid left identity for TryThunk: `append(empty(), a) == a`.
3221	#[quickcheck]
3222	fn try_thunk_monoid_left_identity(x: String) -> bool {
3223		use crate::classes::{
3224			monoid::empty,
3225			semigroup::append,
3226		};
3227
3228		let a: TryThunk<String, ()> = TryThunk::ok(x.clone());
3229		let lhs: TryThunk<String, ()> = append(empty(), a);
3230		lhs.evaluate() == Ok(x)
3231	}
3232
3233	/// Monoid right identity for TryThunk: `append(a, empty()) == a`.
3234	#[quickcheck]
3235	fn try_thunk_monoid_right_identity(x: String) -> bool {
3236		use crate::classes::{
3237			monoid::empty,
3238			semigroup::append,
3239		};
3240
3241		let a: TryThunk<String, ()> = TryThunk::ok(x.clone());
3242		let rhs: TryThunk<String, ()> = append(a, empty());
3243		rhs.evaluate() == Ok(x)
3244	}
3245
3246	// 7.6: Thread safety test for TryThunk::into_arc_try_lazy
3247
3248	/// Tests that `TryThunk::into_arc_try_lazy` produces a thread-safe value
3249	/// and all threads see the same cached result.
3250	#[test]
3251	fn test_into_arc_try_lazy_thread_safety() {
3252		use std::{
3253			sync::{
3254				Arc,
3255				atomic::{
3256					AtomicUsize,
3257					Ordering,
3258				},
3259			},
3260			thread,
3261		};
3262
3263		let counter = Arc::new(AtomicUsize::new(0));
3264		let counter_clone = Arc::clone(&counter);
3265		let thunk: TryThunk<i32, String> = TryThunk::new(move || {
3266			counter_clone.fetch_add(1, Ordering::SeqCst);
3267			Ok(42)
3268		});
3269		let lazy = thunk.into_arc_try_lazy();
3270
3271		let handles: Vec<_> = (0 .. 8)
3272			.map(|_| {
3273				let lazy_clone = lazy.clone();
3274				thread::spawn(move || {
3275					assert_eq!(lazy_clone.evaluate(), Ok(&42));
3276				})
3277			})
3278			.collect();
3279
3280		assert_eq!(lazy.evaluate(), Ok(&42));
3281		for h in handles {
3282			h.join().unwrap();
3283		}
3284
3285		// The closure should have been invoked exactly once.
3286		assert_eq!(counter.load(Ordering::SeqCst), 1);
3287	}
3288
3289	/// Tests `Semigroup::append` short-circuits when the first operand is `Err`.
3290	///
3291	/// Verifies that the second operand is never evaluated when the first fails.
3292	#[test]
3293	fn test_semigroup_append_first_err_short_circuits() {
3294		use {
3295			crate::classes::semigroup::append,
3296			std::cell::Cell,
3297		};
3298
3299		let counter = Cell::new(0u32);
3300		let t1: TryThunk<String, &str> = TryThunk::err("first failed");
3301		let t2: TryThunk<String, &str> = TryThunk::new(|| {
3302			counter.set(counter.get() + 1);
3303			Ok("second".to_string())
3304		});
3305		let result = append(t1, t2);
3306		assert_eq!(result.evaluate(), Err("first failed"));
3307		assert_eq!(counter.get(), 0, "second operand should not have been evaluated");
3308	}
3309
3310	/// Tests `Semigroup::append` propagates the error when the second operand fails.
3311	///
3312	/// Verifies that when the first operand succeeds but the second fails, the error
3313	/// from the second operand is returned.
3314	#[test]
3315	fn test_semigroup_append_second_err_propagates() {
3316		use crate::classes::semigroup::append;
3317
3318		let t1: TryThunk<String, &str> = TryThunk::pure("hello".to_string());
3319		let t2: TryThunk<String, &str> = TryThunk::err("second failed");
3320		let result = append(t1, t2);
3321		assert_eq!(result.evaluate(), Err("second failed"));
3322	}
3323
3324	/// Tests `TryThunk::tail_rec_m` computes a simple recursive sum.
3325	///
3326	/// Verifies that the loop accumulates correctly and terminates with `Break`.
3327	#[test]
3328	fn test_tail_rec_m_success() {
3329		use core::ops::ControlFlow;
3330		let result: TryThunk<i32, ()> = TryThunk::tail_rec_m(
3331			|x| {
3332				TryThunk::ok(
3333					if x < 1000 { ControlFlow::Continue(x + 1) } else { ControlFlow::Break(x) },
3334				)
3335			},
3336			0,
3337		);
3338		assert_eq!(result.evaluate(), Ok(1000));
3339	}
3340
3341	/// Tests `TryThunk::tail_rec_m` short-circuits on error.
3342	///
3343	/// Verifies that when the step function returns `Err`, the loop terminates
3344	/// immediately and propagates the error.
3345	#[test]
3346	fn test_tail_rec_m_early_error() {
3347		use core::ops::ControlFlow;
3348		let result: TryThunk<i32, &str> = TryThunk::tail_rec_m(
3349			|x| {
3350				if x == 5 {
3351					TryThunk::err("stopped at 5")
3352				} else {
3353					TryThunk::ok(ControlFlow::Continue(x + 1))
3354				}
3355			},
3356			0,
3357		);
3358		assert_eq!(result.evaluate(), Err("stopped at 5"));
3359	}
3360
3361	/// Tests `TryThunk::tail_rec_m` stack safety with many iterations.
3362	///
3363	/// Verifies that the loop does not overflow the stack with 100,000 iterations.
3364	#[test]
3365	fn test_tail_rec_m_stack_safety() {
3366		use core::ops::ControlFlow;
3367		let iterations: i64 = 100_000;
3368		let result: TryThunk<i64, ()> = TryThunk::tail_rec_m(
3369			|acc| {
3370				TryThunk::ok(
3371					if acc < iterations {
3372						ControlFlow::Continue(acc + 1)
3373					} else {
3374						ControlFlow::Break(acc)
3375					},
3376				)
3377			},
3378			0i64,
3379		);
3380		assert_eq!(result.evaluate(), Ok(iterations));
3381	}
3382
3383	/// Tests `TryThunk::arc_tail_rec_m` computes correctly and tracks calls.
3384	///
3385	/// Verifies that the Arc-wrapped variant produces the same result as
3386	/// `tail_rec_m` and that the step function is called the expected number
3387	/// of times.
3388	#[test]
3389	fn test_arc_tail_rec_m_success() {
3390		use {
3391			core::ops::ControlFlow,
3392			std::sync::{
3393				Arc,
3394				atomic::{
3395					AtomicUsize,
3396					Ordering,
3397				},
3398			},
3399		};
3400		let counter = Arc::new(AtomicUsize::new(0));
3401		let counter_clone = Arc::clone(&counter);
3402		let result: TryThunk<i32, ()> = TryThunk::arc_tail_rec_m(
3403			move |x| {
3404				counter_clone.fetch_add(1, Ordering::SeqCst);
3405				TryThunk::ok(
3406					if x < 100 { ControlFlow::Continue(x + 1) } else { ControlFlow::Break(x) },
3407				)
3408			},
3409			0,
3410		);
3411		assert_eq!(result.evaluate(), Ok(100));
3412		assert_eq!(counter.load(Ordering::SeqCst), 101);
3413	}
3414
3415	/// Tests `TryThunk::arc_tail_rec_m` short-circuits on error.
3416	///
3417	/// Verifies that the Arc-wrapped variant propagates errors immediately.
3418	#[test]
3419	fn test_arc_tail_rec_m_early_error() {
3420		use core::ops::ControlFlow;
3421		let result: TryThunk<i32, &str> = TryThunk::arc_tail_rec_m(
3422			|x| {
3423				if x == 5 {
3424					TryThunk::err("stopped at 5")
3425				} else {
3426					TryThunk::ok(ControlFlow::Continue(x + 1))
3427				}
3428			},
3429			0,
3430		);
3431		assert_eq!(result.evaluate(), Err("stopped at 5"));
3432	}
3433
3434	/// Tests `TryThunk::arc_tail_rec_m` stack safety with many iterations.
3435	///
3436	/// Verifies that the Arc-wrapped variant does not overflow the stack.
3437	#[test]
3438	fn test_arc_tail_rec_m_stack_safety() {
3439		use core::ops::ControlFlow;
3440		let iterations: i64 = 100_000;
3441		let result: TryThunk<i64, ()> = TryThunk::arc_tail_rec_m(
3442			|acc| {
3443				TryThunk::ok(
3444					if acc < iterations {
3445						ControlFlow::Continue(acc + 1)
3446					} else {
3447						ControlFlow::Break(acc)
3448					},
3449				)
3450			},
3451			0i64,
3452		);
3453		assert_eq!(result.evaluate(), Ok(iterations));
3454	}
3455}