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