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}