Skip to main content

fp_library/types/
thunk.rs

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