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