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 pub struct Lazy<'a, A, Config: LazyConfig = RcLazyConfig>(
395 pub(crate) Config::Lazy<'a, A>,
397 )
398 where
399 A: 'a;
400
401 #[document_type_parameters(
402 "The lifetime of the reference.",
403 "The type of the computed value.",
404 "The memoization configuration (determines Rc vs Arc)."
405 )]
406 #[document_parameters("The instance to clone.")]
407 impl<'a, A, Config: LazyConfig> Clone for Lazy<'a, A, Config>
408 where
409 A: 'a,
410 {
411 #[document_signature]
412 #[document_returns("A new `Lazy` instance that shares the same underlying memoized value.")]
413 #[document_examples]
414 fn clone(&self) -> Self {
428 Self(self.0.clone())
429 }
430 }
431
432 #[document_type_parameters(
433 "The lifetime of the reference.",
434 "The type of the computed value.",
435 "The memoization configuration (determines Rc vs Arc)."
436 )]
437 #[document_parameters("The lazy instance.")]
438 impl<'a, A, Config: LazyConfig> Lazy<'a, A, Config>
439 where
440 A: 'a,
441 {
442 #[document_signature]
444 #[document_returns("A reference to the memoized value.")]
446 #[document_examples]
448 pub fn evaluate(&self) -> &A {
456 Config::evaluate(&self.0)
457 }
458 }
459
460 #[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
461 #[document_parameters("The lazy instance.")]
462 impl<'a, A> Lazy<'a, A, RcLazyConfig>
463 where
464 A: 'a,
465 {
466 #[document_signature]
468 #[document_parameters("The closure that produces the value.")]
470 #[document_returns("A new `Lazy` instance.")]
472 #[document_examples]
474 pub fn new(f: impl FnOnce() -> A + 'a) -> Self {
482 Lazy(RcLazyConfig::lazy_new(Box::new(f)))
483 }
484
485 #[document_signature]
489 #[document_parameters("The pre-computed value to wrap.")]
491 #[document_returns("A new `Lazy` instance containing the value.")]
493 #[document_examples]
495 pub fn pure(a: A) -> Self {
503 Lazy(RcLazyConfig::lazy_new(Box::new(move || a)))
504 }
505
506 #[document_signature]
512 #[document_type_parameters("The type of the result.")]
513 #[document_parameters("The function to apply to the memoized value.")]
514 #[document_returns("A new `Lazy` instance containing the mapped value.")]
515 #[document_examples]
516 pub fn ref_map<B: 'a>(
525 self,
526 f: impl FnOnce(&A) -> B + 'a,
527 ) -> Lazy<'a, B, RcLazyConfig> {
528 let fa = self.clone();
529 let init: Box<dyn FnOnce() -> B + 'a> = Box::new(move || f(fa.evaluate()));
530 Lazy(RcLazyConfig::lazy_new(init))
531 }
532 }
533
534 #[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
535 impl<'a, A> From<Thunk<'a, A>> for Lazy<'a, A, RcLazyConfig> {
536 #[document_signature]
537 #[document_parameters("The thunk to convert.")]
538 #[document_returns("A new `Lazy` instance that will evaluate the thunk on first access.")]
539 #[document_examples]
540 fn from(eval: Thunk<'a, A>) -> Self {
548 Self::new(move || eval.evaluate())
549 }
550 }
551
552 #[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
553 impl<'a, A> From<Trampoline<A>> for Lazy<'a, A, RcLazyConfig>
554 where
555 A: Send,
556 {
557 #[document_signature]
558 #[document_parameters("The trampoline to convert.")]
559 #[document_returns(
560 "A new `Lazy` instance that will evaluate the trampoline on first access."
561 )]
562 #[document_examples]
563 fn from(task: Trampoline<A>) -> Self {
571 Self::new(move || task.evaluate())
572 }
573 }
574
575 #[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
576 impl<'a, A> Lazy<'a, A, ArcLazyConfig>
577 where
578 A: 'a,
579 {
580 #[document_signature]
582 #[document_parameters("The closure that produces the value.")]
584 #[document_returns("A new `Lazy` instance.")]
586 #[document_examples]
588 pub fn new(f: impl FnOnce() -> A + Send + 'a) -> Self {
596 Lazy(ArcLazyConfig::lazy_new(Box::new(f)))
597 }
598
599 #[document_signature]
604 #[document_parameters("The pre-computed value to wrap.")]
606 #[document_returns("A new `Lazy` instance containing the value.")]
608 #[document_examples]
610 pub fn pure(a: A) -> Self
618 where
619 A: Send, {
620 Lazy(ArcLazyConfig::lazy_new(Box::new(move || a)))
621 }
622 }
623
624 pub type RcLazy<'a, A> = Lazy<'a, A, RcLazyConfig>;
626
627 pub type ArcLazy<'a, A> = Lazy<'a, A, ArcLazyConfig>;
629
630 impl_kind! {
631 impl<Config: LazyConfig> for LazyBrand<Config> {
632 type Of<'a, A: 'a>: 'a = Lazy<'a, A, Config>;
633 }
634 }
635
636 #[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
637 impl<'a, A> Deferrable<'a> for Lazy<'a, A, RcLazyConfig>
638 where
639 A: Clone + 'a,
640 {
641 #[document_signature]
646 #[document_parameters("The thunk that produces the lazy value.")]
648 #[document_returns("A new `Lazy` value.")]
650 #[document_examples]
652 fn defer(f: impl FnOnce() -> Self + 'a) -> Self
665 where
666 Self: Sized, {
667 RcLazy::new(move || f().evaluate().clone())
668 }
669 }
670
671 #[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
672 impl<'a, A> SendDeferrable<'a> for Lazy<'a, A, ArcLazyConfig>
673 where
674 A: Clone + Send + Sync + 'a,
675 {
676 #[document_signature]
681 #[document_parameters("The thunk that produces the lazy value.")]
683 #[document_returns("A new `ArcLazy` value.")]
685 #[document_examples]
687 fn send_defer(f: impl FnOnce() -> Self + Send + Sync + 'a) -> Self
699 where
700 Self: Sized, {
701 ArcLazy::new(move || f().evaluate().clone())
702 }
703 }
704
705 impl RefFunctor for LazyBrand<RcLazyConfig> {
706 #[document_signature]
708 #[document_type_parameters(
710 "The lifetime of the values.",
711 "The type of the value.",
712 "The type of the result."
713 )]
714 #[document_parameters("The function to apply.", "The memoized value.")]
716 #[document_returns("A new memoized value.")]
718 #[document_examples]
720 fn ref_map<'a, A: 'a, B: 'a>(
733 f: impl FnOnce(&A) -> B + 'a,
734 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
735 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
736 fa.ref_map(f)
737 }
738 }
739}
740
741pub use inner::*;
742
743#[cfg(test)]
744mod tests {
745 use {
746 super::inner::*,
747 crate::types::{
748 Thunk,
749 Trampoline,
750 },
751 quickcheck_macros::quickcheck,
752 std::{
753 cell::RefCell,
754 rc::Rc,
755 sync::Arc,
756 },
757 };
758
759 #[test]
763 fn test_memo_caching() {
764 let counter = Rc::new(RefCell::new(0));
765 let counter_clone = counter.clone();
766 let memo = RcLazy::new(move || {
767 *counter_clone.borrow_mut() += 1;
768 42
769 });
770
771 assert_eq!(*counter.borrow(), 0);
772 assert_eq!(*memo.evaluate(), 42);
773 assert_eq!(*counter.borrow(), 1);
774 assert_eq!(*memo.evaluate(), 42);
775 assert_eq!(*counter.borrow(), 1);
776 }
777
778 #[test]
782 fn test_memo_sharing() {
783 let counter = Rc::new(RefCell::new(0));
784 let counter_clone = counter.clone();
785 let memo = RcLazy::new(move || {
786 *counter_clone.borrow_mut() += 1;
787 42
788 });
789 let shared = memo.clone();
790
791 assert_eq!(*memo.evaluate(), 42);
792 assert_eq!(*counter.borrow(), 1);
793 assert_eq!(*shared.evaluate(), 42);
794 assert_eq!(*counter.borrow(), 1);
795 }
796
797 #[test]
801 fn test_arc_memo_thread_safety() {
802 use std::{
803 sync::atomic::{
804 AtomicUsize,
805 Ordering,
806 },
807 thread,
808 };
809
810 let counter = Arc::new(AtomicUsize::new(0));
811 let counter_clone = counter.clone();
812 let memo = ArcLazy::new(move || {
813 counter_clone.fetch_add(1, Ordering::SeqCst);
814 42
815 });
816
817 let mut handles = vec![];
818 for _ in 0 .. 10 {
819 let memo_clone = memo.clone();
820 handles.push(thread::spawn(move || {
821 assert_eq!(*memo_clone.evaluate(), 42);
822 }));
823 }
824
825 for handle in handles {
826 handle.join().unwrap();
827 }
828
829 assert_eq!(counter.load(Ordering::SeqCst), 1);
830 }
831
832 #[test]
836 fn test_memo_from_eval() {
837 let eval = Thunk::new(|| 42);
838 let memo = RcLazy::from(eval);
839 assert_eq!(*memo.evaluate(), 42);
840 }
841
842 #[test]
846 fn test_memo_from_task() {
847 let task = Trampoline::pure(42);
849 let memo = RcLazy::from(task);
850 assert_eq!(*memo.evaluate(), 42);
851 }
852
853 #[test]
855 fn test_defer() {
856 use crate::classes::deferrable::defer;
857
858 let memo: RcLazy<i32> = defer(|| RcLazy::new(|| 42));
859 assert_eq!(*memo.evaluate(), 42);
860 }
861
862 #[test]
864 fn test_send_defer() {
865 use crate::classes::send_deferrable::send_defer;
866
867 let memo: ArcLazy<i32> = send_defer(|| ArcLazy::new(|| 42));
868 assert_eq!(*memo.evaluate(), 42);
869 }
870
871 #[test]
875 fn test_rc_lazy_pure() {
876 let lazy = RcLazy::pure(42);
877 assert_eq!(*lazy.evaluate(), 42);
878
879 let shared = lazy.clone();
881 assert_eq!(*shared.evaluate(), 42);
882 }
883
884 #[test]
888 fn test_arc_lazy_pure() {
889 let lazy = ArcLazy::pure(42);
890 assert_eq!(*lazy.evaluate(), 42);
891
892 let shared = lazy.clone();
894 assert_eq!(*shared.evaluate(), 42);
895 }
896
897 #[test]
901 fn test_arc_lazy_pure_thread_safety() {
902 use std::thread;
903
904 let lazy = ArcLazy::pure(42);
905
906 let mut handles = vec![];
907 for _ in 0 .. 10 {
908 let lazy_clone = lazy.clone();
909 handles.push(thread::spawn(move || {
910 assert_eq!(*lazy_clone.evaluate(), 42);
911 }));
912 }
913
914 for handle in handles {
915 handle.join().unwrap();
916 }
917 }
918
919 #[quickcheck]
923 fn prop_rc_memo_get_memoization(x: i32) -> bool {
924 let memo = RcLazy::new(move || x.wrapping_mul(2));
925 let result1 = *memo.evaluate();
926 let result2 = *memo.evaluate();
927 result1 == result2
928 }
929
930 #[quickcheck]
932 fn prop_arc_memo_get_memoization(x: i32) -> bool {
933 let memo = ArcLazy::new(move || x.wrapping_mul(2));
934 let result1 = *memo.evaluate();
935 let result2 = *memo.evaluate();
936 result1 == result2
937 }
938
939 #[quickcheck]
943 fn prop_rc_memo_clone_shares_state(x: i32) -> bool {
944 let memo1 = RcLazy::new(move || x);
945 let memo2 = memo1.clone();
946
947 let result1 = *memo1.evaluate();
948 let result2 = *memo2.evaluate();
949 result1 == result2
950 }
951
952 #[quickcheck]
954 fn prop_arc_memo_clone_shares_state(x: i32) -> bool {
955 let memo1 = ArcLazy::new(move || x);
956 let memo2 = memo1.clone();
957
958 let result1 = *memo1.evaluate();
959 let result2 = *memo2.evaluate();
960 result1 == result2
961 }
962
963 #[quickcheck]
965 fn prop_memo_get_original_then_clone(x: String) -> bool {
966 let value = x.clone();
967 let memo = RcLazy::new(move || value.clone());
968 let memo_clone = memo.clone();
969
970 let result1 = memo.evaluate().clone();
971 let result2 = memo_clone.evaluate().clone();
972
973 result1 == result2
974 }
975
976 #[quickcheck]
980 fn prop_memo_deterministic(
981 x: i32,
982 y: i32,
983 ) -> bool {
984 let memo1 = RcLazy::new(move || x.wrapping_add(y));
985 let memo2 = RcLazy::new(move || x.wrapping_add(y));
986
987 *memo1.evaluate() == *memo2.evaluate()
988 }
989}