Skip to main content

fp_library/types/
lazy.rs

1//! Memoized lazy evaluation with shared cache semantics.
2//!
3//! Computes a value at most once on first access and caches the result. All clones share the same cache. Available in both single-threaded [`RcLazy`] and thread-safe [`ArcLazy`] variants.
4
5#[fp_macros::document_module]
6mod inner {
7	use {
8		crate::{
9			Apply,
10			brands::LazyBrand,
11			classes::{
12				Deferrable,
13				RefFunctor,
14				SendDeferrable,
15			},
16			impl_kind,
17			kinds::*,
18			types::{
19				Thunk,
20				Trampoline,
21			},
22		},
23		fp_macros::*,
24		std::{
25			cell::LazyCell,
26			rc::Rc,
27			sync::{
28				Arc,
29				LazyLock,
30			},
31		},
32	};
33
34	/// Configuration for memoization strategy.
35	///
36	/// This trait bundles together the choices for:
37	/// - Pointer type ([`Rc`] vs [`Arc`]).
38	/// - Lazy cell type ([`LazyCell`] vs [`LazyLock`]).
39	///
40	/// # Note on Standard Library Usage
41	///
42	/// This design leverages Rust 1.80's `LazyCell` and `LazyLock` types,
43	/// which encapsulate the initialization-once logic.
44	pub trait LazyConfig: 'static {
45		/// The lazy cell type for infallible memoization.
46		type Lazy<'a, A: 'a>: Clone;
47
48		/// The lazy cell type for fallible memoization.
49		type TryLazy<'a, A: 'a, E: 'a>: Clone;
50
51		/// The type of the initializer thunk.
52		type Thunk<'a, A: 'a>: ?Sized;
53
54		/// The type of the fallible initializer thunk.
55		type TryThunk<'a, A: 'a, E: 'a>: ?Sized;
56
57		/// Creates a new lazy cell from an initializer.
58		#[document_signature]
59		///
60		#[document_type_parameters("The lifetime of the computation.", "The type of the value.")]
61		///
62		#[document_parameters("The initializer thunk.")]
63		///
64		#[document_returns("A new lazy cell.")]
65		///
66		#[document_examples]
67		///
68		/// ```
69		/// use fp_library::types::*;
70		///
71		/// let lazy = RcLazyConfig::lazy_new(Box::new(|| 42));
72		/// assert_eq!(*RcLazyConfig::evaluate(&lazy), 42);
73		/// ```
74		fn lazy_new<'a, A: 'a>(f: Box<Self::Thunk<'a, A>>) -> Self::Lazy<'a, A>;
75
76		/// Creates a new fallible lazy cell from an initializer.
77		#[document_signature]
78		///
79		#[document_type_parameters(
80			"The lifetime of the computation.",
81			"The type of the value.",
82			"The type of the error."
83		)]
84		///
85		#[document_parameters("The initializer thunk.")]
86		///
87		#[document_returns("A new fallible lazy cell.")]
88		///
89		#[document_examples]
90		///
91		/// ```
92		/// use fp_library::types::*;
93		///
94		/// let lazy = RcLazyConfig::try_lazy_new(Box::new(|| Ok::<i32, ()>(42)));
95		/// assert_eq!(RcLazyConfig::try_evaluate(&lazy), Ok(&42));
96		/// ```
97		fn try_lazy_new<'a, A: 'a, E: 'a>(
98			f: Box<Self::TryThunk<'a, A, E>>
99		) -> Self::TryLazy<'a, A, E>;
100
101		/// Forces evaluation and returns a reference.
102		#[document_signature]
103		///
104		#[document_type_parameters(
105			"The lifetime of the computation.",
106			"The lifetime of the reference.",
107			"The type of the value."
108		)]
109		///
110		#[document_parameters("The lazy cell to evaluate.")]
111		///
112		#[document_returns("A reference to the value.")]
113		///
114		#[document_examples]
115		///
116		/// ```
117		/// use fp_library::types::*;
118		///
119		/// let lazy = RcLazyConfig::lazy_new(Box::new(|| 42));
120		/// assert_eq!(*RcLazyConfig::evaluate(&lazy), 42);
121		/// ```
122		fn evaluate<'a, 'b, A: 'a>(lazy: &'b Self::Lazy<'a, A>) -> &'b A;
123
124		/// Forces evaluation and returns a reference to the result.
125		#[document_signature]
126		///
127		#[document_type_parameters(
128			"The lifetime of the computation.",
129			"The lifetime of the reference.",
130			"The type of the value.",
131			"The type of the error."
132		)]
133		///
134		#[document_parameters("The fallible lazy cell to evaluate.")]
135		///
136		#[document_returns("A result containing a reference to the value or error.")]
137		///
138		#[document_examples]
139		///
140		/// ```
141		/// use fp_library::types::*;
142		///
143		/// let lazy = RcLazyConfig::try_lazy_new(Box::new(|| Ok::<i32, ()>(42)));
144		/// assert_eq!(RcLazyConfig::try_evaluate(&lazy), Ok(&42));
145		/// ```
146		fn try_evaluate<'a, 'b, A: 'a, E: 'a>(
147			lazy: &'b Self::TryLazy<'a, A, E>
148		) -> Result<&'b A, &'b E>;
149	}
150
151	/// Single-threaded memoization using [`Rc<LazyCell>`].
152	///
153	/// Not thread-safe. Use [`ArcLazyConfig`] for multi-threaded contexts.
154	pub struct RcLazyConfig;
155
156	impl LazyConfig for RcLazyConfig {
157		type Lazy<'a, A: 'a> = Rc<LazyCell<A, Box<dyn FnOnce() -> A + 'a>>>;
158		type Thunk<'a, A: 'a> = dyn FnOnce() -> A + 'a;
159		type TryLazy<'a, A: 'a, E: 'a> =
160			Rc<LazyCell<Result<A, E>, Box<dyn FnOnce() -> Result<A, E> + 'a>>>;
161		type TryThunk<'a, A: 'a, E: 'a> = dyn FnOnce() -> Result<A, E> + 'a;
162
163		/// Creates a new lazy cell from an initializer.
164		#[document_signature]
165		///
166		#[document_type_parameters("The lifetime of the computation.", "The type of the value.")]
167		///
168		#[document_parameters("The initializer thunk.")]
169		///
170		#[document_returns("A new lazy cell.")]
171		///
172		#[document_examples]
173		///
174		/// ```
175		/// use fp_library::types::*;
176		///
177		/// let lazy = RcLazyConfig::lazy_new(Box::new(|| 42));
178		/// assert_eq!(*RcLazyConfig::evaluate(&lazy), 42);
179		/// ```
180		fn lazy_new<'a, A: 'a>(f: Box<Self::Thunk<'a, A>>) -> Self::Lazy<'a, A> {
181			Rc::new(LazyCell::new(f))
182		}
183
184		/// Creates a new fallible lazy cell from an initializer.
185		#[document_signature]
186		///
187		#[document_type_parameters(
188			"The lifetime of the computation.",
189			"The type of the value.",
190			"The type of the error."
191		)]
192		///
193		#[document_parameters("The initializer thunk.")]
194		///
195		#[document_returns("A new fallible lazy cell.")]
196		///
197		#[document_examples]
198		///
199		/// ```
200		/// use fp_library::types::*;
201		///
202		/// let lazy = RcLazyConfig::try_lazy_new(Box::new(|| Ok::<i32, ()>(42)));
203		/// assert_eq!(RcLazyConfig::try_evaluate(&lazy), Ok(&42));
204		/// ```
205		fn try_lazy_new<'a, A: 'a, E: 'a>(
206			f: Box<Self::TryThunk<'a, A, E>>
207		) -> Self::TryLazy<'a, A, E> {
208			Rc::new(LazyCell::new(f))
209		}
210
211		/// Forces evaluation and returns a reference.
212		#[document_signature]
213		///
214		#[document_type_parameters(
215			"The lifetime of the computation.",
216			"The lifetime of the reference.",
217			"The type of the value."
218		)]
219		///
220		#[document_parameters("The lazy cell to evaluate.")]
221		///
222		#[document_returns("A reference to the value.")]
223		///
224		#[document_examples]
225		///
226		/// ```
227		/// use fp_library::types::*;
228		///
229		/// let lazy = RcLazyConfig::lazy_new(Box::new(|| 42));
230		/// assert_eq!(*RcLazyConfig::evaluate(&lazy), 42);
231		/// ```
232		fn evaluate<'a, 'b, A: 'a>(lazy: &'b Self::Lazy<'a, A>) -> &'b A {
233			LazyCell::force(lazy)
234		}
235
236		/// Forces evaluation and returns a reference to the result.
237		#[document_signature]
238		///
239		#[document_type_parameters(
240			"The lifetime of the computation.",
241			"The lifetime of the reference.",
242			"The type of the value.",
243			"The type of the error."
244		)]
245		///
246		#[document_parameters("The fallible lazy cell to evaluate.")]
247		///
248		#[document_returns("A result containing a reference to the value or error.")]
249		///
250		#[document_examples]
251		///
252		/// ```
253		/// use fp_library::types::*;
254		///
255		/// let lazy = RcLazyConfig::try_lazy_new(Box::new(|| Ok::<i32, ()>(42)));
256		/// assert_eq!(RcLazyConfig::try_evaluate(&lazy), Ok(&42));
257		/// ```
258		fn try_evaluate<'a, 'b, A: 'a, E: 'a>(
259			lazy: &'b Self::TryLazy<'a, A, E>
260		) -> Result<&'b A, &'b E> {
261			LazyCell::force(lazy).as_ref()
262		}
263	}
264
265	/// Thread-safe memoization using [`Arc<LazyLock>`].
266	///
267	/// Requires `A: Send + Sync` for the value type.
268	pub struct ArcLazyConfig;
269
270	impl LazyConfig for ArcLazyConfig {
271		type Lazy<'a, A: 'a> = Arc<LazyLock<A, Box<dyn FnOnce() -> A + Send + 'a>>>;
272		type Thunk<'a, A: 'a> = dyn FnOnce() -> A + Send + 'a;
273		type TryLazy<'a, A: 'a, E: 'a> =
274			Arc<LazyLock<Result<A, E>, Box<dyn FnOnce() -> Result<A, E> + Send + 'a>>>;
275		type TryThunk<'a, A: 'a, E: 'a> = dyn FnOnce() -> Result<A, E> + Send + 'a;
276
277		/// Creates a new lazy cell from an initializer.
278		#[document_signature]
279		///
280		#[document_type_parameters("The lifetime of the computation.", "The type of the value.")]
281		///
282		#[document_parameters("The initializer thunk.")]
283		///
284		#[document_returns("A new lazy cell.")]
285		///
286		#[document_examples]
287		///
288		/// ```
289		/// use fp_library::types::*;
290		///
291		/// let lazy = ArcLazyConfig::lazy_new(Box::new(|| 42));
292		/// assert_eq!(*ArcLazyConfig::evaluate(&lazy), 42);
293		/// ```
294		fn lazy_new<'a, A: 'a>(f: Box<Self::Thunk<'a, A>>) -> Self::Lazy<'a, A> {
295			Arc::new(LazyLock::new(f))
296		}
297
298		/// Creates a new fallible lazy cell from an initializer.
299		#[document_signature]
300		///
301		#[document_type_parameters(
302			"The lifetime of the computation.",
303			"The type of the value.",
304			"The type of the error."
305		)]
306		///
307		#[document_parameters("The initializer thunk.")]
308		///
309		#[document_returns("A new fallible lazy cell.")]
310		///
311		#[document_examples]
312		///
313		/// ```
314		/// use fp_library::types::*;
315		///
316		/// let lazy = ArcLazyConfig::try_lazy_new(Box::new(|| Ok::<i32, ()>(42)));
317		/// assert_eq!(ArcLazyConfig::try_evaluate(&lazy), Ok(&42));
318		/// ```
319		fn try_lazy_new<'a, A: 'a, E: 'a>(
320			f: Box<Self::TryThunk<'a, A, E>>
321		) -> Self::TryLazy<'a, A, E> {
322			Arc::new(LazyLock::new(f))
323		}
324
325		/// Forces evaluation and returns a reference.
326		#[document_signature]
327		///
328		#[document_type_parameters(
329			"The lifetime of the computation.",
330			"The lifetime of the reference.",
331			"The type of the value."
332		)]
333		///
334		#[document_parameters("The lazy cell to evaluate.")]
335		///
336		#[document_returns("A reference to the value.")]
337		///
338		#[document_examples]
339		///
340		/// ```
341		/// use fp_library::types::*;
342		///
343		/// let lazy = ArcLazyConfig::lazy_new(Box::new(|| 42));
344		/// assert_eq!(*ArcLazyConfig::evaluate(&lazy), 42);
345		/// ```
346		fn evaluate<'a, 'b, A: 'a>(lazy: &'b Self::Lazy<'a, A>) -> &'b A {
347			LazyLock::force(lazy)
348		}
349
350		/// Forces evaluation and returns a reference to the result.
351		#[document_signature]
352		///
353		#[document_type_parameters(
354			"The lifetime of the computation.",
355			"The lifetime of the reference.",
356			"The type of the value.",
357			"The type of the error."
358		)]
359		///
360		#[document_parameters("The fallible lazy cell to evaluate.")]
361		///
362		#[document_returns("A result containing a reference to the value or error.")]
363		///
364		#[document_examples]
365		///
366		/// ```
367		/// use fp_library::types::*;
368		///
369		/// let lazy = ArcLazyConfig::try_lazy_new(Box::new(|| Ok::<i32, ()>(42)));
370		/// assert_eq!(ArcLazyConfig::try_evaluate(&lazy), Ok(&42));
371		/// ```
372		fn try_evaluate<'a, 'b, A: 'a, E: 'a>(
373			lazy: &'b Self::TryLazy<'a, A, E>
374		) -> Result<&'b A, &'b E> {
375			LazyLock::force(lazy).as_ref()
376		}
377	}
378
379	/// A lazily-computed, memoized value with shared semantics.
380	///
381	/// The computation runs at most once; subsequent accesses return the cached value.
382	/// Cloning a `Lazy` shares the underlying cache - all clones see the same value.
383	///
384	/// ### Higher-Kinded Type Representation
385	///
386	/// The higher-kinded representation of this type constructor is [`LazyBrand<Config>`](crate::brands::LazyBrand),
387	/// which is parameterized by the memoization configuration and is polymorphic over the computed value type.
388	#[document_type_parameters(
389		"The lifetime of the reference.",
390		"The type of the computed value.",
391		"The memoization configuration (determines Rc vs Arc)."
392	)]
393	///
394	#[document_fields("The internal lazy cell.")]
395	///
396	pub struct Lazy<'a, A, Config: LazyConfig = RcLazyConfig>(pub(crate) Config::Lazy<'a, A>)
397	where
398		A: 'a;
399
400	#[document_type_parameters(
401		"The lifetime of the reference.",
402		"The type of the computed value.",
403		"The memoization configuration (determines Rc vs Arc)."
404	)]
405	#[document_parameters("The instance to clone.")]
406	impl<'a, A, Config: LazyConfig> Clone for Lazy<'a, A, Config>
407	where
408		A: 'a,
409	{
410		#[document_signature]
411		#[document_returns("A new `Lazy` instance that shares the same underlying memoized value.")]
412		#[document_examples]
413		///
414		/// ```
415		/// use fp_library::types::*;
416		///
417		/// let memo = Lazy::<_, RcLazyConfig>::new(|| 5);
418		/// let shared = memo.clone();
419		///
420		/// // First force computes and caches:
421		/// let value = memo.evaluate();
422		///
423		/// // Second force returns cached value (shared sees same result):
424		/// assert_eq!(shared.evaluate(), value);
425		/// ```
426		fn clone(&self) -> Self {
427			Self(self.0.clone())
428		}
429	}
430
431	#[document_type_parameters(
432		"The lifetime of the reference.",
433		"The type of the computed value.",
434		"The memoization configuration (determines Rc vs Arc)."
435	)]
436	#[document_parameters("The lazy instance.")]
437	impl<'a, A, Config: LazyConfig> Lazy<'a, A, Config>
438	where
439		A: 'a,
440	{
441		/// Gets the memoized value, computing on first access.
442		#[document_signature]
443		///
444		#[document_returns("A reference to the memoized value.")]
445		///
446		#[document_examples]
447		///
448		/// ```
449		/// use fp_library::types::*;
450		///
451		/// let memo = Lazy::<_, RcLazyConfig>::new(|| 42);
452		/// assert_eq!(*memo.evaluate(), 42);
453		/// ```
454		pub fn evaluate(&self) -> &A {
455			Config::evaluate(&self.0)
456		}
457	}
458
459	#[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
460	impl<'a, A> Lazy<'a, A, RcLazyConfig>
461	where
462		A: 'a,
463	{
464		/// Creates a new Lazy that will run `f` on first access.
465		#[document_signature]
466		///
467		#[document_parameters("The closure that produces the value.")]
468		///
469		#[document_returns("A new `Lazy` instance.")]
470		///
471		#[document_examples]
472		///
473		/// ```
474		/// use fp_library::types::*;
475		///
476		/// let memo = Lazy::<_, RcLazyConfig>::new(|| 42);
477		/// assert_eq!(*memo.evaluate(), 42);
478		/// ```
479		pub fn new(f: impl FnOnce() -> A + 'a) -> Self {
480			Lazy(RcLazyConfig::lazy_new(Box::new(f)))
481		}
482
483		/// Creates a `Lazy` from an already-computed value.
484		///
485		/// The value is immediately available without any computation.
486		#[document_signature]
487		///
488		#[document_parameters("The pre-computed value to wrap.")]
489		///
490		#[document_returns("A new `Lazy` instance containing the value.")]
491		///
492		#[document_examples]
493		///
494		/// ```
495		/// use fp_library::types::*;
496		///
497		/// let lazy = Lazy::<_, RcLazyConfig>::pure(42);
498		/// assert_eq!(*lazy.evaluate(), 42);
499		/// ```
500		pub fn pure(a: A) -> Self {
501			Lazy(RcLazyConfig::lazy_new(Box::new(move || a)))
502		}
503	}
504
505	#[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
506	impl<'a, A> From<Thunk<'a, A>> for Lazy<'a, A, RcLazyConfig> {
507		#[document_signature]
508		#[document_parameters("The thunk to convert.")]
509		#[document_returns("A new `Lazy` instance that will evaluate the thunk on first access.")]
510		#[document_examples]
511		///
512		/// ```
513		/// use fp_library::types::*;
514		/// let thunk = Thunk::new(|| 42);
515		/// let lazy = Lazy::from(thunk);
516		/// assert_eq!(*lazy.evaluate(), 42);
517		/// ```
518		fn from(eval: Thunk<'a, A>) -> Self {
519			Self::new(move || eval.evaluate())
520		}
521	}
522
523	#[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
524	impl<'a, A> From<Trampoline<A>> for Lazy<'a, A, RcLazyConfig>
525	where
526		A: Send,
527	{
528		#[document_signature]
529		#[document_parameters("The trampoline to convert.")]
530		#[document_returns(
531			"A new `Lazy` instance that will evaluate the trampoline on first access."
532		)]
533		#[document_examples]
534		///
535		/// ```
536		/// use fp_library::types::*;
537		/// let task = Trampoline::pure(42);
538		/// let lazy = Lazy::from(task);
539		/// assert_eq!(*lazy.evaluate(), 42);
540		/// ```
541		fn from(task: Trampoline<A>) -> Self {
542			Self::new(move || task.evaluate())
543		}
544	}
545
546	#[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
547	impl<'a, A> Lazy<'a, A, ArcLazyConfig>
548	where
549		A: 'a,
550	{
551		/// Creates a new Lazy that will run `f` on first access.
552		#[document_signature]
553		///
554		#[document_parameters("The closure that produces the value.")]
555		///
556		#[document_returns("A new `Lazy` instance.")]
557		///
558		#[document_examples]
559		///
560		/// ```
561		/// use fp_library::types::*;
562		///
563		/// let lazy = Lazy::<_, ArcLazyConfig>::new(|| 42);
564		/// assert_eq!(*lazy.evaluate(), 42);
565		/// ```
566		pub fn new(f: impl FnOnce() -> A + Send + 'a) -> Self {
567			Lazy(ArcLazyConfig::lazy_new(Box::new(f)))
568		}
569
570		/// Creates a `Lazy` from an already-computed value.
571		///
572		/// The value is immediately available without any computation.
573		/// Requires `Send` since `ArcLazy` is thread-safe.
574		#[document_signature]
575		///
576		#[document_parameters("The pre-computed value to wrap.")]
577		///
578		#[document_returns("A new `Lazy` instance containing the value.")]
579		///
580		#[document_examples]
581		///
582		/// ```
583		/// use fp_library::types::*;
584		///
585		/// let lazy = Lazy::<_, ArcLazyConfig>::pure(42);
586		/// assert_eq!(*lazy.evaluate(), 42);
587		/// ```
588		pub fn pure(a: A) -> Self
589		where
590			A: Send, {
591			Lazy(ArcLazyConfig::lazy_new(Box::new(move || a)))
592		}
593	}
594
595	/// Single-threaded memoization alias.
596	pub type RcLazy<'a, A> = Lazy<'a, A, RcLazyConfig>;
597
598	/// Thread-safe memoization alias.
599	pub type ArcLazy<'a, A> = Lazy<'a, A, ArcLazyConfig>;
600
601	impl_kind! {
602		impl<Config: LazyConfig> for LazyBrand<Config> {
603			type Of<'a, A: 'a>: 'a = Lazy<'a, A, Config>;
604		}
605	}
606
607	#[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
608	impl<'a, A> Deferrable<'a> for Lazy<'a, A, RcLazyConfig>
609	where
610		A: Clone + 'a,
611	{
612		/// Defers a computation that produces a `Lazy` value.
613		///
614		/// This flattens the nested structure: instead of `Lazy<Lazy<A>>`, we get `Lazy<A>`.
615		/// The inner `Lazy` is computed only when the outer `Lazy` is evaluated.
616		#[document_signature]
617		///
618		#[document_parameters("The thunk that produces the lazy value.")]
619		///
620		#[document_returns("A new `Lazy` value.")]
621		///
622		#[document_examples]
623		///
624		/// ```
625		/// use fp_library::{
626		/// 	brands::*,
627		/// 	classes::*,
628		/// 	functions::*,
629		/// 	types::*,
630		/// };
631		///
632		/// let lazy = Lazy::<_, RcLazyConfig>::defer(|| RcLazy::pure(42));
633		/// assert_eq!(*lazy.evaluate(), 42);
634		/// ```
635		fn defer(f: impl FnOnce() -> Self + 'a) -> Self
636		where
637			Self: Sized, {
638			RcLazy::new(move || f().evaluate().clone())
639		}
640	}
641
642	#[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
643	impl<'a, A> SendDeferrable<'a> for Lazy<'a, A, ArcLazyConfig>
644	where
645		A: Clone + Send + Sync + 'a,
646	{
647		/// Defers a computation that produces a thread-safe `Lazy` value.
648		///
649		/// This flattens the nested structure: instead of `ArcLazy<ArcLazy<A>>`, we get `ArcLazy<A>`.
650		/// The inner `Lazy` is computed only when the outer `Lazy` is evaluated.
651		#[document_signature]
652		///
653		#[document_parameters("The thunk that produces the lazy value.")]
654		///
655		#[document_returns("A new `ArcLazy` value.")]
656		///
657		#[document_examples]
658		///
659		/// ```
660		/// use fp_library::{
661		/// 	brands::*,
662		/// 	classes::*,
663		/// 	types::*,
664		/// };
665		///
666		/// let lazy = ArcLazy::send_defer(|| ArcLazy::pure(42));
667		/// assert_eq!(*lazy.evaluate(), 42);
668		/// ```
669		fn send_defer(f: impl FnOnce() -> Self + Send + Sync + 'a) -> Self
670		where
671			Self: Sized, {
672			ArcLazy::new(move || f().evaluate().clone())
673		}
674	}
675
676	impl RefFunctor for LazyBrand<RcLazyConfig> {
677		/// Maps a function over the memoized value, where the function takes a reference.
678		#[document_signature]
679		///
680		#[document_type_parameters(
681			"The lifetime of the values.",
682			"The type of the value.",
683			"The type of the result."
684		)]
685		///
686		#[document_parameters("The function to apply.", "The memoized value.")]
687		///
688		#[document_returns("A new memoized value.")]
689		///
690		#[document_examples]
691		///
692		/// ```
693		/// use fp_library::{
694		/// 	brands::*,
695		/// 	classes::*,
696		/// 	types::*,
697		/// };
698		///
699		/// let memo = Lazy::<_, RcLazyConfig>::new(|| 10);
700		/// let mapped = LazyBrand::<RcLazyConfig>::ref_map(|x: &i32| *x * 2, memo);
701		/// assert_eq!(*mapped.evaluate(), 20);
702		/// ```
703		fn ref_map<'a, A: 'a, B: 'a>(
704			f: impl FnOnce(&A) -> B + 'a,
705			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
706		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
707			let fa = fa.clone();
708			let init: Box<dyn FnOnce() -> B + 'a> = Box::new(move || f(fa.evaluate()));
709			Lazy(RcLazyConfig::lazy_new(init))
710		}
711	}
712}
713
714pub use inner::*;
715
716#[cfg(test)]
717mod tests {
718	use {
719		super::inner::*,
720		crate::types::{
721			Thunk,
722			Trampoline,
723		},
724		std::{
725			cell::RefCell,
726			rc::Rc,
727			sync::Arc,
728		},
729	};
730
731	/// Tests that `Lazy` caches the result of the computation.
732	///
733	/// Verifies that the initializer is called only once.
734	#[test]
735	fn test_memo_caching() {
736		let counter = Rc::new(RefCell::new(0));
737		let counter_clone = counter.clone();
738		let memo = RcLazy::new(move || {
739			*counter_clone.borrow_mut() += 1;
740			42
741		});
742
743		assert_eq!(*counter.borrow(), 0);
744		assert_eq!(*memo.evaluate(), 42);
745		assert_eq!(*counter.borrow(), 1);
746		assert_eq!(*memo.evaluate(), 42);
747		assert_eq!(*counter.borrow(), 1);
748	}
749
750	/// Tests that `Lazy` shares the cache across clones.
751	///
752	/// Verifies that clones see the same value and don't recompute.
753	#[test]
754	fn test_memo_sharing() {
755		let counter = Rc::new(RefCell::new(0));
756		let counter_clone = counter.clone();
757		let memo = RcLazy::new(move || {
758			*counter_clone.borrow_mut() += 1;
759			42
760		});
761		let shared = memo.clone();
762
763		assert_eq!(*memo.evaluate(), 42);
764		assert_eq!(*counter.borrow(), 1);
765		assert_eq!(*shared.evaluate(), 42);
766		assert_eq!(*counter.borrow(), 1);
767	}
768
769	/// Tests thread safety of `ArcLazy`.
770	///
771	/// Verifies that `ArcLazy` can be shared across threads and computes only once.
772	#[test]
773	fn test_arc_memo_thread_safety() {
774		use std::{
775			sync::atomic::{
776				AtomicUsize,
777				Ordering,
778			},
779			thread,
780		};
781
782		let counter = Arc::new(AtomicUsize::new(0));
783		let counter_clone = counter.clone();
784		let memo = ArcLazy::new(move || {
785			counter_clone.fetch_add(1, Ordering::SeqCst);
786			42
787		});
788
789		let mut handles = vec![];
790		for _ in 0 .. 10 {
791			let memo_clone = memo.clone();
792			handles.push(thread::spawn(move || {
793				assert_eq!(*memo_clone.evaluate(), 42);
794			}));
795		}
796
797		for handle in handles {
798			handle.join().unwrap();
799		}
800
801		assert_eq!(counter.load(Ordering::SeqCst), 1);
802	}
803
804	/// Tests creation from `Thunk`.
805	///
806	/// Verifies `From<Thunk>` works correctly.
807	#[test]
808	fn test_memo_from_eval() {
809		let eval = Thunk::new(|| 42);
810		let memo = RcLazy::from(eval);
811		assert_eq!(*memo.evaluate(), 42);
812	}
813
814	/// Tests creation from `Trampoline`.
815	///
816	/// Verifies `From<Trampoline>` works correctly.
817	#[test]
818	fn test_memo_from_task() {
819		// Trampoline requires Send, so we use a Send-compatible value
820		let task = Trampoline::pure(42);
821		let memo = RcLazy::from(task);
822		assert_eq!(*memo.evaluate(), 42);
823	}
824
825	/// Tests Defer implementation.
826	#[test]
827	fn test_defer() {
828		use crate::classes::deferrable::defer;
829
830		let memo: RcLazy<i32> = defer(|| RcLazy::new(|| 42));
831		assert_eq!(*memo.evaluate(), 42);
832	}
833
834	/// Tests SendDefer implementation.
835	#[test]
836	fn test_send_defer() {
837		use crate::classes::send_deferrable::send_defer;
838
839		let memo: ArcLazy<i32> = send_defer(|| ArcLazy::new(|| 42));
840		assert_eq!(*memo.evaluate(), 42);
841	}
842
843	/// Tests `RcLazy::pure`.
844	///
845	/// Verifies that `pure` creates a lazy value from a pre-computed value.
846	#[test]
847	fn test_rc_lazy_pure() {
848		let lazy = RcLazy::pure(42);
849		assert_eq!(*lazy.evaluate(), 42);
850
851		// Verify it's still lazy (shares cache)
852		let shared = lazy.clone();
853		assert_eq!(*shared.evaluate(), 42);
854	}
855
856	/// Tests `ArcLazy::pure`.
857	///
858	/// Verifies that `pure` creates a thread-safe lazy value from a pre-computed value.
859	#[test]
860	fn test_arc_lazy_pure() {
861		let lazy = ArcLazy::pure(42);
862		assert_eq!(*lazy.evaluate(), 42);
863
864		// Verify it's still lazy (shares cache)
865		let shared = lazy.clone();
866		assert_eq!(*shared.evaluate(), 42);
867	}
868
869	/// Tests `ArcLazy::pure` with thread safety.
870	///
871	/// Verifies that `pure` works across threads.
872	#[test]
873	fn test_arc_lazy_pure_thread_safety() {
874		use std::thread;
875
876		let lazy = ArcLazy::pure(42);
877
878		let mut handles = vec![];
879		for _ in 0 .. 10 {
880			let lazy_clone = lazy.clone();
881			handles.push(thread::spawn(move || {
882				assert_eq!(*lazy_clone.evaluate(), 42);
883			}));
884		}
885
886		for handle in handles {
887			handle.join().unwrap();
888		}
889	}
890}