Skip to main content

fp_library/types/
trampoline.rs

1//! Stack-safe computation type with guaranteed safety for unlimited recursion depth.
2//!
3//! Trampolining converts stack-based recursion into heap-based iteration: instead
4//! of each recursive call consuming a stack frame, each step returns a thunk that
5//! the driver loop evaluates iteratively. This eliminates the risk of stack overflow
6//! regardless of recursion depth.
7//!
8//! Built on the [`Free`](crate::types::Free) monad with O(1) [`bind`](Trampoline::bind) operations. Provides complete stack safety at the cost of requiring `'static` types. Use this for deep recursion and heavy monadic pipelines.
9//!
10//! ### Examples
11//!
12//! ```
13//! use fp_library::types::*;
14//!
15//! let task = Trampoline::new(|| 1 + 1)
16//! 	.bind(|x| Trampoline::new(move || x * 2))
17//! 	.bind(|x| Trampoline::new(move || x + 10));
18//!
19//! assert_eq!(task.evaluate(), 14);
20//! ```
21
22#[fp_macros::document_module]
23mod inner {
24	use {
25		crate::{
26			brands::ThunkBrand,
27			classes::{
28				Deferrable,
29				LazyConfig,
30				Monoid,
31				Semigroup,
32			},
33			types::{
34				ArcLazyConfig,
35				Free,
36				Lazy,
37				RcLazyConfig,
38				Thunk,
39			},
40		},
41		core::ops::ControlFlow,
42		fp_macros::*,
43		std::fmt,
44	};
45
46	/// A lazy, stack-safe computation that produces a value of type `A`.
47	///
48	/// `Trampoline` is the "heavy-duty" monadic type for deferred computations that
49	/// require **guaranteed stack safety**. It is built on [`Free<Thunk, A>`] with
50	/// [`CatList`](crate::types::CatList)-based bind stack, ensuring O(1) [`bind`](Trampoline::bind)
51	/// operations and unlimited recursion depth without stack overflow.
52	///
53	/// # Requirements
54	///
55	/// - `A: 'static` - Required due to type erasure via [`Box<dyn Any>`].
56	///
57	/// # Guarantees
58	///
59	/// - **Stack safe**: Will not overflow regardless of recursion depth.
60	/// - **O(1) bind**: Left-associated `bind` chains don't degrade.
61	/// - **Lazy**: Computation is deferred until [`Trampoline::evaluate`] is called.
62	///
63	/// # When to Use `Trampoline` vs [`Thunk`]
64	///
65	/// - Use **`Trampoline<A>`** for deep recursion, heavy monadic pipelines.
66	/// - Use **`Thunk<'a, A>`** for HKT integration, borrowed references, glue code.
67	///
68	/// # Memoization
69	///
70	/// `Trampoline` does NOT memoize. Each call to `evaluate` re-evaluates.
71	/// For memoization, wrap in [`Lazy`]:
72	///
73	/// ```rust
74	/// use fp_library::types::*;
75	///
76	/// let lazy: Lazy<i32> = Lazy::<_, RcLazyConfig>::new(|| Trampoline::new(|| 1 + 1).evaluate());
77	/// lazy.evaluate(); // Computes
78	/// lazy.evaluate(); // Returns cached
79	/// ```
80	#[document_type_parameters("The type of the value produced by the task.")]
81	///
82	pub struct Trampoline<A: 'static>(
83		/// The internal `Free` monad representation.
84		Free<ThunkBrand, A>,
85	);
86
87	#[document_type_parameters("The type of the value produced by the task.")]
88	#[document_parameters("The `Trampoline` instance.")]
89	impl<A: 'static> Trampoline<A> {
90		/// Creates a `Trampoline` from an already-computed value.
91		///
92		/// ### Complexity
93		///
94		/// O(1) creation, O(1) evaluation
95		#[document_signature]
96		///
97		#[document_parameters("The value to wrap.")]
98		///
99		#[document_returns("A `Trampoline` that produces the value `a`.")]
100		///
101		#[inline]
102		#[document_examples]
103		///
104		/// ```
105		/// use fp_library::types::*;
106		///
107		/// let task = Trampoline::pure(42);
108		/// assert_eq!(task.evaluate(), 42);
109		/// ```
110		pub fn pure(a: A) -> Self {
111			Trampoline(Free::pure(a))
112		}
113
114		/// Creates a lazy `Trampoline` that computes `f` on first evaluation.
115		///
116		/// `Trampoline` does NOT memoize - each `evaluate()`
117		/// re-evaluates. Use [`Lazy`] for caching.
118		///
119		/// # Complexity
120		/// O(1) creation
121		#[document_signature]
122		///
123		#[document_parameters("The closure to execute.")]
124		///
125		#[document_returns("A `Trampoline` that executes `f` when run.")]
126		///
127		#[inline]
128		#[document_examples]
129		///
130		/// ```
131		/// use fp_library::types::*;
132		///
133		/// let task = Trampoline::new(|| {
134		/// 	// println!("Computing!");
135		/// 	1 + 1
136		/// });
137		///
138		/// // Nothing computed yet
139		/// let result = task.evaluate(); // Now the closure runs
140		/// assert_eq!(result, 2);
141		/// ```
142		pub fn new(f: impl FnOnce() -> A + 'static) -> Self {
143			Trampoline(Free::wrap(Thunk::new(move || Free::pure(f()))))
144		}
145
146		/// Defers the construction of a `Trampoline` itself.
147		///
148		/// This is critical for stack-safe recursion: instead of
149		/// building a chain of `Trampoline`s directly (which grows the stack),
150		/// we defer the construction.
151		#[document_signature]
152		///
153		#[document_parameters("The closure that produces a `Trampoline`.")]
154		///
155		#[document_returns("A `Trampoline` that defers the creation of the inner task.")]
156		///
157		#[inline]
158		#[document_examples]
159		///
160		/// ```
161		/// use fp_library::types::*;
162		///
163		/// fn recursive_sum(
164		/// 	n: u64,
165		/// 	acc: u64,
166		/// ) -> Trampoline<u64> {
167		/// 	if n == 0 {
168		/// 		Trampoline::pure(acc)
169		/// 	} else {
170		/// 		// Defer construction to avoid stack growth
171		/// 		Trampoline::defer(move || recursive_sum(n - 1, acc + n))
172		/// 	}
173		/// }
174		///
175		/// // This works for n = 1_000_000 without stack overflow!
176		/// let result = recursive_sum(1_000, 0).evaluate();
177		/// assert_eq!(result, 500500);
178		/// ```
179		pub fn defer(f: impl FnOnce() -> Trampoline<A> + 'static) -> Self {
180			Trampoline(Free::wrap(Thunk::new(move || f().0)))
181		}
182
183		/// Monadic bind with O(1) complexity.
184		///
185		/// Chains computations together. The key property is that
186		/// left-associated chains don't degrade to O(n²).
187		#[document_signature]
188		///
189		#[document_type_parameters("The type of the result of the new task.")]
190		///
191		#[document_parameters("The function to apply to the result of this task.")]
192		///
193		#[document_returns("A new `Trampoline` that chains `f` after this task.")]
194		///
195		#[inline]
196		#[document_examples]
197		///
198		/// ```
199		/// use fp_library::types::*;
200		///
201		/// // This is O(n), not O(n²)
202		/// let mut task = Trampoline::pure(0);
203		/// for i in 0 .. 100 {
204		/// 	task = task.bind(move |x| Trampoline::pure(x + i));
205		/// }
206		/// assert_eq!(task.evaluate(), 4950);
207		/// ```
208		pub fn bind<B: 'static>(
209			self,
210			f: impl FnOnce(A) -> Trampoline<B> + 'static,
211		) -> Trampoline<B> {
212			Trampoline(self.0.bind(move |a| f(a).0))
213		}
214
215		/// Functor map: transforms the result without changing structure.
216		#[document_signature]
217		///
218		#[document_type_parameters("The type of the result of the mapping function.")]
219		///
220		#[document_parameters("The function to apply to the result of this task.")]
221		///
222		#[document_returns("A new `Trampoline` with the transformed result.")]
223		///
224		#[inline]
225		#[document_examples]
226		///
227		/// ```
228		/// use fp_library::types::*;
229		///
230		/// let task = Trampoline::pure(10).map(|x| x * 2);
231		/// assert_eq!(task.evaluate(), 20);
232		/// ```
233		pub fn map<B: 'static>(
234			self,
235			f: impl FnOnce(A) -> B + 'static,
236		) -> Trampoline<B> {
237			Trampoline(self.0.map(f))
238		}
239
240		/// Forces evaluation and returns the result.
241		///
242		/// This runs the trampoline loop, iteratively processing
243		/// the CatList of continuations without growing the stack.
244		#[document_signature]
245		///
246		#[document_parameters]
247		///
248		#[document_returns("The result of the computation.")]
249		///
250		#[document_examples]
251		///
252		/// ```
253		/// use fp_library::types::*;
254		///
255		/// let task = Trampoline::new(|| 1 + 1);
256		/// assert_eq!(task.evaluate(), 2);
257		/// ```
258		pub fn evaluate(self) -> A {
259			self.0.evaluate()
260		}
261
262		/// Converts this `Trampoline` into a memoized [`Lazy`](crate::types::Lazy) value.
263		///
264		/// The computation will be evaluated at most once; subsequent accesses
265		/// return the cached result.
266		#[document_signature]
267		///
268		#[document_returns(
269			"A memoized `Lazy` value that evaluates this trampoline on first access."
270		)]
271		///
272		#[document_examples]
273		///
274		/// ```
275		/// use fp_library::types::*;
276		///
277		/// let task = Trampoline::new(|| 42);
278		/// let lazy = task.into_rc_lazy();
279		/// // evaluate() returns &i32, so deref to get i32 for comparison
280		/// assert_eq!(*lazy.evaluate(), 42);
281		/// ```
282		pub fn into_rc_lazy(self) -> Lazy<'static, A, RcLazyConfig> {
283			Lazy::from(self)
284		}
285
286		/// Evaluates this `Trampoline` and wraps the result in a thread-safe [`ArcLazy`](crate::types::Lazy).
287		///
288		/// The trampoline is evaluated eagerly because its inner closures are
289		/// `!Send` (they are stored as `Box<dyn FnOnce>` inside the underlying
290		/// `Free` monad), so they cannot be placed inside an `Arc`-based lazy
291		/// value that requires `Send`. By evaluating first, only the resulting
292		/// `A` (which is `Send + Sync`) needs to cross the thread-safety boundary.
293		#[document_signature]
294		///
295		#[document_returns("A thread-safe `ArcLazy` containing the eagerly evaluated result.")]
296		///
297		#[document_examples]
298		///
299		/// ```
300		/// use fp_library::types::*;
301		///
302		/// let task = Trampoline::new(|| 42);
303		/// let lazy = task.into_arc_lazy();
304		/// assert_eq!(*lazy.evaluate(), 42);
305		/// ```
306		pub fn into_arc_lazy(self) -> Lazy<'static, A, ArcLazyConfig>
307		where
308			A: Send + Sync, {
309			Lazy::from(self)
310		}
311
312		/// Combines two `Trampoline`s, running both and combining results.
313		#[document_signature]
314		///
315		#[document_type_parameters(
316			"The type of the second task's result.",
317			"The type of the combined result."
318		)]
319		///
320		#[document_parameters("The second task.", "The function to combine the results.")]
321		///
322		#[document_returns("A new `Trampoline` producing the combined result.")]
323		///
324		#[document_examples]
325		///
326		/// ```
327		/// use fp_library::types::*;
328		///
329		/// let t1 = Trampoline::pure(10);
330		/// let t2 = Trampoline::pure(20);
331		/// let t3 = t1.lift2(t2, |a, b| a + b);
332		/// assert_eq!(t3.evaluate(), 30);
333		/// ```
334		pub fn lift2<B: 'static, C: 'static>(
335			self,
336			other: Trampoline<B>,
337			f: impl FnOnce(A, B) -> C + 'static,
338		) -> Trampoline<C> {
339			self.bind(move |a| other.map(move |b| f(a, b)))
340		}
341
342		/// Sequences two `Trampoline`s, discarding the first result.
343		#[document_signature]
344		///
345		#[document_type_parameters("The type of the second task's result.")]
346		///
347		#[document_parameters("The second task.")]
348		///
349		#[document_returns(
350			"A new `Trampoline` that runs both tasks and returns the result of the second."
351		)]
352		///
353		#[document_examples]
354		///
355		/// ```
356		/// use fp_library::types::*;
357		///
358		/// let t1 = Trampoline::pure(10);
359		/// let t2 = Trampoline::pure(20);
360		/// let t3 = t1.then(t2);
361		/// assert_eq!(t3.evaluate(), 20);
362		/// ```
363		pub fn then<B: 'static>(
364			self,
365			other: Trampoline<B>,
366		) -> Trampoline<B> {
367			self.bind(move |_| other)
368		}
369
370		/// Combines two `Trampoline` values using the `Semigroup` operation on their results.
371		///
372		/// Evaluates both trampolines and combines the results via [`Semigroup::append`].
373		/// The combination itself is deferred and stack-safe.
374		#[document_signature]
375		///
376		#[document_parameters(
377			"The second `Trampoline` whose result will be combined with this one."
378		)]
379		///
380		#[document_returns("A new `Trampoline` producing the combined result.")]
381		///
382		#[document_examples]
383		///
384		/// ```
385		/// use fp_library::types::*;
386		///
387		/// let t1 = Trampoline::pure(vec![1, 2]);
388		/// let t2 = Trampoline::pure(vec![3, 4]);
389		/// assert_eq!(t1.append(t2).evaluate(), vec![1, 2, 3, 4]);
390		/// ```
391		#[inline]
392		pub fn append(
393			self,
394			other: Trampoline<A>,
395		) -> Trampoline<A>
396		where
397			A: Semigroup + 'static, {
398			self.lift2(other, Semigroup::append)
399		}
400
401		/// Creates a `Trampoline` that produces the identity element for the given `Monoid`.
402		#[document_signature]
403		///
404		#[document_returns("A `Trampoline` producing the monoid identity element.")]
405		///
406		#[document_examples]
407		///
408		/// ```
409		/// use fp_library::types::*;
410		///
411		/// let t: Trampoline<Vec<i32>> = Trampoline::empty();
412		/// assert_eq!(t.evaluate(), Vec::<i32>::new());
413		/// ```
414		#[inline]
415		pub fn empty() -> Trampoline<A>
416		where
417			A: Monoid + 'static, {
418			Trampoline::pure(Monoid::empty())
419		}
420
421		/// Stack-safe tail recursion within Trampoline.
422		///
423		/// # Clone Bound
424		///
425		/// The function `f` must implement `Clone` because each iteration
426		/// of the recursion may need its own copy. Most closures naturally
427		/// implement `Clone` when all their captures implement `Clone`.
428		///
429		/// For closures that don't implement `Clone`, use `arc_tail_rec_m`
430		/// which wraps the closure in `Arc` internally.
431		#[document_signature]
432		///
433		#[document_type_parameters("The type of the state.")]
434		///
435		#[document_parameters(
436			"The function that performs one step of the recursion.",
437			"The initial state."
438		)]
439		///
440		#[document_returns("A `Trampoline` that performs the recursion.")]
441		#[document_examples]
442		///
443		/// ```
444		/// use {
445		/// 	core::ops::ControlFlow,
446		/// 	fp_library::types::Trampoline,
447		/// };
448		///
449		/// // Fibonacci using tail recursion
450		/// fn fib(n: u64) -> Trampoline<u64> {
451		/// 	Trampoline::tail_rec_m(
452		/// 		|(n, a, b)| {
453		/// 			if n == 0 {
454		/// 				Trampoline::pure(ControlFlow::Break(a))
455		/// 			} else {
456		/// 				Trampoline::pure(ControlFlow::Continue((n - 1, b, a + b)))
457		/// 			}
458		/// 		},
459		/// 		(n, 0u64, 1u64),
460		/// 	)
461		/// }
462		///
463		/// assert_eq!(fib(50).evaluate(), 12586269025);
464		/// ```
465		pub fn tail_rec_m<S: 'static>(
466			f: impl Fn(S) -> Trampoline<ControlFlow<A, S>> + Clone + 'static,
467			initial: S,
468		) -> Self {
469			// Use defer to ensure each step is trampolined.
470			fn go<A: 'static, B: 'static, F>(
471				f: F,
472				a: A,
473			) -> Trampoline<B>
474			where
475				F: Fn(A) -> Trampoline<ControlFlow<B, A>> + Clone + 'static, {
476				Trampoline::defer(move || {
477					let result = f(a);
478					result.bind(move |step| match step {
479						ControlFlow::Continue(next) => go(f, next),
480						ControlFlow::Break(b) => Trampoline::pure(b),
481					})
482				})
483			}
484
485			go(f, initial)
486		}
487
488		/// Arc-wrapped version for non-Clone closures.
489		///
490		/// Use this when your closure captures non-Clone state.
491		#[document_signature]
492		///
493		#[document_type_parameters("The type of the state.")]
494		///
495		#[document_parameters(
496			"The function that performs one step of the recursion.",
497			"The initial state."
498		)]
499		///
500		#[document_returns("A `Trampoline` that performs the recursion.")]
501		#[document_examples]
502		///
503		/// ```
504		/// use {
505		/// 	core::ops::ControlFlow,
506		/// 	fp_library::types::Trampoline,
507		/// 	std::sync::{
508		/// 		Arc,
509		/// 		atomic::{
510		/// 			AtomicUsize,
511		/// 			Ordering,
512		/// 		},
513		/// 	},
514		/// };
515		///
516		/// // Closure captures non-Clone state
517		/// let counter = Arc::new(AtomicUsize::new(0));
518		/// let counter_clone = Arc::clone(&counter);
519		/// let task = Trampoline::arc_tail_rec_m(
520		/// 	move |n| {
521		/// 		counter_clone.fetch_add(1, Ordering::SeqCst);
522		/// 		if n == 0 {
523		/// 			Trampoline::pure(ControlFlow::Break(0))
524		/// 		} else {
525		/// 			Trampoline::pure(ControlFlow::Continue(n - 1))
526		/// 		}
527		/// 	},
528		/// 	100,
529		/// );
530		/// assert_eq!(task.evaluate(), 0);
531		/// assert_eq!(counter.load(Ordering::SeqCst), 101);
532		/// ```
533		pub fn arc_tail_rec_m<S: 'static>(
534			f: impl Fn(S) -> Trampoline<ControlFlow<A, S>> + 'static,
535			initial: S,
536		) -> Self {
537			use std::sync::Arc;
538			let f = Arc::new(f);
539			let wrapper = move |s: S| {
540				let f = Arc::clone(&f);
541				f(s)
542			};
543			Self::tail_rec_m(wrapper, initial)
544		}
545
546		/// Peels off one layer of the trampoline.
547		///
548		/// Returns `Ok(a)` if the computation has already completed with value `a`,
549		/// or `Err(thunk)` if the computation is suspended. Evaluating the returned
550		/// [`Thunk`] yields the next `Trampoline` step.
551		///
552		/// This is useful for implementing custom interpreters or drivers that need
553		/// to interleave trampoline steps with other logic (e.g., logging, resource
554		/// cleanup, cooperative scheduling).
555		#[document_signature]
556		///
557		#[document_returns(
558			"`Ok(a)` if the computation is finished, `Err(thunk)` if it is suspended."
559		)]
560		///
561		#[document_examples]
562		///
563		/// ```
564		/// use fp_library::types::*;
565		///
566		/// // A pure trampoline resumes immediately.
567		/// let t = Trampoline::pure(42);
568		/// assert_eq!(t.resume().unwrap(), 42);
569		///
570		/// // A deferred trampoline is suspended.
571		/// let t = Trampoline::defer(|| Trampoline::pure(99));
572		/// match t.resume() {
573		/// 	Ok(_) => panic!("expected suspension"),
574		/// 	Err(thunk) => {
575		/// 		let next = thunk.evaluate();
576		/// 		assert_eq!(next.resume().unwrap(), 99);
577		/// 	}
578		/// }
579		/// ```
580		pub fn resume(self) -> Result<A, Thunk<'static, Trampoline<A>>> {
581			match self.0.resume() {
582				Ok(a) => Ok(a),
583				Err(thunk_of_free) => Err(thunk_of_free.map(Trampoline)),
584			}
585		}
586	}
587
588	#[document_type_parameters(
589		"The type of the value produced by the task.",
590		"The memoization configuration."
591	)]
592	impl<A: 'static + Clone, Config: LazyConfig> From<Lazy<'static, A, Config>> for Trampoline<A> {
593		/// Converts a [`Lazy`] value into a [`Trampoline`] by cloning the memoized value.
594		///
595		/// This conversion clones the cached value on each evaluation.
596		/// The cost depends on the [`Clone`] implementation of `A`.
597		#[document_signature]
598		#[document_parameters("The lazy value to convert.")]
599		#[document_returns("A trampoline that evaluates the lazy value.")]
600		#[document_examples]
601		///
602		/// ```
603		/// use fp_library::types::*;
604		/// let lazy = Lazy::<_, RcLazyConfig>::pure(42);
605		/// let task = Trampoline::from(lazy);
606		/// assert_eq!(task.evaluate(), 42);
607		/// ```
608		fn from(lazy: Lazy<'static, A, Config>) -> Self {
609			Trampoline::new(move || lazy.evaluate().clone())
610		}
611	}
612
613	#[document_type_parameters("The type of the value produced by the task.")]
614	impl<A: 'static> Deferrable<'static> for Trampoline<A> {
615		/// Creates a `Trampoline` from a computation that produces it.
616		#[document_signature]
617		///
618		#[document_parameters("A thunk that produces the trampoline.")]
619		///
620		#[document_returns("The deferred trampoline.")]
621		///
622		#[document_examples]
623		///
624		/// ```
625		/// use fp_library::{
626		/// 	brands::*,
627		/// 	classes::Deferrable,
628		/// 	functions::*,
629		/// 	types::*,
630		/// };
631		///
632		/// let task: Trampoline<i32> = Deferrable::defer(|| Trampoline::pure(42));
633		/// assert_eq!(task.evaluate(), 42);
634		/// ```
635		fn defer(f: impl FnOnce() -> Self + 'static) -> Self
636		where
637			Self: Sized, {
638			Trampoline::defer(f)
639		}
640	}
641
642	#[document_type_parameters("The type of the value produced by the task.")]
643	impl<A: Semigroup + 'static> Semigroup for Trampoline<A> {
644		/// Combines two `Trampoline`s by combining their results via [`Semigroup::append`].
645		#[document_signature]
646		///
647		#[document_parameters("The first `Trampoline`.", "The second `Trampoline`.")]
648		///
649		#[document_returns("A new `Trampoline` producing the combined result.")]
650		///
651		#[document_examples]
652		///
653		/// ```
654		/// use fp_library::{
655		/// 	classes::*,
656		/// 	functions::*,
657		/// 	types::*,
658		/// };
659		///
660		/// let t1 = Trampoline::pure(vec![1, 2]);
661		/// let t2 = Trampoline::pure(vec![3, 4]);
662		/// let t3 = append::<_>(t1, t2);
663		/// assert_eq!(t3.evaluate(), vec![1, 2, 3, 4]);
664		/// ```
665		fn append(
666			a: Self,
667			b: Self,
668		) -> Self {
669			a.lift2(b, Semigroup::append)
670		}
671	}
672
673	#[document_type_parameters("The type of the value produced by the task.")]
674	impl<A: Monoid + 'static> Monoid for Trampoline<A> {
675		/// Returns a `Trampoline` producing the identity element for `A`.
676		#[document_signature]
677		///
678		#[document_returns("A `Trampoline` producing the monoid identity element.")]
679		///
680		#[document_examples]
681		///
682		/// ```
683		/// use fp_library::{
684		/// 	classes::*,
685		/// 	functions::*,
686		/// 	types::*,
687		/// };
688		///
689		/// let t: Trampoline<Vec<i32>> = empty::<Trampoline<Vec<i32>>>();
690		/// assert_eq!(t.evaluate(), Vec::<i32>::new());
691		/// ```
692		fn empty() -> Self {
693			Trampoline::pure(Monoid::empty())
694		}
695	}
696
697	#[document_type_parameters("The type of the value produced by the task.")]
698	#[document_parameters("The trampoline to format.")]
699	impl<A: 'static> fmt::Debug for Trampoline<A> {
700		/// Formats the trampoline without evaluating it.
701		#[document_signature]
702		#[document_parameters("The formatter.")]
703		#[document_returns("The formatting result.")]
704		#[document_examples]
705		///
706		/// ```
707		/// use fp_library::types::*;
708		/// let task = Trampoline::pure(42);
709		/// assert_eq!(format!("{:?}", task), "Trampoline(<unevaluated>)");
710		/// ```
711		fn fmt(
712			&self,
713			f: &mut fmt::Formatter<'_>,
714		) -> fmt::Result {
715			f.write_str("Trampoline(<unevaluated>)")
716		}
717	}
718}
719pub use inner::*;
720
721#[cfg(test)]
722mod tests {
723	use {
724		super::*,
725		core::ops::ControlFlow,
726		quickcheck_macros::quickcheck,
727	};
728
729	/// Tests `Trampoline::pure`.
730	///
731	/// Verifies that `pure` creates a task that returns the value immediately.
732	#[test]
733	fn test_task_pure() {
734		let task = Trampoline::pure(42);
735		assert_eq!(task.evaluate(), 42);
736	}
737
738	/// Tests `Trampoline::new`.
739	///
740	/// Verifies that `new` creates a task that computes the value when run.
741	#[test]
742	fn test_task_new() {
743		let task = Trampoline::new(|| 42);
744		assert_eq!(task.evaluate(), 42);
745	}
746
747	/// Tests `Trampoline::bind`.
748	///
749	/// Verifies that `bind` chains computations correctly.
750	#[test]
751	fn test_task_bind() {
752		let task = Trampoline::pure(10).bind(|x| Trampoline::pure(x * 2));
753		assert_eq!(task.evaluate(), 20);
754	}
755
756	/// Tests `Trampoline::map`.
757	///
758	/// Verifies that `map` transforms the result.
759	#[test]
760	fn test_task_map() {
761		let task = Trampoline::pure(10).map(|x| x * 2);
762		assert_eq!(task.evaluate(), 20);
763	}
764
765	/// Tests `Trampoline::defer`.
766	///
767	/// Verifies that `defer` delays the creation of the task.
768	#[test]
769	fn test_task_defer() {
770		let task = Trampoline::defer(|| Trampoline::pure(42));
771		assert_eq!(task.evaluate(), 42);
772	}
773
774	/// Tests `Trampoline::tail_rec_m`.
775	///
776	/// Verifies that `tail_rec_m` performs tail recursion correctly.
777	#[test]
778	fn test_task_tail_rec_m() {
779		fn factorial(n: u64) -> Trampoline<u64> {
780			Trampoline::tail_rec_m(
781				|(n, acc)| {
782					if n <= 1 {
783						Trampoline::pure(ControlFlow::Break(acc))
784					} else {
785						Trampoline::pure(ControlFlow::Continue((n - 1, n * acc)))
786					}
787				},
788				(n, 1u64),
789			)
790		}
791
792		assert_eq!(factorial(5).evaluate(), 120);
793	}
794
795	/// Tests `Trampoline::lift2`.
796	///
797	/// Verifies that `lift2` combines two tasks.
798	#[test]
799	fn test_task_lift2() {
800		let t1 = Trampoline::pure(10);
801		let t2 = Trampoline::pure(20);
802		let t3 = t1.lift2(t2, |a, b| a + b);
803		assert_eq!(t3.evaluate(), 30);
804	}
805
806	/// Tests `Trampoline::then`.
807	///
808	/// Verifies that `then` sequences two tasks.
809	#[test]
810	fn test_task_then() {
811		let t1 = Trampoline::pure(10);
812		let t2 = Trampoline::pure(20);
813		let t3 = t1.then(t2);
814		assert_eq!(t3.evaluate(), 20);
815	}
816
817	/// Tests `Trampoline::arc_tail_rec_m`.
818	///
819	/// Verifies that `arc_tail_rec_m` works with non-Clone closures.
820	#[test]
821	fn test_task_arc_tail_rec_m() {
822		use std::sync::{
823			Arc,
824			atomic::{
825				AtomicUsize,
826				Ordering,
827			},
828		};
829
830		let counter = Arc::new(AtomicUsize::new(0));
831		let counter_clone = Arc::clone(&counter);
832
833		let task = Trampoline::arc_tail_rec_m(
834			move |n| {
835				counter_clone.fetch_add(1, Ordering::SeqCst);
836				if n == 0 {
837					Trampoline::pure(ControlFlow::Break(0))
838				} else {
839					Trampoline::pure(ControlFlow::Continue(n - 1))
840				}
841			},
842			10,
843		);
844
845		assert_eq!(task.evaluate(), 0);
846		assert_eq!(counter.load(Ordering::SeqCst), 11);
847	}
848
849	/// Tests `Trampoline::from_memo`.
850	///
851	/// Verifies that `From<Lazy>` creates a task that retrieves the memoized value lazily.
852	#[test]
853	fn test_task_from_memo() {
854		use {
855			crate::types::{
856				Lazy,
857				RcLazyConfig,
858			},
859			std::{
860				cell::RefCell,
861				rc::Rc,
862			},
863		};
864
865		let counter = Rc::new(RefCell::new(0));
866		let counter_clone = counter.clone();
867		let memo = Lazy::<_, RcLazyConfig>::new(move || {
868			*counter_clone.borrow_mut() += 1;
869			42
870		});
871
872		let task = Trampoline::from(memo.clone());
873
874		// Should not have computed yet (lazy creation)
875		assert_eq!(*counter.borrow(), 0);
876
877		assert_eq!(task.evaluate(), 42);
878		assert_eq!(*counter.borrow(), 1);
879
880		// Run again, should use cached value
881		let task2 = Trampoline::from(memo);
882		assert_eq!(task2.evaluate(), 42);
883		assert_eq!(*counter.borrow(), 1);
884	}
885
886	/// Tests `Trampoline::from` with `ArcLazy`.
887	#[test]
888	fn test_task_from_arc_memo() {
889		use {
890			crate::types::{
891				ArcLazyConfig,
892				Lazy,
893			},
894			std::sync::{
895				Arc,
896				Mutex,
897			},
898		};
899
900		let counter = Arc::new(Mutex::new(0));
901		let counter_clone = counter.clone();
902		let memo = Lazy::<_, ArcLazyConfig>::new(move || {
903			*counter_clone.lock().unwrap() += 1;
904			42
905		});
906
907		let task = Trampoline::from(memo.clone());
908
909		// Should not have computed yet (lazy creation)
910		assert_eq!(*counter.lock().unwrap(), 0);
911
912		assert_eq!(task.evaluate(), 42);
913		assert_eq!(*counter.lock().unwrap(), 1);
914
915		// Run again, should use cached value
916		let task2 = Trampoline::from(memo);
917		assert_eq!(task2.evaluate(), 42);
918		assert_eq!(*counter.lock().unwrap(), 1);
919	}
920
921	/// Tests `From<Thunk>` for `Trampoline`.
922	///
923	/// Verifies that converting a `Thunk` to a `Trampoline` preserves the computed value.
924	#[test]
925	fn test_task_from_thunk() {
926		use crate::types::Thunk;
927
928		let thunk = Thunk::pure(42);
929		let task = Trampoline::from(thunk);
930		assert_eq!(task.evaluate(), 42);
931	}
932
933	/// Tests roundtrip `Thunk` -> `Trampoline` -> evaluate.
934	///
935	/// Verifies that a lazy thunk is correctly evaluated when converted to a trampoline.
936	#[test]
937	fn test_task_from_thunk_lazy() {
938		use crate::types::Thunk;
939
940		let thunk = Thunk::new(|| 21 * 2);
941		let task = Trampoline::from(thunk);
942		assert_eq!(task.evaluate(), 42);
943	}
944
945	// QuickCheck Law Tests
946
947	// Functor Laws (via inherent methods)
948
949	/// Functor identity: `pure(a).map(identity) == a`.
950	#[quickcheck]
951	fn functor_identity(x: i32) -> bool {
952		Trampoline::pure(x).map(|a| a).evaluate() == x
953	}
954
955	/// Functor composition: `fa.map(f . g) == fa.map(g).map(f)`.
956	#[quickcheck]
957	fn functor_composition(x: i32) -> bool {
958		let f = |a: i32| a.wrapping_add(1);
959		let g = |a: i32| a.wrapping_mul(2);
960		let lhs = Trampoline::pure(x).map(move |a| f(g(a))).evaluate();
961		let rhs = Trampoline::pure(x).map(g).map(f).evaluate();
962		lhs == rhs
963	}
964
965	// Monad Laws (via inherent methods)
966
967	/// Monad left identity: `pure(a).bind(f) == f(a)`.
968	#[quickcheck]
969	fn monad_left_identity(a: i32) -> bool {
970		let f = |x: i32| Trampoline::pure(x.wrapping_mul(2));
971		Trampoline::pure(a).bind(f).evaluate() == f(a).evaluate()
972	}
973
974	/// Monad right identity: `m.bind(pure) == m`.
975	#[quickcheck]
976	fn monad_right_identity(x: i32) -> bool {
977		Trampoline::pure(x).bind(Trampoline::pure).evaluate() == x
978	}
979
980	/// Monad associativity: `m.bind(f).bind(g) == m.bind(|a| f(a).bind(g))`.
981	#[quickcheck]
982	fn monad_associativity(x: i32) -> bool {
983		let f = |a: i32| Trampoline::pure(a.wrapping_add(1));
984		let g = |a: i32| Trampoline::pure(a.wrapping_mul(3));
985		let lhs = Trampoline::pure(x).bind(f).bind(g).evaluate();
986		let rhs = Trampoline::pure(x).bind(move |a| f(a).bind(g)).evaluate();
987		lhs == rhs
988	}
989
990	// Tests for !Send types (Rc)
991
992	/// Tests that `Trampoline` works with `Rc<T>`, a `!Send` type.
993	///
994	/// This verifies that the `Send` bound relaxation allows single-threaded
995	/// stack-safe recursion with reference-counted types.
996	#[test]
997	fn test_trampoline_with_rc() {
998		use std::rc::Rc;
999
1000		let rc_val = Rc::new(42);
1001		let task = Trampoline::pure(rc_val);
1002		let result = task.evaluate();
1003		assert_eq!(*result, 42);
1004	}
1005
1006	/// Tests `Trampoline::bind` with `Rc<T>`.
1007	///
1008	/// Verifies that `bind` works correctly when the value type is `!Send`.
1009	#[test]
1010	fn test_trampoline_bind_with_rc() {
1011		use std::rc::Rc;
1012
1013		let task = Trampoline::pure(Rc::new(10)).bind(|rc| {
1014			let val = *rc;
1015			Trampoline::pure(Rc::new(val * 2))
1016		});
1017		assert_eq!(*task.evaluate(), 20);
1018	}
1019
1020	/// Tests `Trampoline::map` with `Rc<T>`.
1021	///
1022	/// Verifies that `map` works correctly with `!Send` types.
1023	#[test]
1024	fn test_trampoline_map_with_rc() {
1025		use std::rc::Rc;
1026
1027		let task = Trampoline::pure(Rc::new(10)).map(|rc| Rc::new(*rc * 3));
1028		assert_eq!(*task.evaluate(), 30);
1029	}
1030
1031	/// Tests `Trampoline::defer` with `Rc<T>`.
1032	///
1033	/// Verifies that deferred construction works with `!Send` types.
1034	#[test]
1035	fn test_trampoline_defer_with_rc() {
1036		use std::rc::Rc;
1037
1038		let task = Trampoline::defer(|| Trampoline::pure(Rc::new(42)));
1039		assert_eq!(*task.evaluate(), 42);
1040	}
1041
1042	/// Tests `Trampoline::tail_rec_m` with `Rc<T>`.
1043	///
1044	/// Verifies that stack-safe recursion works with `!Send` types.
1045	#[test]
1046	fn test_trampoline_tail_rec_m_with_rc() {
1047		use std::rc::Rc;
1048
1049		let task = Trampoline::tail_rec_m(
1050			|(n, acc): (u64, Rc<u64>)| {
1051				if n == 0 {
1052					Trampoline::pure(ControlFlow::Break(acc))
1053				} else {
1054					Trampoline::pure(ControlFlow::Continue((n - 1, Rc::new(*acc + n))))
1055				}
1056			},
1057			(100u64, Rc::new(0u64)),
1058		);
1059		assert_eq!(*task.evaluate(), 5050);
1060	}
1061
1062	#[test]
1063	fn test_trampoline_append() {
1064		let t1 = Trampoline::pure(vec![1, 2]);
1065		let t2 = Trampoline::pure(vec![3, 4]);
1066		assert_eq!(t1.append(t2).evaluate(), vec![1, 2, 3, 4]);
1067	}
1068
1069	#[test]
1070	fn test_trampoline_append_strings() {
1071		let t1 = Trampoline::pure("hello".to_string());
1072		let t2 = Trampoline::pure(" world".to_string());
1073		assert_eq!(t1.append(t2).evaluate(), "hello world");
1074	}
1075
1076	#[test]
1077	fn test_trampoline_empty() {
1078		let t: Trampoline<Vec<i32>> = Trampoline::empty();
1079		assert_eq!(t.evaluate(), Vec::<i32>::new());
1080	}
1081
1082	#[test]
1083	fn test_trampoline_append_with_empty() {
1084		let t1 = Trampoline::pure(vec![1, 2, 3]);
1085		let t2: Trampoline<Vec<i32>> = Trampoline::empty();
1086		assert_eq!(t1.append(t2).evaluate(), vec![1, 2, 3]);
1087	}
1088
1089	// 7.7: Deeper stack safety stress test for tail_rec_m
1090
1091	/// Stress test for `Trampoline::tail_rec_m` with 100,000+ iterations.
1092	///
1093	/// Verifies that stack safety holds at depths far exceeding typical stack limits.
1094	#[test]
1095	fn test_tail_rec_m_deep_stack_safety() {
1096		let n: u64 = 200_000;
1097		let result = Trampoline::tail_rec_m(
1098			move |acc: u64| {
1099				if acc >= n {
1100					Trampoline::pure(ControlFlow::Break(acc))
1101				} else {
1102					Trampoline::pure(ControlFlow::Continue(acc + 1))
1103				}
1104			},
1105			0u64,
1106		);
1107		assert_eq!(result.evaluate(), n);
1108	}
1109
1110	/// Stress test for `Trampoline::arc_tail_rec_m` with 100,000+ iterations.
1111	///
1112	/// Verifies that the `Arc`-based variant is also stack-safe at high depth.
1113	#[test]
1114	fn test_arc_tail_rec_m_deep_stack_safety() {
1115		let n: u64 = 200_000;
1116		let result = Trampoline::arc_tail_rec_m(
1117			move |acc: u64| {
1118				if acc >= n {
1119					Trampoline::pure(ControlFlow::Break(acc))
1120				} else {
1121					Trampoline::pure(ControlFlow::Continue(acc + 1))
1122				}
1123			},
1124			0u64,
1125		);
1126		assert_eq!(result.evaluate(), n);
1127	}
1128
1129	/// Tests `Trampoline::resume` on a pure value.
1130	///
1131	/// Verifies that resuming a pure trampoline returns `Ok(value)`.
1132	#[test]
1133	fn test_resume_pure() {
1134		let t = Trampoline::pure(42);
1135		assert_eq!(t.resume().unwrap(), 42);
1136	}
1137
1138	/// Tests `Trampoline::resume` on a deferred computation.
1139	///
1140	/// Verifies that resuming a deferred trampoline returns `Err(thunk)`,
1141	/// and evaluating the thunk yields another trampoline that can be resumed.
1142	#[test]
1143	fn test_resume_deferred() {
1144		let t = Trampoline::defer(|| Trampoline::pure(99));
1145		match t.resume() {
1146			Ok(_) => panic!("expected suspension"),
1147			Err(thunk) => {
1148				let next = thunk.evaluate();
1149				assert_eq!(next.resume().unwrap(), 99);
1150			}
1151		}
1152	}
1153
1154	/// Tests that resuming through a chain of deferred steps eventually reaches `Ok`.
1155	///
1156	/// Builds a chain of three deferred steps and manually drives it to completion.
1157	#[test]
1158	fn test_resume_chain_reaches_ok() {
1159		let t =
1160			Trampoline::defer(|| Trampoline::defer(|| Trampoline::defer(|| Trampoline::pure(7))));
1161
1162		let mut current = t;
1163		let mut steps = 0;
1164		loop {
1165			match current.resume() {
1166				Ok(value) => {
1167					assert_eq!(value, 7);
1168					break;
1169				}
1170				Err(thunk) => {
1171					current = thunk.evaluate();
1172					steps += 1;
1173				}
1174			}
1175		}
1176		assert!(steps > 0, "expected at least one suspension step");
1177	}
1178}