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	#[document_parameters("The lazy instance.")]
461	impl<'a, A> Lazy<'a, A, RcLazyConfig>
462	where
463		A: 'a,
464	{
465		/// Creates a new Lazy that will run `f` on first access.
466		#[document_signature]
467		///
468		#[document_parameters("The closure that produces the value.")]
469		///
470		#[document_returns("A new `Lazy` instance.")]
471		///
472		#[document_examples]
473		///
474		/// ```
475		/// use fp_library::types::*;
476		///
477		/// let memo = Lazy::<_, RcLazyConfig>::new(|| 42);
478		/// assert_eq!(*memo.evaluate(), 42);
479		/// ```
480		pub fn new(f: impl FnOnce() -> A + 'a) -> Self {
481			Lazy(RcLazyConfig::lazy_new(Box::new(f)))
482		}
483
484		/// Creates a `Lazy` from an already-computed value.
485		///
486		/// The value is immediately available without any computation.
487		#[document_signature]
488		///
489		#[document_parameters("The pre-computed value to wrap.")]
490		///
491		#[document_returns("A new `Lazy` instance containing the value.")]
492		///
493		#[document_examples]
494		///
495		/// ```
496		/// use fp_library::types::*;
497		///
498		/// let lazy = Lazy::<_, RcLazyConfig>::pure(42);
499		/// assert_eq!(*lazy.evaluate(), 42);
500		/// ```
501		pub fn pure(a: A) -> Self {
502			Lazy(RcLazyConfig::lazy_new(Box::new(move || a)))
503		}
504
505		/// Maps a function over the memoized value by reference.
506		///
507		/// This is the inherent method form of [`RefFunctor::ref_map`](crate::classes::ref_functor::RefFunctor::ref_map).
508		/// The mapping function receives a reference to the cached value and returns a new value,
509		/// which is itself lazily memoized.
510		#[document_signature]
511		#[document_type_parameters("The type of the result.")]
512		#[document_parameters("The function to apply to the memoized value.")]
513		#[document_returns("A new `Lazy` instance containing the mapped value.")]
514		#[document_examples]
515		///
516		/// ```
517		/// use fp_library::types::*;
518		///
519		/// let memo = Lazy::<_, RcLazyConfig>::new(|| 10);
520		/// let mapped = memo.ref_map(|x| *x * 2);
521		/// assert_eq!(*mapped.evaluate(), 20);
522		/// ```
523		pub fn ref_map<B: 'a>(
524			self,
525			f: impl FnOnce(&A) -> B + 'a,
526		) -> Lazy<'a, B, RcLazyConfig> {
527			let fa = self.clone();
528			let init: Box<dyn FnOnce() -> B + 'a> = Box::new(move || f(fa.evaluate()));
529			Lazy(RcLazyConfig::lazy_new(init))
530		}
531	}
532
533	#[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
534	impl<'a, A> From<Thunk<'a, A>> for Lazy<'a, A, RcLazyConfig> {
535		#[document_signature]
536		#[document_parameters("The thunk to convert.")]
537		#[document_returns("A new `Lazy` instance that will evaluate the thunk on first access.")]
538		#[document_examples]
539		///
540		/// ```
541		/// use fp_library::types::*;
542		/// let thunk = Thunk::new(|| 42);
543		/// let lazy = Lazy::from(thunk);
544		/// assert_eq!(*lazy.evaluate(), 42);
545		/// ```
546		fn from(eval: Thunk<'a, A>) -> Self {
547			Self::new(move || eval.evaluate())
548		}
549	}
550
551	#[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
552	impl<'a, A> From<Trampoline<A>> for Lazy<'a, A, RcLazyConfig>
553	where
554		A: Send,
555	{
556		#[document_signature]
557		#[document_parameters("The trampoline to convert.")]
558		#[document_returns(
559			"A new `Lazy` instance that will evaluate the trampoline on first access."
560		)]
561		#[document_examples]
562		///
563		/// ```
564		/// use fp_library::types::*;
565		/// let task = Trampoline::pure(42);
566		/// let lazy = Lazy::from(task);
567		/// assert_eq!(*lazy.evaluate(), 42);
568		/// ```
569		fn from(task: Trampoline<A>) -> Self {
570			Self::new(move || task.evaluate())
571		}
572	}
573
574	#[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
575	impl<'a, A> Lazy<'a, A, ArcLazyConfig>
576	where
577		A: 'a,
578	{
579		/// Creates a new Lazy that will run `f` on first access.
580		#[document_signature]
581		///
582		#[document_parameters("The closure that produces the value.")]
583		///
584		#[document_returns("A new `Lazy` instance.")]
585		///
586		#[document_examples]
587		///
588		/// ```
589		/// use fp_library::types::*;
590		///
591		/// let lazy = Lazy::<_, ArcLazyConfig>::new(|| 42);
592		/// assert_eq!(*lazy.evaluate(), 42);
593		/// ```
594		pub fn new(f: impl FnOnce() -> A + Send + 'a) -> Self {
595			Lazy(ArcLazyConfig::lazy_new(Box::new(f)))
596		}
597
598		/// Creates a `Lazy` from an already-computed value.
599		///
600		/// The value is immediately available without any computation.
601		/// Requires `Send` since `ArcLazy` is thread-safe.
602		#[document_signature]
603		///
604		#[document_parameters("The pre-computed value to wrap.")]
605		///
606		#[document_returns("A new `Lazy` instance containing the value.")]
607		///
608		#[document_examples]
609		///
610		/// ```
611		/// use fp_library::types::*;
612		///
613		/// let lazy = Lazy::<_, ArcLazyConfig>::pure(42);
614		/// assert_eq!(*lazy.evaluate(), 42);
615		/// ```
616		pub fn pure(a: A) -> Self
617		where
618			A: Send, {
619			Lazy(ArcLazyConfig::lazy_new(Box::new(move || a)))
620		}
621	}
622
623	/// Single-threaded memoization alias.
624	pub type RcLazy<'a, A> = Lazy<'a, A, RcLazyConfig>;
625
626	/// Thread-safe memoization alias.
627	pub type ArcLazy<'a, A> = Lazy<'a, A, ArcLazyConfig>;
628
629	impl_kind! {
630		impl<Config: LazyConfig> for LazyBrand<Config> {
631			type Of<'a, A: 'a>: 'a = Lazy<'a, A, Config>;
632		}
633	}
634
635	#[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
636	impl<'a, A> Deferrable<'a> for Lazy<'a, A, RcLazyConfig>
637	where
638		A: Clone + 'a,
639	{
640		/// Defers a computation that produces a `Lazy` value.
641		///
642		/// This flattens the nested structure: instead of `Lazy<Lazy<A>>`, we get `Lazy<A>`.
643		/// The inner `Lazy` is computed only when the outer `Lazy` is evaluated.
644		#[document_signature]
645		///
646		#[document_parameters("The thunk that produces the lazy value.")]
647		///
648		#[document_returns("A new `Lazy` value.")]
649		///
650		#[document_examples]
651		///
652		/// ```
653		/// use fp_library::{
654		/// 	brands::*,
655		/// 	classes::*,
656		/// 	functions::*,
657		/// 	types::*,
658		/// };
659		///
660		/// let lazy = Lazy::<_, RcLazyConfig>::defer(|| RcLazy::pure(42));
661		/// assert_eq!(*lazy.evaluate(), 42);
662		/// ```
663		fn defer(f: impl FnOnce() -> Self + 'a) -> Self
664		where
665			Self: Sized, {
666			RcLazy::new(move || f().evaluate().clone())
667		}
668	}
669
670	#[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
671	impl<'a, A> SendDeferrable<'a> for Lazy<'a, A, ArcLazyConfig>
672	where
673		A: Clone + Send + Sync + 'a,
674	{
675		/// Defers a computation that produces a thread-safe `Lazy` value.
676		///
677		/// This flattens the nested structure: instead of `ArcLazy<ArcLazy<A>>`, we get `ArcLazy<A>`.
678		/// The inner `Lazy` is computed only when the outer `Lazy` is evaluated.
679		#[document_signature]
680		///
681		#[document_parameters("The thunk that produces the lazy value.")]
682		///
683		#[document_returns("A new `ArcLazy` value.")]
684		///
685		#[document_examples]
686		///
687		/// ```
688		/// use fp_library::{
689		/// 	brands::*,
690		/// 	classes::*,
691		/// 	types::*,
692		/// };
693		///
694		/// let lazy = ArcLazy::send_defer(|| ArcLazy::pure(42));
695		/// assert_eq!(*lazy.evaluate(), 42);
696		/// ```
697		fn send_defer(f: impl FnOnce() -> Self + Send + Sync + 'a) -> Self
698		where
699			Self: Sized, {
700			ArcLazy::new(move || f().evaluate().clone())
701		}
702	}
703
704	impl RefFunctor for LazyBrand<RcLazyConfig> {
705		/// Maps a function over the memoized value, where the function takes a reference.
706		#[document_signature]
707		///
708		#[document_type_parameters(
709			"The lifetime of the values.",
710			"The type of the value.",
711			"The type of the result."
712		)]
713		///
714		#[document_parameters("The function to apply.", "The memoized value.")]
715		///
716		#[document_returns("A new memoized value.")]
717		///
718		#[document_examples]
719		///
720		/// ```
721		/// use fp_library::{
722		/// 	brands::*,
723		/// 	classes::*,
724		/// 	types::*,
725		/// };
726		///
727		/// let memo = Lazy::<_, RcLazyConfig>::new(|| 10);
728		/// let mapped = LazyBrand::<RcLazyConfig>::ref_map(|x: &i32| *x * 2, memo);
729		/// assert_eq!(*mapped.evaluate(), 20);
730		/// ```
731		fn ref_map<'a, A: 'a, B: 'a>(
732			f: impl FnOnce(&A) -> B + 'a,
733			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
734		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
735			fa.ref_map(f)
736		}
737	}
738}
739
740pub use inner::*;
741
742#[cfg(test)]
743mod tests {
744	use {
745		super::inner::*,
746		crate::types::{
747			Thunk,
748			Trampoline,
749		},
750		std::{
751			cell::RefCell,
752			rc::Rc,
753			sync::Arc,
754		},
755	};
756
757	/// Tests that `Lazy` caches the result of the computation.
758	///
759	/// Verifies that the initializer is called only once.
760	#[test]
761	fn test_memo_caching() {
762		let counter = Rc::new(RefCell::new(0));
763		let counter_clone = counter.clone();
764		let memo = RcLazy::new(move || {
765			*counter_clone.borrow_mut() += 1;
766			42
767		});
768
769		assert_eq!(*counter.borrow(), 0);
770		assert_eq!(*memo.evaluate(), 42);
771		assert_eq!(*counter.borrow(), 1);
772		assert_eq!(*memo.evaluate(), 42);
773		assert_eq!(*counter.borrow(), 1);
774	}
775
776	/// Tests that `Lazy` shares the cache across clones.
777	///
778	/// Verifies that clones see the same value and don't recompute.
779	#[test]
780	fn test_memo_sharing() {
781		let counter = Rc::new(RefCell::new(0));
782		let counter_clone = counter.clone();
783		let memo = RcLazy::new(move || {
784			*counter_clone.borrow_mut() += 1;
785			42
786		});
787		let shared = memo.clone();
788
789		assert_eq!(*memo.evaluate(), 42);
790		assert_eq!(*counter.borrow(), 1);
791		assert_eq!(*shared.evaluate(), 42);
792		assert_eq!(*counter.borrow(), 1);
793	}
794
795	/// Tests thread safety of `ArcLazy`.
796	///
797	/// Verifies that `ArcLazy` can be shared across threads and computes only once.
798	#[test]
799	fn test_arc_memo_thread_safety() {
800		use std::{
801			sync::atomic::{
802				AtomicUsize,
803				Ordering,
804			},
805			thread,
806		};
807
808		let counter = Arc::new(AtomicUsize::new(0));
809		let counter_clone = counter.clone();
810		let memo = ArcLazy::new(move || {
811			counter_clone.fetch_add(1, Ordering::SeqCst);
812			42
813		});
814
815		let mut handles = vec![];
816		for _ in 0 .. 10 {
817			let memo_clone = memo.clone();
818			handles.push(thread::spawn(move || {
819				assert_eq!(*memo_clone.evaluate(), 42);
820			}));
821		}
822
823		for handle in handles {
824			handle.join().unwrap();
825		}
826
827		assert_eq!(counter.load(Ordering::SeqCst), 1);
828	}
829
830	/// Tests creation from `Thunk`.
831	///
832	/// Verifies `From<Thunk>` works correctly.
833	#[test]
834	fn test_memo_from_eval() {
835		let eval = Thunk::new(|| 42);
836		let memo = RcLazy::from(eval);
837		assert_eq!(*memo.evaluate(), 42);
838	}
839
840	/// Tests creation from `Trampoline`.
841	///
842	/// Verifies `From<Trampoline>` works correctly.
843	#[test]
844	fn test_memo_from_task() {
845		// Trampoline requires Send, so we use a Send-compatible value
846		let task = Trampoline::pure(42);
847		let memo = RcLazy::from(task);
848		assert_eq!(*memo.evaluate(), 42);
849	}
850
851	/// Tests Defer implementation.
852	#[test]
853	fn test_defer() {
854		use crate::classes::deferrable::defer;
855
856		let memo: RcLazy<i32> = defer(|| RcLazy::new(|| 42));
857		assert_eq!(*memo.evaluate(), 42);
858	}
859
860	/// Tests SendDefer implementation.
861	#[test]
862	fn test_send_defer() {
863		use crate::classes::send_deferrable::send_defer;
864
865		let memo: ArcLazy<i32> = send_defer(|| ArcLazy::new(|| 42));
866		assert_eq!(*memo.evaluate(), 42);
867	}
868
869	/// Tests `RcLazy::pure`.
870	///
871	/// Verifies that `pure` creates a lazy value from a pre-computed value.
872	#[test]
873	fn test_rc_lazy_pure() {
874		let lazy = RcLazy::pure(42);
875		assert_eq!(*lazy.evaluate(), 42);
876
877		// Verify it's still lazy (shares cache)
878		let shared = lazy.clone();
879		assert_eq!(*shared.evaluate(), 42);
880	}
881
882	/// Tests `ArcLazy::pure`.
883	///
884	/// Verifies that `pure` creates a thread-safe lazy value from a pre-computed value.
885	#[test]
886	fn test_arc_lazy_pure() {
887		let lazy = ArcLazy::pure(42);
888		assert_eq!(*lazy.evaluate(), 42);
889
890		// Verify it's still lazy (shares cache)
891		let shared = lazy.clone();
892		assert_eq!(*shared.evaluate(), 42);
893	}
894
895	/// Tests `ArcLazy::pure` with thread safety.
896	///
897	/// Verifies that `pure` works across threads.
898	#[test]
899	fn test_arc_lazy_pure_thread_safety() {
900		use std::thread;
901
902		let lazy = ArcLazy::pure(42);
903
904		let mut handles = vec![];
905		for _ in 0 .. 10 {
906			let lazy_clone = lazy.clone();
907			handles.push(thread::spawn(move || {
908				assert_eq!(*lazy_clone.evaluate(), 42);
909			}));
910		}
911
912		for handle in handles {
913			handle.join().unwrap();
914		}
915	}
916}