1#[fp_macros::document_module]
6mod inner {
7 use {
8 crate::{
9 Apply,
10 brands::LazyBrand,
11 classes::{
12 Deferrable,
13 RefFunctor,
14 SendDeferrable,
15 },
16 impl_kind,
17 kinds::*,
18 types::{
19 Thunk,
20 Trampoline,
21 },
22 },
23 fp_macros::*,
24 std::{
25 cell::LazyCell,
26 rc::Rc,
27 sync::{
28 Arc,
29 LazyLock,
30 },
31 },
32 };
33
34 pub trait LazyConfig: 'static {
45 type Lazy<'a, A: 'a>: Clone;
47
48 type TryLazy<'a, A: 'a, E: 'a>: Clone;
50
51 type Thunk<'a, A: 'a>: ?Sized;
53
54 type TryThunk<'a, A: 'a, E: 'a>: ?Sized;
56
57 #[document_signature]
59 #[document_type_parameters("The lifetime of the computation.", "The type of the value.")]
61 #[document_parameters("The initializer thunk.")]
63 #[document_returns("A new lazy cell.")]
65 #[document_examples]
67 fn lazy_new<'a, A: 'a>(f: Box<Self::Thunk<'a, A>>) -> Self::Lazy<'a, A>;
75
76 #[document_signature]
78 #[document_type_parameters(
80 "The lifetime of the computation.",
81 "The type of the value.",
82 "The type of the error."
83 )]
84 #[document_parameters("The initializer thunk.")]
86 #[document_returns("A new fallible lazy cell.")]
88 #[document_examples]
90 fn try_lazy_new<'a, A: 'a, E: 'a>(
98 f: Box<Self::TryThunk<'a, A, E>>
99 ) -> Self::TryLazy<'a, A, E>;
100
101 #[document_signature]
103 #[document_type_parameters(
105 "The lifetime of the computation.",
106 "The lifetime of the reference.",
107 "The type of the value."
108 )]
109 #[document_parameters("The lazy cell to evaluate.")]
111 #[document_returns("A reference to the value.")]
113 #[document_examples]
115 fn evaluate<'a, 'b, A: 'a>(lazy: &'b Self::Lazy<'a, A>) -> &'b A;
123
124 #[document_signature]
126 #[document_type_parameters(
128 "The lifetime of the computation.",
129 "The lifetime of the reference.",
130 "The type of the value.",
131 "The type of the error."
132 )]
133 #[document_parameters("The fallible lazy cell to evaluate.")]
135 #[document_returns("A result containing a reference to the value or error.")]
137 #[document_examples]
139 fn try_evaluate<'a, 'b, A: 'a, E: 'a>(
147 lazy: &'b Self::TryLazy<'a, A, E>
148 ) -> Result<&'b A, &'b E>;
149 }
150
151 pub struct RcLazyConfig;
155
156 impl LazyConfig for RcLazyConfig {
157 type Lazy<'a, A: 'a> = Rc<LazyCell<A, Box<dyn FnOnce() -> A + 'a>>>;
158 type Thunk<'a, A: 'a> = dyn FnOnce() -> A + 'a;
159 type TryLazy<'a, A: 'a, E: 'a> =
160 Rc<LazyCell<Result<A, E>, Box<dyn FnOnce() -> Result<A, E> + 'a>>>;
161 type TryThunk<'a, A: 'a, E: 'a> = dyn FnOnce() -> Result<A, E> + 'a;
162
163 #[document_signature]
165 #[document_type_parameters("The lifetime of the computation.", "The type of the value.")]
167 #[document_parameters("The initializer thunk.")]
169 #[document_returns("A new lazy cell.")]
171 #[document_examples]
173 fn lazy_new<'a, A: 'a>(f: Box<Self::Thunk<'a, A>>) -> Self::Lazy<'a, A> {
181 Rc::new(LazyCell::new(f))
182 }
183
184 #[document_signature]
186 #[document_type_parameters(
188 "The lifetime of the computation.",
189 "The type of the value.",
190 "The type of the error."
191 )]
192 #[document_parameters("The initializer thunk.")]
194 #[document_returns("A new fallible lazy cell.")]
196 #[document_examples]
198 fn try_lazy_new<'a, A: 'a, E: 'a>(
206 f: Box<Self::TryThunk<'a, A, E>>
207 ) -> Self::TryLazy<'a, A, E> {
208 Rc::new(LazyCell::new(f))
209 }
210
211 #[document_signature]
213 #[document_type_parameters(
215 "The lifetime of the computation.",
216 "The lifetime of the reference.",
217 "The type of the value."
218 )]
219 #[document_parameters("The lazy cell to evaluate.")]
221 #[document_returns("A reference to the value.")]
223 #[document_examples]
225 fn evaluate<'a, 'b, A: 'a>(lazy: &'b Self::Lazy<'a, A>) -> &'b A {
233 LazyCell::force(lazy)
234 }
235
236 #[document_signature]
238 #[document_type_parameters(
240 "The lifetime of the computation.",
241 "The lifetime of the reference.",
242 "The type of the value.",
243 "The type of the error."
244 )]
245 #[document_parameters("The fallible lazy cell to evaluate.")]
247 #[document_returns("A result containing a reference to the value or error.")]
249 #[document_examples]
251 fn try_evaluate<'a, 'b, A: 'a, E: 'a>(
259 lazy: &'b Self::TryLazy<'a, A, E>
260 ) -> Result<&'b A, &'b E> {
261 LazyCell::force(lazy).as_ref()
262 }
263 }
264
265 pub struct ArcLazyConfig;
269
270 impl LazyConfig for ArcLazyConfig {
271 type Lazy<'a, A: 'a> = Arc<LazyLock<A, Box<dyn FnOnce() -> A + Send + 'a>>>;
272 type Thunk<'a, A: 'a> = dyn FnOnce() -> A + Send + 'a;
273 type TryLazy<'a, A: 'a, E: 'a> =
274 Arc<LazyLock<Result<A, E>, Box<dyn FnOnce() -> Result<A, E> + Send + 'a>>>;
275 type TryThunk<'a, A: 'a, E: 'a> = dyn FnOnce() -> Result<A, E> + Send + 'a;
276
277 #[document_signature]
279 #[document_type_parameters("The lifetime of the computation.", "The type of the value.")]
281 #[document_parameters("The initializer thunk.")]
283 #[document_returns("A new lazy cell.")]
285 #[document_examples]
287 fn lazy_new<'a, A: 'a>(f: Box<Self::Thunk<'a, A>>) -> Self::Lazy<'a, A> {
295 Arc::new(LazyLock::new(f))
296 }
297
298 #[document_signature]
300 #[document_type_parameters(
302 "The lifetime of the computation.",
303 "The type of the value.",
304 "The type of the error."
305 )]
306 #[document_parameters("The initializer thunk.")]
308 #[document_returns("A new fallible lazy cell.")]
310 #[document_examples]
312 fn try_lazy_new<'a, A: 'a, E: 'a>(
320 f: Box<Self::TryThunk<'a, A, E>>
321 ) -> Self::TryLazy<'a, A, E> {
322 Arc::new(LazyLock::new(f))
323 }
324
325 #[document_signature]
327 #[document_type_parameters(
329 "The lifetime of the computation.",
330 "The lifetime of the reference.",
331 "The type of the value."
332 )]
333 #[document_parameters("The lazy cell to evaluate.")]
335 #[document_returns("A reference to the value.")]
337 #[document_examples]
339 fn evaluate<'a, 'b, A: 'a>(lazy: &'b Self::Lazy<'a, A>) -> &'b A {
347 LazyLock::force(lazy)
348 }
349
350 #[document_signature]
352 #[document_type_parameters(
354 "The lifetime of the computation.",
355 "The lifetime of the reference.",
356 "The type of the value.",
357 "The type of the error."
358 )]
359 #[document_parameters("The fallible lazy cell to evaluate.")]
361 #[document_returns("A result containing a reference to the value or error.")]
363 #[document_examples]
365 fn try_evaluate<'a, 'b, A: 'a, E: 'a>(
373 lazy: &'b Self::TryLazy<'a, A, E>
374 ) -> Result<&'b A, &'b E> {
375 LazyLock::force(lazy).as_ref()
376 }
377 }
378
379 #[document_type_parameters(
389 "The lifetime of the reference.",
390 "The type of the computed value.",
391 "The memoization configuration (determines Rc vs Arc)."
392 )]
393 #[document_fields("The internal lazy cell.")]
395 pub struct Lazy<'a, A, Config: LazyConfig = RcLazyConfig>(pub(crate) Config::Lazy<'a, A>)
397 where
398 A: 'a;
399
400 #[document_type_parameters(
401 "The lifetime of the reference.",
402 "The type of the computed value.",
403 "The memoization configuration (determines Rc vs Arc)."
404 )]
405 #[document_parameters("The instance to clone.")]
406 impl<'a, A, Config: LazyConfig> Clone for Lazy<'a, A, Config>
407 where
408 A: 'a,
409 {
410 #[document_signature]
411 #[document_returns("A new `Lazy` instance that shares the same underlying memoized value.")]
412 #[document_examples]
413 fn clone(&self) -> Self {
427 Self(self.0.clone())
428 }
429 }
430
431 #[document_type_parameters(
432 "The lifetime of the reference.",
433 "The type of the computed value.",
434 "The memoization configuration (determines Rc vs Arc)."
435 )]
436 #[document_parameters("The lazy instance.")]
437 impl<'a, A, Config: LazyConfig> Lazy<'a, A, Config>
438 where
439 A: 'a,
440 {
441 #[document_signature]
443 #[document_returns("A reference to the memoized value.")]
445 #[document_examples]
447 pub fn evaluate(&self) -> &A {
455 Config::evaluate(&self.0)
456 }
457 }
458
459 #[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
460 impl<'a, A> Lazy<'a, A, RcLazyConfig>
461 where
462 A: 'a,
463 {
464 #[document_signature]
466 #[document_parameters("The closure that produces the value.")]
468 #[document_returns("A new `Lazy` instance.")]
470 #[document_examples]
472 pub fn new(f: impl FnOnce() -> A + 'a) -> Self {
480 Lazy(RcLazyConfig::lazy_new(Box::new(f)))
481 }
482
483 #[document_signature]
487 #[document_parameters("The pre-computed value to wrap.")]
489 #[document_returns("A new `Lazy` instance containing the value.")]
491 #[document_examples]
493 pub fn pure(a: A) -> Self {
501 Lazy(RcLazyConfig::lazy_new(Box::new(move || a)))
502 }
503 }
504
505 #[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
506 impl<'a, A> From<Thunk<'a, A>> for Lazy<'a, A, RcLazyConfig> {
507 #[document_signature]
508 #[document_parameters("The thunk to convert.")]
509 #[document_returns("A new `Lazy` instance that will evaluate the thunk on first access.")]
510 #[document_examples]
511 fn from(eval: Thunk<'a, A>) -> Self {
519 Self::new(move || eval.evaluate())
520 }
521 }
522
523 #[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
524 impl<'a, A> From<Trampoline<A>> for Lazy<'a, A, RcLazyConfig>
525 where
526 A: Send,
527 {
528 #[document_signature]
529 #[document_parameters("The trampoline to convert.")]
530 #[document_returns(
531 "A new `Lazy` instance that will evaluate the trampoline on first access."
532 )]
533 #[document_examples]
534 fn from(task: Trampoline<A>) -> Self {
542 Self::new(move || task.evaluate())
543 }
544 }
545
546 #[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
547 impl<'a, A> Lazy<'a, A, ArcLazyConfig>
548 where
549 A: 'a,
550 {
551 #[document_signature]
553 #[document_parameters("The closure that produces the value.")]
555 #[document_returns("A new `Lazy` instance.")]
557 #[document_examples]
559 pub fn new(f: impl FnOnce() -> A + Send + 'a) -> Self {
567 Lazy(ArcLazyConfig::lazy_new(Box::new(f)))
568 }
569
570 #[document_signature]
575 #[document_parameters("The pre-computed value to wrap.")]
577 #[document_returns("A new `Lazy` instance containing the value.")]
579 #[document_examples]
581 pub fn pure(a: A) -> Self
589 where
590 A: Send, {
591 Lazy(ArcLazyConfig::lazy_new(Box::new(move || a)))
592 }
593 }
594
595 pub type RcLazy<'a, A> = Lazy<'a, A, RcLazyConfig>;
597
598 pub type ArcLazy<'a, A> = Lazy<'a, A, ArcLazyConfig>;
600
601 impl_kind! {
602 impl<Config: LazyConfig> for LazyBrand<Config> {
603 type Of<'a, A: 'a>: 'a = Lazy<'a, A, Config>;
604 }
605 }
606
607 #[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
608 impl<'a, A> Deferrable<'a> for Lazy<'a, A, RcLazyConfig>
609 where
610 A: Clone + 'a,
611 {
612 #[document_signature]
617 #[document_parameters("The thunk that produces the lazy value.")]
619 #[document_returns("A new `Lazy` value.")]
621 #[document_examples]
623 fn defer(f: impl FnOnce() -> Self + 'a) -> Self
636 where
637 Self: Sized, {
638 RcLazy::new(move || f().evaluate().clone())
639 }
640 }
641
642 #[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
643 impl<'a, A> SendDeferrable<'a> for Lazy<'a, A, ArcLazyConfig>
644 where
645 A: Clone + Send + Sync + 'a,
646 {
647 #[document_signature]
652 #[document_parameters("The thunk that produces the lazy value.")]
654 #[document_returns("A new `ArcLazy` value.")]
656 #[document_examples]
658 fn send_defer(f: impl FnOnce() -> Self + Send + Sync + 'a) -> Self
670 where
671 Self: Sized, {
672 ArcLazy::new(move || f().evaluate().clone())
673 }
674 }
675
676 impl RefFunctor for LazyBrand<RcLazyConfig> {
677 #[document_signature]
679 #[document_type_parameters(
681 "The lifetime of the values.",
682 "The type of the value.",
683 "The type of the result."
684 )]
685 #[document_parameters("The function to apply.", "The memoized value.")]
687 #[document_returns("A new memoized value.")]
689 #[document_examples]
691 fn ref_map<'a, A: 'a, B: 'a>(
704 f: impl FnOnce(&A) -> B + 'a,
705 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
706 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
707 let fa = fa.clone();
708 let init: Box<dyn FnOnce() -> B + 'a> = Box::new(move || f(fa.evaluate()));
709 Lazy(RcLazyConfig::lazy_new(init))
710 }
711 }
712}
713
714pub use inner::*;
715
716#[cfg(test)]
717mod tests {
718 use {
719 super::inner::*,
720 crate::types::{
721 Thunk,
722 Trampoline,
723 },
724 std::{
725 cell::RefCell,
726 rc::Rc,
727 sync::Arc,
728 },
729 };
730
731 #[test]
735 fn test_memo_caching() {
736 let counter = Rc::new(RefCell::new(0));
737 let counter_clone = counter.clone();
738 let memo = RcLazy::new(move || {
739 *counter_clone.borrow_mut() += 1;
740 42
741 });
742
743 assert_eq!(*counter.borrow(), 0);
744 assert_eq!(*memo.evaluate(), 42);
745 assert_eq!(*counter.borrow(), 1);
746 assert_eq!(*memo.evaluate(), 42);
747 assert_eq!(*counter.borrow(), 1);
748 }
749
750 #[test]
754 fn test_memo_sharing() {
755 let counter = Rc::new(RefCell::new(0));
756 let counter_clone = counter.clone();
757 let memo = RcLazy::new(move || {
758 *counter_clone.borrow_mut() += 1;
759 42
760 });
761 let shared = memo.clone();
762
763 assert_eq!(*memo.evaluate(), 42);
764 assert_eq!(*counter.borrow(), 1);
765 assert_eq!(*shared.evaluate(), 42);
766 assert_eq!(*counter.borrow(), 1);
767 }
768
769 #[test]
773 fn test_arc_memo_thread_safety() {
774 use std::{
775 sync::atomic::{
776 AtomicUsize,
777 Ordering,
778 },
779 thread,
780 };
781
782 let counter = Arc::new(AtomicUsize::new(0));
783 let counter_clone = counter.clone();
784 let memo = ArcLazy::new(move || {
785 counter_clone.fetch_add(1, Ordering::SeqCst);
786 42
787 });
788
789 let mut handles = vec![];
790 for _ in 0 .. 10 {
791 let memo_clone = memo.clone();
792 handles.push(thread::spawn(move || {
793 assert_eq!(*memo_clone.evaluate(), 42);
794 }));
795 }
796
797 for handle in handles {
798 handle.join().unwrap();
799 }
800
801 assert_eq!(counter.load(Ordering::SeqCst), 1);
802 }
803
804 #[test]
808 fn test_memo_from_eval() {
809 let eval = Thunk::new(|| 42);
810 let memo = RcLazy::from(eval);
811 assert_eq!(*memo.evaluate(), 42);
812 }
813
814 #[test]
818 fn test_memo_from_task() {
819 let task = Trampoline::pure(42);
821 let memo = RcLazy::from(task);
822 assert_eq!(*memo.evaluate(), 42);
823 }
824
825 #[test]
827 fn test_defer() {
828 use crate::classes::deferrable::defer;
829
830 let memo: RcLazy<i32> = defer(|| RcLazy::new(|| 42));
831 assert_eq!(*memo.evaluate(), 42);
832 }
833
834 #[test]
836 fn test_send_defer() {
837 use crate::classes::send_deferrable::send_defer;
838
839 let memo: ArcLazy<i32> = send_defer(|| ArcLazy::new(|| 42));
840 assert_eq!(*memo.evaluate(), 42);
841 }
842
843 #[test]
847 fn test_rc_lazy_pure() {
848 let lazy = RcLazy::pure(42);
849 assert_eq!(*lazy.evaluate(), 42);
850
851 let shared = lazy.clone();
853 assert_eq!(*shared.evaluate(), 42);
854 }
855
856 #[test]
860 fn test_arc_lazy_pure() {
861 let lazy = ArcLazy::pure(42);
862 assert_eq!(*lazy.evaluate(), 42);
863
864 let shared = lazy.clone();
866 assert_eq!(*shared.evaluate(), 42);
867 }
868
869 #[test]
873 fn test_arc_lazy_pure_thread_safety() {
874 use std::thread;
875
876 let lazy = ArcLazy::pure(42);
877
878 let mut handles = vec![];
879 for _ in 0 .. 10 {
880 let lazy_clone = lazy.clone();
881 handles.push(thread::spawn(move || {
882 assert_eq!(*lazy_clone.evaluate(), 42);
883 }));
884 }
885
886 for handle in handles {
887 handle.join().unwrap();
888 }
889 }
890}