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 #[document_parameters("The lazy instance.")]
461 impl<'a, A> Lazy<'a, A, RcLazyConfig>
462 where
463 A: 'a,
464 {
465 #[document_signature]
467 #[document_parameters("The closure that produces the value.")]
469 #[document_returns("A new `Lazy` instance.")]
471 #[document_examples]
473 pub fn new(f: impl FnOnce() -> A + 'a) -> Self {
481 Lazy(RcLazyConfig::lazy_new(Box::new(f)))
482 }
483
484 #[document_signature]
488 #[document_parameters("The pre-computed value to wrap.")]
490 #[document_returns("A new `Lazy` instance containing the value.")]
492 #[document_examples]
494 pub fn pure(a: A) -> Self {
502 Lazy(RcLazyConfig::lazy_new(Box::new(move || a)))
503 }
504
505 #[document_signature]
511 #[document_type_parameters("The type of the result.")]
512 #[document_parameters("The function to apply to the memoized value.")]
513 #[document_returns("A new `Lazy` instance containing the mapped value.")]
514 #[document_examples]
515 pub fn ref_map<B: 'a>(
524 self,
525 f: impl FnOnce(&A) -> B + 'a,
526 ) -> Lazy<'a, B, RcLazyConfig> {
527 let fa = self.clone();
528 let init: Box<dyn FnOnce() -> B + 'a> = Box::new(move || f(fa.evaluate()));
529 Lazy(RcLazyConfig::lazy_new(init))
530 }
531 }
532
533 #[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
534 impl<'a, A> From<Thunk<'a, A>> for Lazy<'a, A, RcLazyConfig> {
535 #[document_signature]
536 #[document_parameters("The thunk to convert.")]
537 #[document_returns("A new `Lazy` instance that will evaluate the thunk on first access.")]
538 #[document_examples]
539 fn from(eval: Thunk<'a, A>) -> Self {
547 Self::new(move || eval.evaluate())
548 }
549 }
550
551 #[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
552 impl<'a, A> From<Trampoline<A>> for Lazy<'a, A, RcLazyConfig>
553 where
554 A: Send,
555 {
556 #[document_signature]
557 #[document_parameters("The trampoline to convert.")]
558 #[document_returns(
559 "A new `Lazy` instance that will evaluate the trampoline on first access."
560 )]
561 #[document_examples]
562 fn from(task: Trampoline<A>) -> Self {
570 Self::new(move || task.evaluate())
571 }
572 }
573
574 #[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
575 impl<'a, A> Lazy<'a, A, ArcLazyConfig>
576 where
577 A: 'a,
578 {
579 #[document_signature]
581 #[document_parameters("The closure that produces the value.")]
583 #[document_returns("A new `Lazy` instance.")]
585 #[document_examples]
587 pub fn new(f: impl FnOnce() -> A + Send + 'a) -> Self {
595 Lazy(ArcLazyConfig::lazy_new(Box::new(f)))
596 }
597
598 #[document_signature]
603 #[document_parameters("The pre-computed value to wrap.")]
605 #[document_returns("A new `Lazy` instance containing the value.")]
607 #[document_examples]
609 pub fn pure(a: A) -> Self
617 where
618 A: Send, {
619 Lazy(ArcLazyConfig::lazy_new(Box::new(move || a)))
620 }
621 }
622
623 pub type RcLazy<'a, A> = Lazy<'a, A, RcLazyConfig>;
625
626 pub type ArcLazy<'a, A> = Lazy<'a, A, ArcLazyConfig>;
628
629 impl_kind! {
630 impl<Config: LazyConfig> for LazyBrand<Config> {
631 type Of<'a, A: 'a>: 'a = Lazy<'a, A, Config>;
632 }
633 }
634
635 #[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
636 impl<'a, A> Deferrable<'a> for Lazy<'a, A, RcLazyConfig>
637 where
638 A: Clone + 'a,
639 {
640 #[document_signature]
645 #[document_parameters("The thunk that produces the lazy value.")]
647 #[document_returns("A new `Lazy` value.")]
649 #[document_examples]
651 fn defer(f: impl FnOnce() -> Self + 'a) -> Self
664 where
665 Self: Sized, {
666 RcLazy::new(move || f().evaluate().clone())
667 }
668 }
669
670 #[document_type_parameters("The lifetime of the reference.", "The type of the computed value.")]
671 impl<'a, A> SendDeferrable<'a> for Lazy<'a, A, ArcLazyConfig>
672 where
673 A: Clone + Send + Sync + 'a,
674 {
675 #[document_signature]
680 #[document_parameters("The thunk that produces the lazy value.")]
682 #[document_returns("A new `ArcLazy` value.")]
684 #[document_examples]
686 fn send_defer(f: impl FnOnce() -> Self + Send + Sync + 'a) -> Self
698 where
699 Self: Sized, {
700 ArcLazy::new(move || f().evaluate().clone())
701 }
702 }
703
704 impl RefFunctor for LazyBrand<RcLazyConfig> {
705 #[document_signature]
707 #[document_type_parameters(
709 "The lifetime of the values.",
710 "The type of the value.",
711 "The type of the result."
712 )]
713 #[document_parameters("The function to apply.", "The memoized value.")]
715 #[document_returns("A new memoized value.")]
717 #[document_examples]
719 fn ref_map<'a, A: 'a, B: 'a>(
732 f: impl FnOnce(&A) -> B + 'a,
733 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
734 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
735 fa.ref_map(f)
736 }
737 }
738}
739
740pub use inner::*;
741
742#[cfg(test)]
743mod tests {
744 use {
745 super::inner::*,
746 crate::types::{
747 Thunk,
748 Trampoline,
749 },
750 std::{
751 cell::RefCell,
752 rc::Rc,
753 sync::Arc,
754 },
755 };
756
757 #[test]
761 fn test_memo_caching() {
762 let counter = Rc::new(RefCell::new(0));
763 let counter_clone = counter.clone();
764 let memo = RcLazy::new(move || {
765 *counter_clone.borrow_mut() += 1;
766 42
767 });
768
769 assert_eq!(*counter.borrow(), 0);
770 assert_eq!(*memo.evaluate(), 42);
771 assert_eq!(*counter.borrow(), 1);
772 assert_eq!(*memo.evaluate(), 42);
773 assert_eq!(*counter.borrow(), 1);
774 }
775
776 #[test]
780 fn test_memo_sharing() {
781 let counter = Rc::new(RefCell::new(0));
782 let counter_clone = counter.clone();
783 let memo = RcLazy::new(move || {
784 *counter_clone.borrow_mut() += 1;
785 42
786 });
787 let shared = memo.clone();
788
789 assert_eq!(*memo.evaluate(), 42);
790 assert_eq!(*counter.borrow(), 1);
791 assert_eq!(*shared.evaluate(), 42);
792 assert_eq!(*counter.borrow(), 1);
793 }
794
795 #[test]
799 fn test_arc_memo_thread_safety() {
800 use std::{
801 sync::atomic::{
802 AtomicUsize,
803 Ordering,
804 },
805 thread,
806 };
807
808 let counter = Arc::new(AtomicUsize::new(0));
809 let counter_clone = counter.clone();
810 let memo = ArcLazy::new(move || {
811 counter_clone.fetch_add(1, Ordering::SeqCst);
812 42
813 });
814
815 let mut handles = vec![];
816 for _ in 0 .. 10 {
817 let memo_clone = memo.clone();
818 handles.push(thread::spawn(move || {
819 assert_eq!(*memo_clone.evaluate(), 42);
820 }));
821 }
822
823 for handle in handles {
824 handle.join().unwrap();
825 }
826
827 assert_eq!(counter.load(Ordering::SeqCst), 1);
828 }
829
830 #[test]
834 fn test_memo_from_eval() {
835 let eval = Thunk::new(|| 42);
836 let memo = RcLazy::from(eval);
837 assert_eq!(*memo.evaluate(), 42);
838 }
839
840 #[test]
844 fn test_memo_from_task() {
845 let task = Trampoline::pure(42);
847 let memo = RcLazy::from(task);
848 assert_eq!(*memo.evaluate(), 42);
849 }
850
851 #[test]
853 fn test_defer() {
854 use crate::classes::deferrable::defer;
855
856 let memo: RcLazy<i32> = defer(|| RcLazy::new(|| 42));
857 assert_eq!(*memo.evaluate(), 42);
858 }
859
860 #[test]
862 fn test_send_defer() {
863 use crate::classes::send_deferrable::send_defer;
864
865 let memo: ArcLazy<i32> = send_defer(|| ArcLazy::new(|| 42));
866 assert_eq!(*memo.evaluate(), 42);
867 }
868
869 #[test]
873 fn test_rc_lazy_pure() {
874 let lazy = RcLazy::pure(42);
875 assert_eq!(*lazy.evaluate(), 42);
876
877 let shared = lazy.clone();
879 assert_eq!(*shared.evaluate(), 42);
880 }
881
882 #[test]
886 fn test_arc_lazy_pure() {
887 let lazy = ArcLazy::pure(42);
888 assert_eq!(*lazy.evaluate(), 42);
889
890 let shared = lazy.clone();
892 assert_eq!(*shared.evaluate(), 42);
893 }
894
895 #[test]
899 fn test_arc_lazy_pure_thread_safety() {
900 use std::thread;
901
902 let lazy = ArcLazy::pure(42);
903
904 let mut handles = vec![];
905 for _ in 0 .. 10 {
906 let lazy_clone = lazy.clone();
907 handles.push(thread::spawn(move || {
908 assert_eq!(*lazy_clone.evaluate(), 42);
909 }));
910 }
911
912 for handle in handles {
913 handle.join().unwrap();
914 }
915 }
916}