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