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	pub struct Lazy<'a, A, Config: LazyConfig = RcLazyConfig>(
395		/// The internal lazy cell.
396		pub(crate) Config::Lazy<'a, A>,
397	)
398	where
399		A: 'a;
400
401	#[document_type_parameters(
402		"The lifetime of the reference.",
403		"The type of the computed value.",
404		"The memoization configuration (determines Rc vs Arc)."
405	)]
406	#[document_parameters("The instance to clone.")]
407	impl<'a, A, Config: LazyConfig> Clone for Lazy<'a, A, Config>
408	where
409		A: 'a,
410	{
411		#[document_signature]
412		#[document_returns("A new `Lazy` instance that shares the same underlying memoized value.")]
413		#[document_examples]
414		///
415		/// ```
416		/// use fp_library::types::*;
417		///
418		/// let memo = Lazy::<_, RcLazyConfig>::new(|| 5);
419		/// let shared = memo.clone();
420		///
421		/// // First force computes and caches:
422		/// let value = memo.evaluate();
423		///
424		/// // Second force returns cached value (shared sees same result):
425		/// assert_eq!(shared.evaluate(), value);
426		/// ```
427		fn clone(&self) -> Self {
428			Self(self.0.clone())
429		}
430	}
431
432	#[document_type_parameters(
433		"The lifetime of the reference.",
434		"The type of the computed value.",
435		"The memoization configuration (determines Rc vs Arc)."
436	)]
437	#[document_parameters("The lazy instance.")]
438	impl<'a, A, Config: LazyConfig> Lazy<'a, A, Config>
439	where
440		A: 'a,
441	{
442		/// Gets the memoized value, computing on first access.
443		#[document_signature]
444		///
445		#[document_returns("A reference to the memoized value.")]
446		///
447		#[document_examples]
448		///
449		/// ```
450		/// use fp_library::types::*;
451		///
452		/// let memo = Lazy::<_, RcLazyConfig>::new(|| 42);
453		/// assert_eq!(*memo.evaluate(), 42);
454		/// ```
455		pub fn evaluate(&self) -> &A {
456			Config::evaluate(&self.0)
457		}
458	}
459
460	#[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
461	#[document_parameters("The lazy instance.")]
462	impl<'a, A> Lazy<'a, A, RcLazyConfig>
463	where
464		A: 'a,
465	{
466		/// Creates a new Lazy that will run `f` on first access.
467		#[document_signature]
468		///
469		#[document_parameters("The closure that produces the value.")]
470		///
471		#[document_returns("A new `Lazy` instance.")]
472		///
473		#[document_examples]
474		///
475		/// ```
476		/// use fp_library::types::*;
477		///
478		/// let memo = Lazy::<_, RcLazyConfig>::new(|| 42);
479		/// assert_eq!(*memo.evaluate(), 42);
480		/// ```
481		pub fn new(f: impl FnOnce() -> A + 'a) -> Self {
482			Lazy(RcLazyConfig::lazy_new(Box::new(f)))
483		}
484
485		/// Creates a `Lazy` from an already-computed value.
486		///
487		/// The value is immediately available without any computation.
488		#[document_signature]
489		///
490		#[document_parameters("The pre-computed value to wrap.")]
491		///
492		#[document_returns("A new `Lazy` instance containing the value.")]
493		///
494		#[document_examples]
495		///
496		/// ```
497		/// use fp_library::types::*;
498		///
499		/// let lazy = Lazy::<_, RcLazyConfig>::pure(42);
500		/// assert_eq!(*lazy.evaluate(), 42);
501		/// ```
502		pub fn pure(a: A) -> Self {
503			Lazy(RcLazyConfig::lazy_new(Box::new(move || a)))
504		}
505
506		/// Maps a function over the memoized value by reference.
507		///
508		/// This is the inherent method form of [`RefFunctor::ref_map`](crate::classes::ref_functor::RefFunctor::ref_map).
509		/// The mapping function receives a reference to the cached value and returns a new value,
510		/// which is itself lazily memoized.
511		#[document_signature]
512		#[document_type_parameters("The type of the result.")]
513		#[document_parameters("The function to apply to the memoized value.")]
514		#[document_returns("A new `Lazy` instance containing the mapped value.")]
515		#[document_examples]
516		///
517		/// ```
518		/// use fp_library::types::*;
519		///
520		/// let memo = Lazy::<_, RcLazyConfig>::new(|| 10);
521		/// let mapped = memo.ref_map(|x| *x * 2);
522		/// assert_eq!(*mapped.evaluate(), 20);
523		/// ```
524		pub fn ref_map<B: 'a>(
525			self,
526			f: impl FnOnce(&A) -> B + 'a,
527		) -> Lazy<'a, B, RcLazyConfig> {
528			let fa = self.clone();
529			let init: Box<dyn FnOnce() -> B + 'a> = Box::new(move || f(fa.evaluate()));
530			Lazy(RcLazyConfig::lazy_new(init))
531		}
532	}
533
534	#[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
535	impl<'a, A> From<Thunk<'a, A>> for Lazy<'a, A, RcLazyConfig> {
536		#[document_signature]
537		#[document_parameters("The thunk to convert.")]
538		#[document_returns("A new `Lazy` instance that will evaluate the thunk on first access.")]
539		#[document_examples]
540		///
541		/// ```
542		/// use fp_library::types::*;
543		/// let thunk = Thunk::new(|| 42);
544		/// let lazy = Lazy::from(thunk);
545		/// assert_eq!(*lazy.evaluate(), 42);
546		/// ```
547		fn from(eval: Thunk<'a, A>) -> Self {
548			Self::new(move || eval.evaluate())
549		}
550	}
551
552	#[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
553	impl<'a, A> From<Trampoline<A>> for Lazy<'a, A, RcLazyConfig>
554	where
555		A: Send,
556	{
557		#[document_signature]
558		#[document_parameters("The trampoline to convert.")]
559		#[document_returns(
560			"A new `Lazy` instance that will evaluate the trampoline on first access."
561		)]
562		#[document_examples]
563		///
564		/// ```
565		/// use fp_library::types::*;
566		/// let task = Trampoline::pure(42);
567		/// let lazy = Lazy::from(task);
568		/// assert_eq!(*lazy.evaluate(), 42);
569		/// ```
570		fn from(task: Trampoline<A>) -> Self {
571			Self::new(move || task.evaluate())
572		}
573	}
574
575	#[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
576	impl<'a, A> Lazy<'a, A, ArcLazyConfig>
577	where
578		A: 'a,
579	{
580		/// Creates a new Lazy that will run `f` on first access.
581		#[document_signature]
582		///
583		#[document_parameters("The closure that produces the value.")]
584		///
585		#[document_returns("A new `Lazy` instance.")]
586		///
587		#[document_examples]
588		///
589		/// ```
590		/// use fp_library::types::*;
591		///
592		/// let lazy = Lazy::<_, ArcLazyConfig>::new(|| 42);
593		/// assert_eq!(*lazy.evaluate(), 42);
594		/// ```
595		pub fn new(f: impl FnOnce() -> A + Send + 'a) -> Self {
596			Lazy(ArcLazyConfig::lazy_new(Box::new(f)))
597		}
598
599		/// Creates a `Lazy` from an already-computed value.
600		///
601		/// The value is immediately available without any computation.
602		/// Requires `Send` since `ArcLazy` is thread-safe.
603		#[document_signature]
604		///
605		#[document_parameters("The pre-computed value to wrap.")]
606		///
607		#[document_returns("A new `Lazy` instance containing the value.")]
608		///
609		#[document_examples]
610		///
611		/// ```
612		/// use fp_library::types::*;
613		///
614		/// let lazy = Lazy::<_, ArcLazyConfig>::pure(42);
615		/// assert_eq!(*lazy.evaluate(), 42);
616		/// ```
617		pub fn pure(a: A) -> Self
618		where
619			A: Send, {
620			Lazy(ArcLazyConfig::lazy_new(Box::new(move || a)))
621		}
622	}
623
624	/// Single-threaded memoization alias.
625	pub type RcLazy<'a, A> = Lazy<'a, A, RcLazyConfig>;
626
627	/// Thread-safe memoization alias.
628	pub type ArcLazy<'a, A> = Lazy<'a, A, ArcLazyConfig>;
629
630	impl_kind! {
631		impl<Config: LazyConfig> for LazyBrand<Config> {
632			type Of<'a, A: 'a>: 'a = Lazy<'a, A, Config>;
633		}
634	}
635
636	#[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
637	impl<'a, A> Deferrable<'a> for Lazy<'a, A, RcLazyConfig>
638	where
639		A: Clone + 'a,
640	{
641		/// Defers a computation that produces a `Lazy` value.
642		///
643		/// This flattens the nested structure: instead of `Lazy<Lazy<A>>`, we get `Lazy<A>`.
644		/// The inner `Lazy` is computed only when the outer `Lazy` is evaluated.
645		#[document_signature]
646		///
647		#[document_parameters("The thunk that produces the lazy value.")]
648		///
649		#[document_returns("A new `Lazy` value.")]
650		///
651		#[document_examples]
652		///
653		/// ```
654		/// use fp_library::{
655		/// 	brands::*,
656		/// 	classes::*,
657		/// 	functions::*,
658		/// 	types::*,
659		/// };
660		///
661		/// let lazy = Lazy::<_, RcLazyConfig>::defer(|| RcLazy::pure(42));
662		/// assert_eq!(*lazy.evaluate(), 42);
663		/// ```
664		fn defer(f: impl FnOnce() -> Self + 'a) -> Self
665		where
666			Self: Sized, {
667			RcLazy::new(move || f().evaluate().clone())
668		}
669	}
670
671	#[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
672	impl<'a, A> SendDeferrable<'a> for Lazy<'a, A, ArcLazyConfig>
673	where
674		A: Clone + Send + Sync + 'a,
675	{
676		/// Defers a computation that produces a thread-safe `Lazy` value.
677		///
678		/// This flattens the nested structure: instead of `ArcLazy<ArcLazy<A>>`, we get `ArcLazy<A>`.
679		/// The inner `Lazy` is computed only when the outer `Lazy` is evaluated.
680		#[document_signature]
681		///
682		#[document_parameters("The thunk that produces the lazy value.")]
683		///
684		#[document_returns("A new `ArcLazy` value.")]
685		///
686		#[document_examples]
687		///
688		/// ```
689		/// use fp_library::{
690		/// 	brands::*,
691		/// 	classes::*,
692		/// 	types::*,
693		/// };
694		///
695		/// let lazy = ArcLazy::send_defer(|| ArcLazy::pure(42));
696		/// assert_eq!(*lazy.evaluate(), 42);
697		/// ```
698		fn send_defer(f: impl FnOnce() -> Self + Send + Sync + 'a) -> Self
699		where
700			Self: Sized, {
701			ArcLazy::new(move || f().evaluate().clone())
702		}
703	}
704
705	impl RefFunctor for LazyBrand<RcLazyConfig> {
706		/// Maps a function over the memoized value, where the function takes a reference.
707		#[document_signature]
708		///
709		#[document_type_parameters(
710			"The lifetime of the values.",
711			"The type of the value.",
712			"The type of the result."
713		)]
714		///
715		#[document_parameters("The function to apply.", "The memoized value.")]
716		///
717		#[document_returns("A new memoized value.")]
718		///
719		#[document_examples]
720		///
721		/// ```
722		/// use fp_library::{
723		/// 	brands::*,
724		/// 	classes::*,
725		/// 	types::*,
726		/// };
727		///
728		/// let memo = Lazy::<_, RcLazyConfig>::new(|| 10);
729		/// let mapped = LazyBrand::<RcLazyConfig>::ref_map(|x: &i32| *x * 2, memo);
730		/// assert_eq!(*mapped.evaluate(), 20);
731		/// ```
732		fn ref_map<'a, A: 'a, B: 'a>(
733			f: impl FnOnce(&A) -> B + 'a,
734			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
735		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
736			fa.ref_map(f)
737		}
738	}
739}
740
741pub use inner::*;
742
743#[cfg(test)]
744mod tests {
745	use {
746		super::inner::*,
747		crate::types::{
748			Thunk,
749			Trampoline,
750		},
751		quickcheck_macros::quickcheck,
752		std::{
753			cell::RefCell,
754			rc::Rc,
755			sync::Arc,
756		},
757	};
758
759	/// Tests that `Lazy` caches the result of the computation.
760	///
761	/// Verifies that the initializer is called only once.
762	#[test]
763	fn test_memo_caching() {
764		let counter = Rc::new(RefCell::new(0));
765		let counter_clone = counter.clone();
766		let memo = RcLazy::new(move || {
767			*counter_clone.borrow_mut() += 1;
768			42
769		});
770
771		assert_eq!(*counter.borrow(), 0);
772		assert_eq!(*memo.evaluate(), 42);
773		assert_eq!(*counter.borrow(), 1);
774		assert_eq!(*memo.evaluate(), 42);
775		assert_eq!(*counter.borrow(), 1);
776	}
777
778	/// Tests that `Lazy` shares the cache across clones.
779	///
780	/// Verifies that clones see the same value and don't recompute.
781	#[test]
782	fn test_memo_sharing() {
783		let counter = Rc::new(RefCell::new(0));
784		let counter_clone = counter.clone();
785		let memo = RcLazy::new(move || {
786			*counter_clone.borrow_mut() += 1;
787			42
788		});
789		let shared = memo.clone();
790
791		assert_eq!(*memo.evaluate(), 42);
792		assert_eq!(*counter.borrow(), 1);
793		assert_eq!(*shared.evaluate(), 42);
794		assert_eq!(*counter.borrow(), 1);
795	}
796
797	/// Tests thread safety of `ArcLazy`.
798	///
799	/// Verifies that `ArcLazy` can be shared across threads and computes only once.
800	#[test]
801	fn test_arc_memo_thread_safety() {
802		use std::{
803			sync::atomic::{
804				AtomicUsize,
805				Ordering,
806			},
807			thread,
808		};
809
810		let counter = Arc::new(AtomicUsize::new(0));
811		let counter_clone = counter.clone();
812		let memo = ArcLazy::new(move || {
813			counter_clone.fetch_add(1, Ordering::SeqCst);
814			42
815		});
816
817		let mut handles = vec![];
818		for _ in 0 .. 10 {
819			let memo_clone = memo.clone();
820			handles.push(thread::spawn(move || {
821				assert_eq!(*memo_clone.evaluate(), 42);
822			}));
823		}
824
825		for handle in handles {
826			handle.join().unwrap();
827		}
828
829		assert_eq!(counter.load(Ordering::SeqCst), 1);
830	}
831
832	/// Tests creation from `Thunk`.
833	///
834	/// Verifies `From<Thunk>` works correctly.
835	#[test]
836	fn test_memo_from_eval() {
837		let eval = Thunk::new(|| 42);
838		let memo = RcLazy::from(eval);
839		assert_eq!(*memo.evaluate(), 42);
840	}
841
842	/// Tests creation from `Trampoline`.
843	///
844	/// Verifies `From<Trampoline>` works correctly.
845	#[test]
846	fn test_memo_from_task() {
847		// Trampoline requires Send, so we use a Send-compatible value
848		let task = Trampoline::pure(42);
849		let memo = RcLazy::from(task);
850		assert_eq!(*memo.evaluate(), 42);
851	}
852
853	/// Tests Defer implementation.
854	#[test]
855	fn test_defer() {
856		use crate::classes::deferrable::defer;
857
858		let memo: RcLazy<i32> = defer(|| RcLazy::new(|| 42));
859		assert_eq!(*memo.evaluate(), 42);
860	}
861
862	/// Tests SendDefer implementation.
863	#[test]
864	fn test_send_defer() {
865		use crate::classes::send_deferrable::send_defer;
866
867		let memo: ArcLazy<i32> = send_defer(|| ArcLazy::new(|| 42));
868		assert_eq!(*memo.evaluate(), 42);
869	}
870
871	/// Tests `RcLazy::pure`.
872	///
873	/// Verifies that `pure` creates a lazy value from a pre-computed value.
874	#[test]
875	fn test_rc_lazy_pure() {
876		let lazy = RcLazy::pure(42);
877		assert_eq!(*lazy.evaluate(), 42);
878
879		// Verify it's still lazy (shares cache)
880		let shared = lazy.clone();
881		assert_eq!(*shared.evaluate(), 42);
882	}
883
884	/// Tests `ArcLazy::pure`.
885	///
886	/// Verifies that `pure` creates a thread-safe lazy value from a pre-computed value.
887	#[test]
888	fn test_arc_lazy_pure() {
889		let lazy = ArcLazy::pure(42);
890		assert_eq!(*lazy.evaluate(), 42);
891
892		// Verify it's still lazy (shares cache)
893		let shared = lazy.clone();
894		assert_eq!(*shared.evaluate(), 42);
895	}
896
897	/// Tests `ArcLazy::pure` with thread safety.
898	///
899	/// Verifies that `pure` works across threads.
900	#[test]
901	fn test_arc_lazy_pure_thread_safety() {
902		use std::thread;
903
904		let lazy = ArcLazy::pure(42);
905
906		let mut handles = vec![];
907		for _ in 0 .. 10 {
908			let lazy_clone = lazy.clone();
909			handles.push(thread::spawn(move || {
910				assert_eq!(*lazy_clone.evaluate(), 42);
911			}));
912		}
913
914		for handle in handles {
915			handle.join().unwrap();
916		}
917	}
918
919	// Memoization Properties
920
921	/// Property: getting a memoized value twice returns the same result (Rc).
922	#[quickcheck]
923	fn prop_rc_memo_get_memoization(x: i32) -> bool {
924		let memo = RcLazy::new(move || x.wrapping_mul(2));
925		let result1 = *memo.evaluate();
926		let result2 = *memo.evaluate();
927		result1 == result2
928	}
929
930	/// Property: getting a memoized value twice returns the same result (Arc).
931	#[quickcheck]
932	fn prop_arc_memo_get_memoization(x: i32) -> bool {
933		let memo = ArcLazy::new(move || x.wrapping_mul(2));
934		let result1 = *memo.evaluate();
935		let result2 = *memo.evaluate();
936		result1 == result2
937	}
938
939	// Clone Equivalence Properties
940
941	/// Property: cloning an RcLazy shares state.
942	#[quickcheck]
943	fn prop_rc_memo_clone_shares_state(x: i32) -> bool {
944		let memo1 = RcLazy::new(move || x);
945		let memo2 = memo1.clone();
946
947		let result1 = *memo1.evaluate();
948		let result2 = *memo2.evaluate();
949		result1 == result2
950	}
951
952	/// Property: cloning an ArcLazy shares state.
953	#[quickcheck]
954	fn prop_arc_memo_clone_shares_state(x: i32) -> bool {
955		let memo1 = ArcLazy::new(move || x);
956		let memo2 = memo1.clone();
957
958		let result1 = *memo1.evaluate();
959		let result2 = *memo2.evaluate();
960		result1 == result2
961	}
962
963	/// Property: getting original then clone gives same result.
964	#[quickcheck]
965	fn prop_memo_get_original_then_clone(x: String) -> bool {
966		let value = x.clone();
967		let memo = RcLazy::new(move || value.clone());
968		let memo_clone = memo.clone();
969
970		let result1 = memo.evaluate().clone();
971		let result2 = memo_clone.evaluate().clone();
972
973		result1 == result2
974	}
975
976	// Determinism Properties
977
978	/// Property: lazy computation is deterministic.
979	#[quickcheck]
980	fn prop_memo_deterministic(
981		x: i32,
982		y: i32,
983	) -> bool {
984		let memo1 = RcLazy::new(move || x.wrapping_add(y));
985		let memo2 = RcLazy::new(move || x.wrapping_add(y));
986
987		*memo1.evaluate() == *memo2.evaluate()
988	}
989}