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		quickcheck_macros::quickcheck,
751		std::{
752			cell::RefCell,
753			rc::Rc,
754			sync::Arc,
755		},
756	};
757
758	/// Tests that `Lazy` caches the result of the computation.
759	///
760	/// Verifies that the initializer is called only once.
761	#[test]
762	fn test_memo_caching() {
763		let counter = Rc::new(RefCell::new(0));
764		let counter_clone = counter.clone();
765		let memo = RcLazy::new(move || {
766			*counter_clone.borrow_mut() += 1;
767			42
768		});
769
770		assert_eq!(*counter.borrow(), 0);
771		assert_eq!(*memo.evaluate(), 42);
772		assert_eq!(*counter.borrow(), 1);
773		assert_eq!(*memo.evaluate(), 42);
774		assert_eq!(*counter.borrow(), 1);
775	}
776
777	/// Tests that `Lazy` shares the cache across clones.
778	///
779	/// Verifies that clones see the same value and don't recompute.
780	#[test]
781	fn test_memo_sharing() {
782		let counter = Rc::new(RefCell::new(0));
783		let counter_clone = counter.clone();
784		let memo = RcLazy::new(move || {
785			*counter_clone.borrow_mut() += 1;
786			42
787		});
788		let shared = memo.clone();
789
790		assert_eq!(*memo.evaluate(), 42);
791		assert_eq!(*counter.borrow(), 1);
792		assert_eq!(*shared.evaluate(), 42);
793		assert_eq!(*counter.borrow(), 1);
794	}
795
796	/// Tests thread safety of `ArcLazy`.
797	///
798	/// Verifies that `ArcLazy` can be shared across threads and computes only once.
799	#[test]
800	fn test_arc_memo_thread_safety() {
801		use std::{
802			sync::atomic::{
803				AtomicUsize,
804				Ordering,
805			},
806			thread,
807		};
808
809		let counter = Arc::new(AtomicUsize::new(0));
810		let counter_clone = counter.clone();
811		let memo = ArcLazy::new(move || {
812			counter_clone.fetch_add(1, Ordering::SeqCst);
813			42
814		});
815
816		let mut handles = vec![];
817		for _ in 0 .. 10 {
818			let memo_clone = memo.clone();
819			handles.push(thread::spawn(move || {
820				assert_eq!(*memo_clone.evaluate(), 42);
821			}));
822		}
823
824		for handle in handles {
825			handle.join().unwrap();
826		}
827
828		assert_eq!(counter.load(Ordering::SeqCst), 1);
829	}
830
831	/// Tests creation from `Thunk`.
832	///
833	/// Verifies `From<Thunk>` works correctly.
834	#[test]
835	fn test_memo_from_eval() {
836		let eval = Thunk::new(|| 42);
837		let memo = RcLazy::from(eval);
838		assert_eq!(*memo.evaluate(), 42);
839	}
840
841	/// Tests creation from `Trampoline`.
842	///
843	/// Verifies `From<Trampoline>` works correctly.
844	#[test]
845	fn test_memo_from_task() {
846		// Trampoline requires Send, so we use a Send-compatible value
847		let task = Trampoline::pure(42);
848		let memo = RcLazy::from(task);
849		assert_eq!(*memo.evaluate(), 42);
850	}
851
852	/// Tests Defer implementation.
853	#[test]
854	fn test_defer() {
855		use crate::classes::deferrable::defer;
856
857		let memo: RcLazy<i32> = defer(|| RcLazy::new(|| 42));
858		assert_eq!(*memo.evaluate(), 42);
859	}
860
861	/// Tests SendDefer implementation.
862	#[test]
863	fn test_send_defer() {
864		use crate::classes::send_deferrable::send_defer;
865
866		let memo: ArcLazy<i32> = send_defer(|| ArcLazy::new(|| 42));
867		assert_eq!(*memo.evaluate(), 42);
868	}
869
870	/// Tests `RcLazy::pure`.
871	///
872	/// Verifies that `pure` creates a lazy value from a pre-computed value.
873	#[test]
874	fn test_rc_lazy_pure() {
875		let lazy = RcLazy::pure(42);
876		assert_eq!(*lazy.evaluate(), 42);
877
878		// Verify it's still lazy (shares cache)
879		let shared = lazy.clone();
880		assert_eq!(*shared.evaluate(), 42);
881	}
882
883	/// Tests `ArcLazy::pure`.
884	///
885	/// Verifies that `pure` creates a thread-safe lazy value from a pre-computed value.
886	#[test]
887	fn test_arc_lazy_pure() {
888		let lazy = ArcLazy::pure(42);
889		assert_eq!(*lazy.evaluate(), 42);
890
891		// Verify it's still lazy (shares cache)
892		let shared = lazy.clone();
893		assert_eq!(*shared.evaluate(), 42);
894	}
895
896	/// Tests `ArcLazy::pure` with thread safety.
897	///
898	/// Verifies that `pure` works across threads.
899	#[test]
900	fn test_arc_lazy_pure_thread_safety() {
901		use std::thread;
902
903		let lazy = ArcLazy::pure(42);
904
905		let mut handles = vec![];
906		for _ in 0 .. 10 {
907			let lazy_clone = lazy.clone();
908			handles.push(thread::spawn(move || {
909				assert_eq!(*lazy_clone.evaluate(), 42);
910			}));
911		}
912
913		for handle in handles {
914			handle.join().unwrap();
915		}
916	}
917
918	// Memoization Properties
919
920	/// Property: getting a memoized value twice returns the same result (Rc).
921	#[quickcheck]
922	fn prop_rc_memo_get_memoization(x: i32) -> bool {
923		let memo = RcLazy::new(move || x.wrapping_mul(2));
924		let result1 = *memo.evaluate();
925		let result2 = *memo.evaluate();
926		result1 == result2
927	}
928
929	/// Property: getting a memoized value twice returns the same result (Arc).
930	#[quickcheck]
931	fn prop_arc_memo_get_memoization(x: i32) -> bool {
932		let memo = ArcLazy::new(move || x.wrapping_mul(2));
933		let result1 = *memo.evaluate();
934		let result2 = *memo.evaluate();
935		result1 == result2
936	}
937
938	// Clone Equivalence Properties
939
940	/// Property: cloning an RcLazy shares state.
941	#[quickcheck]
942	fn prop_rc_memo_clone_shares_state(x: i32) -> bool {
943		let memo1 = RcLazy::new(move || x);
944		let memo2 = memo1.clone();
945
946		let result1 = *memo1.evaluate();
947		let result2 = *memo2.evaluate();
948		result1 == result2
949	}
950
951	/// Property: cloning an ArcLazy shares state.
952	#[quickcheck]
953	fn prop_arc_memo_clone_shares_state(x: i32) -> bool {
954		let memo1 = ArcLazy::new(move || x);
955		let memo2 = memo1.clone();
956
957		let result1 = *memo1.evaluate();
958		let result2 = *memo2.evaluate();
959		result1 == result2
960	}
961
962	/// Property: getting original then clone gives same result.
963	#[quickcheck]
964	fn prop_memo_get_original_then_clone(x: String) -> bool {
965		let value = x.clone();
966		let memo = RcLazy::new(move || value.clone());
967		let memo_clone = memo.clone();
968
969		let result1 = memo.evaluate().clone();
970		let result2 = memo_clone.evaluate().clone();
971
972		result1 == result2
973	}
974
975	// Determinism Properties
976
977	/// Property: lazy computation is deterministic.
978	#[quickcheck]
979	fn prop_memo_deterministic(
980		x: i32,
981		y: i32,
982	) -> bool {
983		let memo1 = RcLazy::new(move || x.wrapping_add(y));
984		let memo2 = RcLazy::new(move || x.wrapping_add(y));
985
986		*memo1.evaluate() == *memo2.evaluate()
987	}
988}