Skip to main content

fp_library/types/
thunk.rs

1//! Deferred, non-memoized computation with higher-kinded type support.
2//!
3//! Builds computation chains without stack safety guarantees but supports borrowing and lifetime polymorphism. Each call to [`Thunk::evaluate`] re-executes the computation. For stack-safe alternatives, use [`Trampoline`](crate::types::Trampoline).
4
5#[fp_macros::document_module]
6mod inner {
7	use crate::{
8		Apply,
9		brands::ThunkBrand,
10		classes::{
11			ApplyFirst, ApplySecond, CloneableFn, Deferrable, Evaluable, Foldable, Functor, Lift,
12			MonadRec, Monoid, Pointed, Semiapplicative, Semigroup, Semimonad,
13		},
14		impl_kind,
15		kinds::*,
16		types::{Lazy, LazyConfig, Step},
17	};
18	use fp_macros::{document_fields, document_parameters, document_type_parameters};
19
20	/// A deferred computation that produces a value of type `A`.
21	///
22	/// `Thunk` is NOT memoized - each call to [`Thunk::evaluate`] re-executes the computation.
23	/// This type exists to build computation chains without allocation overhead.
24	///
25	/// Unlike [`Trampoline`](crate::types::Trampoline), `Thunk` does NOT require `'static` and CAN implement
26	/// HKT traits like [`Functor`], [`Semimonad`], etc.
27	///
28	/// ### Higher-Kinded Type Representation
29	///
30	/// The higher-kinded representation of this type constructor is [`ThunkBrand`](crate::brands::ThunkBrand),
31	/// which is fully polymorphic over the result type.
32	///
33	/// ### Trade-offs vs `Trampoline`
34	///
35	/// | Aspect         | `Thunk<'a, A>`              | `Trampoline<A>`            |
36	/// |----------------|-----------------------------|----------------------------|
37	/// | HKT compatible | ✅ Yes                      | ❌ No (requires `'static`) |
38	/// | Stack-safe     | ⚠️ Partial (tail_rec_m only) | ✅ Yes (unlimited)         |
39	/// | Lifetime       | `'a` (can borrow)           | `'static` only             |
40	/// | Use case       | Glue code, composition      | Deep recursion, pipelines  |
41	///
42	/// ### Algebraic Properties
43	///
44	/// `Thunk` is a proper Monad:
45	/// - `pure(a).evaluate() == a` (left identity).
46	/// - `thunk.bind(pure) == thunk` (right identity).
47	/// - `thunk.bind(f).bind(g) == thunk.bind(|a| f(a).bind(g))` (associativity).
48	///
49	/// ### Limitations
50	///
51	/// **Cannot implement `Traversable`**: `Thunk` wraps `Box<dyn FnOnce() -> A>`, which cannot be cloned
52	/// because `FnOnce` is consumed when called. The [`Traversable`](crate::classes::Traversable) trait
53	/// requires `Clone` bounds on the result type (to build the output structure), making it fundamentally
54	/// incompatible with `Thunk`'s design. This is an intentional trade-off: `Thunk` prioritizes
55	/// zero-overhead deferred execution and lifetime flexibility over structural cloning.
56	///
57	/// Implemented typeclasses:
58	/// - ✅ [`Functor`], [`Foldable`], [`Semimonad`]/Monad, [`Semiapplicative`]/Applicative
59	/// - ❌ [`Traversable`](crate::classes::Traversable) (requires `Clone`)
60	///
61	/// ### Type Parameters
62	///
63	#[document_type_parameters(
64		"The lifetime of the computation.",
65		"The type of the value produced by the computation."
66	)]
67	///
68	/// ### Fields
69	///
70	#[document_fields("The closure that performs the computation.")]
71	///
72	/// ### Examples
73	///
74	/// ```
75	/// use fp_library::types::*;
76	///
77	/// let computation = Thunk::new(|| 5)
78	///     .map(|x| x * 2)
79	///     .map(|x| x + 1);
80	///
81	/// // No computation has happened yet!
82	/// // Only when we call evaluate() does it execute:
83	/// let result = computation.evaluate();
84	/// assert_eq!(result, 11);
85	/// ```
86	pub struct Thunk<'a, A>(Box<dyn FnOnce() -> A + 'a>);
87
88	/// ### Type Parameters
89	///
90	#[document_type_parameters(
91		"The lifetime of the computation.",
92		"The type of the value produced by the computation."
93	)]
94	#[document_parameters("The thunk instance.")]
95	impl<'a, A: 'a> Thunk<'a, A> {
96		/// Creates a new `Thunk` from a thunk.
97		///
98		/// ### Type Signature
99		///
100		#[document_signature]
101		///
102		/// ### Type Parameters
103		///
104		#[document_type_parameters("The type of the closure.")]
105		///
106		/// ### Parameters
107		///
108		#[document_parameters("The thunk to wrap.")]
109		///
110		/// ### Returns
111		///
112		/// A new `Thunk` instance.
113		///
114		/// ### Examples
115		///
116		/// ```
117		/// use fp_library::types::*;
118		///
119		/// let thunk = Thunk::new(|| 42);
120		/// assert_eq!(thunk.evaluate(), 42);
121		/// ```
122		pub fn new<F>(f: F) -> Self
123		where
124			F: FnOnce() -> A + 'a,
125		{
126			Thunk(Box::new(f))
127		}
128
129		/// Returns a pure value (already computed).
130		///
131		/// ### Type Signature
132		///
133		#[document_signature]
134		///
135		/// ### Parameters
136		///
137		#[document_parameters("The value to wrap.")]
138		///
139		/// ### Returns
140		///
141		/// A new `Thunk` instance containing the value.
142		///
143		/// ### Examples
144		///
145		/// ```
146		/// use fp_library::{brands::*, functions::*, classes::*};
147		///
148		/// let thunk = pure::<ThunkBrand, _>(42);
149		/// assert_eq!(thunk.evaluate(), 42);
150		/// ```
151		pub fn pure(a: A) -> Self
152		where
153			A: 'a,
154		{
155			Thunk::new(move || a)
156		}
157
158		/// Defers a computation that returns a Thunk.
159		///
160		/// ### Type Signature
161		///
162		#[document_signature]
163		///
164		/// ### Type Parameters
165		///
166		#[document_type_parameters("The type of the closure.")]
167		///
168		/// ### Parameters
169		///
170		#[document_parameters("The thunk that returns a `Thunk`.")]
171		///
172		/// ### Returns
173		///
174		/// A new `Thunk` instance.
175		///
176		/// ### Examples
177		///
178		/// ```
179		/// use fp_library::{brands::*, functions::*, types::*};
180		///
181		/// let thunk = Thunk::defer(|| pure::<ThunkBrand, _>(42));
182		/// assert_eq!(thunk.evaluate(), 42);
183		/// ```
184		pub fn defer<F>(f: F) -> Self
185		where
186			F: FnOnce() -> Thunk<'a, A> + 'a,
187		{
188			Thunk::new(move || f().evaluate())
189		}
190
191		/// Monadic bind: chains computations.
192		///
193		/// Note: Each `bind` adds to the call stack. For deep recursion,
194		/// use [`Trampoline`](crate::types::Trampoline) instead.
195		///
196		/// ### Type Signature
197		///
198		#[document_signature]
199		///
200		/// ### Type Parameters
201		///
202		#[document_type_parameters(
203			"The type of the result of the new computation.",
204			"The type of the function to apply."
205		)]
206		///
207		/// ### Parameters
208		///
209		#[document_parameters("The function to apply to the result of the computation.")]
210		///
211		/// ### Returns
212		///
213		/// A new `Thunk` instance representing the chained computation.
214		///
215		/// ### Examples
216		///
217		/// ```
218		/// use fp_library::{brands::*, functions::*};
219		///
220		/// let thunk = pure::<ThunkBrand, _>(21).bind(|x| pure::<ThunkBrand, _>(x * 2));
221		/// assert_eq!(thunk.evaluate(), 42);
222		/// ```
223		pub fn bind<B: 'a, F>(
224			self,
225			f: F,
226		) -> Thunk<'a, B>
227		where
228			F: FnOnce(A) -> Thunk<'a, B> + 'a,
229		{
230			Thunk::new(move || {
231				let a = (self.0)();
232				let thunk_b = f(a);
233				(thunk_b.0)()
234			})
235		}
236
237		/// Functor map: transforms the result.
238		///
239		/// ### Type Signature
240		///
241		#[document_signature]
242		///
243		/// ### Type Parameters
244		///
245		#[document_type_parameters(
246			"The type of the result of the transformation.",
247			"The type of the transformation function."
248		)]
249		///
250		/// ### Parameters
251		///
252		#[document_parameters("The function to apply to the result of the computation.")]
253		///
254		/// ### Returns
255		///
256		/// A new `Thunk` instance with the transformed result.
257		///
258		/// ### Examples
259		///
260		/// ```
261		/// use fp_library::{brands::*, functions::*};
262		///
263		/// let thunk = pure::<ThunkBrand, _>(21).map(|x| x * 2);
264		/// assert_eq!(thunk.evaluate(), 42);
265		/// ```
266		pub fn map<B: 'a, F>(
267			self,
268			f: F,
269		) -> Thunk<'a, B>
270		where
271			F: FnOnce(A) -> B + 'a,
272		{
273			Thunk::new(move || f((self.0)()))
274		}
275
276		/// Forces evaluation and returns the result.
277		///
278		/// ### Type Signature
279		///
280		#[document_signature]
281		///
282		/// ### Returns
283		///
284		/// The result of the computation.
285		///
286		/// ### Examples
287		///
288		/// ```
289		/// use fp_library::{brands::*, functions::*};
290		///
291		/// let thunk = pure::<ThunkBrand, _>(42);
292		/// assert_eq!(thunk.evaluate(), 42);
293		/// ```
294		pub fn evaluate(self) -> A {
295			(self.0)()
296		}
297	}
298
299	/// ### Type Parameters
300	///
301	#[document_type_parameters(
302		"The lifetime of the computation.",
303		"The type of the value produced by the computation.",
304		"The memoization configuration."
305	)]
306	impl<'a, A, Config> From<Lazy<'a, A, Config>> for Thunk<'a, A>
307	where
308		A: Clone + 'a,
309		Config: LazyConfig,
310	{
311		/// ### Type Signature
312		///
313		#[document_signature]
314		///
315		/// ### Parameters
316		///
317		#[document_parameters("The lazy value to convert.")]
318		fn from(lazy: Lazy<'a, A, Config>) -> Self {
319			Thunk::new(move || lazy.evaluate().clone())
320		}
321	}
322
323	impl_kind! {
324		for ThunkBrand {
325			type Of<'a, A: 'a>: 'a = Thunk<'a, A>;
326		}
327	}
328
329	/// ### Type Parameters
330	///
331	#[document_type_parameters(
332		"The lifetime of the computation.",
333		"The type of the value produced by the computation."
334	)]
335	impl<'a, A: 'a> Deferrable<'a> for Thunk<'a, A> {
336		/// Creates a `Thunk` from a computation that produces it.
337		///
338		/// ### Type Signature
339		///
340		#[document_signature]
341		///
342		/// ### Type Parameters
343		///
344		#[document_type_parameters("The type of the closure.")]
345		///
346		/// ### Parameters
347		///
348		#[document_parameters("A thunk that produces the thunk.")]
349		///
350		/// ### Returns
351		///
352		/// The deferred thunk.
353		///
354		/// ### Examples
355		///
356		/// ```
357		/// use fp_library::{brands::*, functions::*, types::*, classes::Deferrable};
358		///
359		/// let task: Thunk<i32> = Deferrable::defer(|| Thunk::pure(42));
360		/// assert_eq!(task.evaluate(), 42);
361		/// ```
362		fn defer<F>(f: F) -> Self
363		where
364			F: FnOnce() -> Self + 'a,
365			Self: Sized,
366		{
367			Thunk::defer(f)
368		}
369	}
370
371	impl Functor for ThunkBrand {
372		/// Maps a function over the result of a `Thunk` computation.
373		///
374		/// ### Type Signature
375		///
376		#[document_signature]
377		///
378		/// ### Type Parameters
379		///
380		#[document_type_parameters(
381			"The lifetime of the computation.",
382			"The type of the value inside the `Thunk`.",
383			"The type of the result of the transformation.",
384			"The type of the transformation function."
385		)]
386		///
387		/// ### Parameters
388		///
389		#[document_parameters(
390			"The function to apply to the result of the computation.",
391			"The `Thunk` instance."
392		)]
393		///
394		/// ### Returns
395		///
396		/// A new `Thunk` instance with the transformed result.
397		///
398		/// ### Examples
399		///
400		/// ```
401		/// use fp_library::{brands::*, functions::*};
402		///
403		/// let thunk = pure::<ThunkBrand, _>(10);
404		/// let mapped = map::<ThunkBrand, _, _, _>(|x| x * 2, thunk);
405		/// assert_eq!(mapped.evaluate(), 20);
406		/// ```
407		fn map<'a, A: 'a, B: 'a, Func>(
408			func: Func,
409			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
410		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>)
411		where
412			Func: Fn(A) -> B + 'a,
413		{
414			fa.map(func)
415		}
416	}
417
418	impl Pointed for ThunkBrand {
419		/// Wraps a value in a `Thunk` context.
420		///
421		/// ### Type Signature
422		///
423		#[document_signature]
424		///
425		/// ### Type Parameters
426		///
427		#[document_type_parameters(
428			"The lifetime of the computation.",
429			"The type of the value to wrap."
430		)]
431		///
432		/// ### Parameters
433		///
434		#[document_parameters("The value to wrap.")]
435		///
436		/// ### Returns
437		///
438		/// A new `Thunk` instance containing the value.
439		///
440		/// ### Examples
441		///
442		/// ```
443		/// use fp_library::{brands::*, functions::*, types::*};
444		///
445		/// let thunk: Thunk<i32> = pure::<ThunkBrand, _>(42);
446		/// assert_eq!(thunk.evaluate(), 42);
447		/// ```
448		fn pure<'a, A: 'a>(a: A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>) {
449			Thunk::pure(a)
450		}
451	}
452
453	impl Lift for ThunkBrand {
454		/// Lifts a binary function into the `Thunk` context.
455		///
456		/// ### Type Signature
457		///
458		#[document_signature]
459		///
460		/// ### Type Parameters
461		///
462		#[document_type_parameters(
463			"The lifetime of the computation.",
464			"The type of the first value.",
465			"The type of the second value.",
466			"The type of the result.",
467			"The type of the binary function."
468		)]
469		///
470		/// ### Parameters
471		///
472		#[document_parameters(
473			"The binary function to apply.",
474			"The first `Thunk`.",
475			"The second `Thunk`."
476		)]
477		///
478		/// ### Returns
479		///
480		/// A new `Thunk` instance containing the result of applying the function.
481		///
482		/// ### Examples
483		///
484		/// ```
485		/// use fp_library::{brands::*, functions::*};
486		///
487		/// let eval1 = pure::<ThunkBrand, _>(10);
488		/// let eval2 = pure::<ThunkBrand, _>(20);
489		/// let result = lift2::<ThunkBrand, _, _, _, _>(|a, b| a + b, eval1, eval2);
490		/// assert_eq!(result.evaluate(), 30);
491		/// ```
492		fn lift2<'a, A, B, C, Func>(
493			func: Func,
494			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
495			fb: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
496		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>)
497		where
498			Func: Fn(A, B) -> C + 'a,
499			A: Clone + 'a,
500			B: Clone + 'a,
501			C: 'a,
502		{
503			fa.bind(move |a| fb.map(move |b| func(a, b)))
504		}
505	}
506
507	impl ApplyFirst for ThunkBrand {}
508	impl ApplySecond for ThunkBrand {}
509
510	impl Semiapplicative for ThunkBrand {
511		/// Applies a function wrapped in `Thunk` to a value wrapped in `Thunk`.
512		///
513		/// ### Type Signature
514		///
515		#[document_signature]
516		///
517		/// ### Type Parameters
518		///
519		#[document_type_parameters(
520			"The lifetime of the computation.",
521			"The brand of the cloneable function wrapper.",
522			"The type of the input.",
523			"The type of the result."
524		)]
525		///
526		/// ### Parameters
527		///
528		#[document_parameters(
529			"The `Thunk` containing the function.",
530			"The `Thunk` containing the value."
531		)]
532		///
533		/// ### Returns
534		///
535		/// A new `Thunk` instance containing the result of applying the function.
536		///
537		/// ### Examples
538		///
539		/// ```
540		/// use fp_library::{brands::*, functions::*};
541		///
542		/// let func = pure::<ThunkBrand, _>(cloneable_fn_new::<RcFnBrand, _, _>(|x: i32| x * 2));
543		/// let val = pure::<ThunkBrand, _>(21);
544		/// let result = apply::<RcFnBrand, ThunkBrand, _, _>(func, val);
545		/// assert_eq!(result.evaluate(), 42);
546		/// ```
547		fn apply<'a, FnBrand: 'a + CloneableFn, A: 'a + Clone, B: 'a>(
548			ff: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as CloneableFn>::Of<'a, A, B>>),
549			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
550		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
551			ff.bind(move |f| {
552				fa.map(
553					#[allow(clippy::redundant_closure)] // Required for move semantics
554					move |a| f(a),
555				)
556			})
557		}
558	}
559
560	impl Semimonad for ThunkBrand {
561		/// Chains `Thunk` computations.
562		///
563		/// ### Type Signature
564		///
565		#[document_signature]
566		///
567		/// ### Type Parameters
568		///
569		#[document_type_parameters(
570			"The lifetime of the computation.",
571			"The type of the result of the first computation.",
572			"The type of the result of the new computation.",
573			"The type of the function to apply."
574		)]
575		///
576		/// ### Parameters
577		///
578		#[document_parameters(
579			"The first `Thunk`.",
580			"The function to apply to the result of the computation."
581		)]
582		///
583		/// ### Returns
584		///
585		/// A new `Thunk` instance representing the chained computation.
586		///
587		/// ### Examples
588		///
589		/// ```
590		/// use fp_library::{brands::*, functions::*};
591		///
592		/// let thunk = pure::<ThunkBrand, _>(10);
593		/// let result = bind::<ThunkBrand, _, _, _>(thunk, |x| pure::<ThunkBrand, _>(x * 2));
594		/// assert_eq!(result.evaluate(), 20);
595		/// ```
596		fn bind<'a, A: 'a, B: 'a, Func>(
597			ma: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
598			func: Func,
599		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>)
600		where
601			Func: Fn(A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) + 'a,
602		{
603			ma.bind(func)
604		}
605	}
606
607	impl MonadRec for ThunkBrand {
608		/// Performs tail-recursive monadic computation.
609		///
610		/// ### Type Signature
611		///
612		#[document_signature]
613		///
614		/// ### Type Parameters
615		///
616		#[document_type_parameters(
617			"The lifetime of the computation.",
618			"The type of the initial value and loop state.",
619			"The type of the result.",
620			"The type of the step function."
621		)]
622		///
623		/// ### Parameters
624		///
625		#[document_parameters("The step function.", "The initial value.")]
626		///
627		/// ### Returns
628		///
629		/// The result of the computation.
630		///
631		/// ### Examples
632		///
633		/// ```
634		/// use fp_library::{brands::*, classes::*, functions::*, types::*};
635		///
636		/// let result = tail_rec_m::<ThunkBrand, _, _, _>(
637		///     |x| pure::<ThunkBrand, _>(if x < 1000 { Step::Loop(x + 1) } else { Step::Done(x) }),
638		///     0,
639		/// );
640		/// assert_eq!(result.evaluate(), 1000);
641		/// ```
642		fn tail_rec_m<'a, A: 'a, B: 'a, F>(
643			f: F,
644			a: A,
645		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>)
646		where
647			F: Fn(A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Step<A, B>>)
648				+ Clone
649				+ 'a,
650		{
651			Thunk::new(move || {
652				let mut current = a;
653				loop {
654					match f(current).evaluate() {
655						Step::Loop(next) => current = next,
656						Step::Done(res) => break res,
657					}
658				}
659			})
660		}
661	}
662
663	impl Evaluable for ThunkBrand {
664		/// Runs the eval, producing the inner value.
665		///
666		/// ### Type Signature
667		///
668		#[document_signature]
669		///
670		/// ### Type Parameters
671		///
672		#[document_type_parameters(
673			"The lifetime of the computation.",
674			"The type of the value inside the thunk."
675		)]
676		///
677		/// ### Parameters
678		///
679		#[document_parameters("The eval to run.")]
680		///
681		/// ### Returns
682		///
683		/// The result of running the thunk.
684		///
685		/// ### Examples
686		///
687		/// ```
688		/// use fp_library::{brands::*, classes::*, functions::*, types::*};
689		///
690		/// let thunk = Thunk::new(|| 42);
691		/// assert_eq!(evaluate::<ThunkBrand, _>(thunk), 42);
692		/// ```
693		fn evaluate<'a, A: 'a>(
694			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
695		) -> A {
696			fa.evaluate()
697		}
698	}
699
700	impl Foldable for ThunkBrand {
701		/// Folds the `Thunk` from the right.
702		///
703		/// ### Type Signature
704		///
705		#[document_signature]
706		///
707		/// ### Type Parameters
708		///
709		#[document_type_parameters(
710			"The lifetime of the computation.",
711			"The brand of the cloneable function to use.",
712			"The type of the elements in the structure.",
713			"The type of the accumulator.",
714			"The type of the folding function."
715		)]
716		///
717		/// ### Parameters
718		///
719		#[document_parameters(
720			"The function to apply to each element and the accumulator.",
721			"The initial value of the accumulator.",
722			"The `Thunk` to fold."
723		)]
724		///
725		/// ### Returns
726		///
727		/// The final accumulator value.
728		///
729		/// ### Examples
730		///
731		/// ```
732		/// use fp_library::{brands::*, functions::*};
733		///
734		/// let thunk = pure::<ThunkBrand, _>(10);
735		/// let result = fold_right::<RcFnBrand, ThunkBrand, _, _, _>(|a, b| a + b, 5, thunk);
736		/// assert_eq!(result, 15);
737		/// ```
738		fn fold_right<'a, FnBrand, A: 'a, B: 'a, Func>(
739			func: Func,
740			initial: B,
741			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
742		) -> B
743		where
744			Func: Fn(A, B) -> B + 'a,
745			FnBrand: CloneableFn + 'a,
746		{
747			func(fa.evaluate(), initial)
748		}
749
750		/// Folds the `Thunk` from the left.
751		///
752		/// ### Type Signature
753		///
754		#[document_signature]
755		///
756		/// ### Type Parameters
757		///
758		#[document_type_parameters(
759			"The lifetime of the computation.",
760			"The brand of the cloneable function to use.",
761			"The type of the elements in the structure.",
762			"The type of the accumulator.",
763			"The type of the folding function."
764		)]
765		///
766		/// ### Parameters
767		///
768		#[document_parameters(
769			"The function to apply to the accumulator and each element.",
770			"The initial value of the accumulator.",
771			"The `Thunk` to fold."
772		)]
773		///
774		/// ### Returns
775		///
776		/// The final accumulator value.
777		///
778		/// ### Examples
779		///
780		/// ```
781		/// use fp_library::{brands::*, functions::*};
782		///
783		/// let thunk = pure::<ThunkBrand, _>(10);
784		/// let result = fold_left::<RcFnBrand, ThunkBrand, _, _, _>(|b, a| b + a, 5, thunk);
785		/// assert_eq!(result, 15);
786		/// ```
787		fn fold_left<'a, FnBrand, A: 'a, B: 'a, Func>(
788			func: Func,
789			initial: B,
790			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
791		) -> B
792		where
793			Func: Fn(B, A) -> B + 'a,
794			FnBrand: CloneableFn + 'a,
795		{
796			func(initial, fa.evaluate())
797		}
798
799		/// Maps the value to a monoid and returns it.
800		///
801		/// ### Type Signature
802		///
803		#[document_signature]
804		///
805		/// ### Type Parameters
806		///
807		#[document_type_parameters(
808			"The lifetime of the computation.",
809			"The brand of the cloneable function to use.",
810			"The type of the elements in the structure.",
811			"The type of the monoid.",
812			"The type of the mapping function."
813		)]
814		///
815		/// ### Parameters
816		///
817		#[document_parameters("The mapping function.", "The Thunk to fold.")]
818		///
819		/// ### Returns
820		///
821		/// The monoid value.
822		///
823		/// ### Examples
824		///
825		/// ```
826		/// use fp_library::{brands::*, functions::*};
827		///
828		/// let thunk = pure::<ThunkBrand, _>(10);
829		/// let result = fold_map::<RcFnBrand, ThunkBrand, _, _, _>(|a| a.to_string(), thunk);
830		/// assert_eq!(result, "10");
831		/// ```
832		fn fold_map<'a, FnBrand, A: 'a, M, Func>(
833			func: Func,
834			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
835		) -> M
836		where
837			M: Monoid + 'a,
838			Func: Fn(A) -> M + 'a,
839			FnBrand: CloneableFn + 'a,
840		{
841			func(fa.evaluate())
842		}
843	}
844
845	/// ### Type Parameters
846	///
847	#[document_type_parameters(
848		"The lifetime of the computation.",
849		"The type of the value produced by the computation."
850	)]
851	impl<'a, A: Semigroup + 'a> Semigroup for Thunk<'a, A> {
852		/// Combines two `Thunk`s by combining their results.
853		///
854		/// ### Type Signature
855		///
856		#[document_signature]
857		///
858		/// ### Parameters
859		///
860		#[document_parameters("The first `Thunk`.", "The second `Thunk`.")]
861		///
862		/// ### Returns
863		///
864		/// A new `Thunk` containing the combined result.
865		///
866		/// ### Examples
867		///
868		/// ```
869		/// use fp_library::{brands::*, classes::*, functions::*};
870		///
871		/// let t1 = pure::<ThunkBrand, _>("Hello".to_string());
872		/// let t2 = pure::<ThunkBrand, _>(" World".to_string());
873		/// let t3 = append::<_>(t1, t2);
874		/// assert_eq!(t3.evaluate(), "Hello World");
875		/// ```
876		fn append(
877			a: Self,
878			b: Self,
879		) -> Self {
880			Thunk::new(move || Semigroup::append(a.evaluate(), b.evaluate()))
881		}
882	}
883
884	/// ### Type Parameters
885	///
886	#[document_type_parameters(
887		"The lifetime of the computation.",
888		"The type of the value produced by the computation."
889	)]
890	impl<'a, A: Monoid + 'a> Monoid for Thunk<'a, A> {
891		/// Returns the identity `Thunk`.
892		///
893		/// ### Type Signature
894		///
895		#[document_signature]
896		///
897		/// ### Returns
898		///
899		/// A `Thunk` producing the identity value of `A`.
900		///
901		/// ### Examples
902		///
903		/// ```
904		/// use fp_library::{classes::*, types::*};
905		///
906		/// let t: Thunk<String> = Thunk::empty();
907		/// assert_eq!(t.evaluate(), "");
908		/// ```
909		fn empty() -> Self {
910			Thunk::new(|| Monoid::empty())
911		}
912	}
913}
914pub use inner::*;
915
916#[cfg(test)]
917mod tests {
918	use super::*;
919
920	/// Tests basic execution of Thunk.
921	///
922	/// Verifies that `Thunk::new` creates a computation that can be run to produce the expected value.
923	#[test]
924	fn test_basic_execution() {
925		let thunk = Thunk::new(|| 42);
926		assert_eq!(thunk.evaluate(), 42);
927	}
928
929	/// Tests `Thunk::pure`.
930	///
931	/// Verifies that `Thunk::pure` creates a computation that returns the provided value.
932	#[test]
933	fn test_pure() {
934		let thunk = Thunk::pure(42);
935		assert_eq!(thunk.evaluate(), 42);
936	}
937
938	/// Tests borrowing in Thunk.
939	///
940	/// Verifies that `Thunk` can capture references to values on the stack.
941	#[test]
942	fn test_borrowing() {
943		let x = 42;
944		let thunk = Thunk::new(|| &x);
945		assert_eq!(thunk.evaluate(), &42);
946	}
947
948	/// Tests `Thunk::map`.
949	///
950	/// Verifies that `map` transforms the result of the computation.
951	#[test]
952	fn test_map() {
953		let thunk = Thunk::pure(21).map(|x| x * 2);
954		assert_eq!(thunk.evaluate(), 42);
955	}
956
957	/// Tests `Thunk::bind`.
958	///
959	/// Verifies that `bind` chains computations correctly.
960	#[test]
961	fn test_bind() {
962		let thunk = Thunk::pure(21).bind(|x| Thunk::pure(x * 2));
963		assert_eq!(thunk.evaluate(), 42);
964	}
965
966	/// Tests `Thunk::defer`.
967	///
968	/// Verifies that `defer` allows creating an `Thunk` from a thunk that returns an `Thunk`.
969	#[test]
970	fn test_defer() {
971		let thunk = Thunk::defer(|| Thunk::pure(42));
972		assert_eq!(thunk.evaluate(), 42);
973	}
974
975	/// Tests `From<Lazy>`.
976	#[test]
977	fn test_eval_from_memo() {
978		use crate::types::RcLazy;
979		let memo = RcLazy::new(|| 42);
980		let thunk = Thunk::from(memo);
981		assert_eq!(thunk.evaluate(), 42);
982	}
983
984	/// Tests the `Semigroup` implementation for `Thunk`.
985	///
986	/// Verifies that `append` correctly combines two evals.
987	#[test]
988	fn test_eval_semigroup() {
989		use crate::classes::semigroup::append;
990		use crate::{brands::*, functions::*};
991		let t1 = pure::<ThunkBrand, _>("Hello".to_string());
992		let t2 = pure::<ThunkBrand, _>(" World".to_string());
993		let t3 = append(t1, t2);
994		assert_eq!(t3.evaluate(), "Hello World");
995	}
996
997	/// Tests the `Monoid` implementation for `Thunk`.
998	///
999	/// Verifies that `empty` returns the identity element.
1000	#[test]
1001	fn test_eval_monoid() {
1002		use crate::classes::monoid::empty;
1003		let t: Thunk<String> = empty();
1004		assert_eq!(t.evaluate(), "");
1005	}
1006}