1#[fp_macros::document_module]
6mod inner {
7 use {
8 crate::{
9 Apply,
10 brands::ThunkBrand,
11 classes::{
12 ApplyFirst,
13 ApplySecond,
14 CloneableFn,
15 Deferrable,
16 Evaluable,
17 Foldable,
18 Functor,
19 Lift,
20 MonadRec,
21 Monoid,
22 Pointed,
23 Semiapplicative,
24 Semigroup,
25 Semimonad,
26 },
27 impl_kind,
28 kinds::*,
29 types::{
30 Lazy,
31 LazyConfig,
32 Step,
33 },
34 },
35 fp_macros::*,
36 };
37
38 #[document_type_parameters(
79 "The lifetime of the computation.",
80 "The type of the value produced by the computation."
81 )]
82 pub struct Thunk<'a, A>(
84 Box<dyn FnOnce() -> A + 'a>,
86 );
87
88 #[document_type_parameters(
89 "The lifetime of the computation.",
90 "The type of the value produced by the computation."
91 )]
92 #[document_parameters("The thunk instance.")]
93 impl<'a, A: 'a> Thunk<'a, A> {
94 #[document_signature]
96 #[document_parameters("The thunk to wrap.")]
98 #[document_returns("A new `Thunk` instance.")]
100 #[document_examples]
102 pub fn new(f: impl FnOnce() -> A + 'a) -> Self {
110 Thunk(Box::new(f))
111 }
112
113 #[document_signature]
115 #[document_parameters("The value to wrap.")]
117 #[document_returns("A new `Thunk` instance containing the value.")]
119 #[document_examples]
121 pub fn pure(a: A) -> Self
133 where
134 A: 'a, {
135 Thunk::new(move || a)
136 }
137
138 #[document_signature]
140 #[document_parameters("The thunk that returns a `Thunk`.")]
142 #[document_returns("A new `Thunk` instance.")]
144 #[document_examples]
146 pub fn defer(f: impl FnOnce() -> Thunk<'a, A> + 'a) -> Self {
158 Thunk::new(move || f().evaluate())
159 }
160
161 #[document_signature]
166 #[document_type_parameters("The type of the result of the new computation.")]
168 #[document_parameters("The function to apply to the result of the computation.")]
170 #[document_returns("A new `Thunk` instance representing the chained computation.")]
172 #[document_examples]
174 pub fn bind<B: 'a>(
185 self,
186 f: impl FnOnce(A) -> Thunk<'a, B> + 'a,
187 ) -> Thunk<'a, B> {
188 Thunk::new(move || {
189 let a = (self.0)();
190 let thunk_b = f(a);
191 (thunk_b.0)()
192 })
193 }
194
195 #[document_signature]
197 #[document_type_parameters("The type of the result of the transformation.")]
199 #[document_parameters("The function to apply to the result of the computation.")]
201 #[document_returns("A new `Thunk` instance with the transformed result.")]
203 #[document_examples]
205 pub fn map<B: 'a>(
216 self,
217 f: impl FnOnce(A) -> B + 'a,
218 ) -> Thunk<'a, B> {
219 Thunk::new(move || f((self.0)()))
220 }
221
222 #[document_signature]
224 #[document_returns("The result of the computation.")]
226 #[document_examples]
228 pub fn evaluate(self) -> A {
239 (self.0)()
240 }
241 }
242
243 #[document_type_parameters(
244 "The lifetime of the computation.",
245 "The type of the value produced by the computation.",
246 "The memoization configuration."
247 )]
248 impl<'a, A, Config> From<Lazy<'a, A, Config>> for Thunk<'a, A>
249 where
250 A: Clone + 'a,
251 Config: LazyConfig,
252 {
253 #[document_signature]
254 #[document_parameters("The lazy value to convert.")]
255 #[document_returns("A thunk that evaluates the lazy value.")]
256 #[document_examples]
257 fn from(lazy: Lazy<'a, A, Config>) -> Self {
265 Thunk::new(move || lazy.evaluate().clone())
266 }
267 }
268
269 impl_kind! {
270 for ThunkBrand {
271 type Of<'a, A: 'a>: 'a = Thunk<'a, A>;
272 }
273 }
274
275 #[document_type_parameters(
276 "The lifetime of the computation.",
277 "The type of the value produced by the computation."
278 )]
279 impl<'a, A: 'a> Deferrable<'a> for Thunk<'a, A> {
280 #[document_signature]
282 #[document_parameters("A thunk that produces the thunk.")]
284 #[document_returns("The deferred thunk.")]
286 #[document_examples]
288 fn defer(f: impl FnOnce() -> Self + 'a) -> Self
301 where
302 Self: Sized, {
303 Thunk::defer(f)
304 }
305 }
306
307 impl Functor for ThunkBrand {
308 #[document_signature]
310 #[document_type_parameters(
312 "The lifetime of the computation.",
313 "The type of the value inside the `Thunk`.",
314 "The type of the result of the transformation."
315 )]
316 #[document_parameters(
318 "The function to apply to the result of the computation.",
319 "The `Thunk` instance."
320 )]
321 #[document_returns("A new `Thunk` instance with the transformed result.")]
323 #[document_examples]
324 fn map<'a, A: 'a, B: 'a>(
336 func: impl Fn(A) -> B + 'a,
337 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
338 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
339 fa.map(func)
340 }
341 }
342
343 impl Pointed for ThunkBrand {
344 #[document_signature]
346 #[document_type_parameters(
348 "The lifetime of the computation.",
349 "The type of the value to wrap."
350 )]
351 #[document_parameters("The value to wrap.")]
353 #[document_returns("A new `Thunk` instance containing the value.")]
355 #[document_examples]
357 fn pure<'a, A: 'a>(a: A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>) {
369 Thunk::pure(a)
370 }
371 }
372
373 impl Lift for ThunkBrand {
374 #[document_signature]
376 #[document_type_parameters(
378 "The lifetime of the computation.",
379 "The type of the first value.",
380 "The type of the second value.",
381 "The type of the result."
382 )]
383 #[document_parameters(
385 "The binary function to apply.",
386 "The first `Thunk`.",
387 "The second `Thunk`."
388 )]
389 #[document_returns(
391 "A new `Thunk` instance containing the result of applying the function."
392 )]
393 #[document_examples]
394 fn lift2<'a, A, B, C>(
407 func: impl Fn(A, B) -> C + 'a,
408 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
409 fb: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
410 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>)
411 where
412 A: Clone + 'a,
413 B: Clone + 'a,
414 C: 'a, {
415 fa.bind(move |a| fb.map(move |b| func(a, b)))
416 }
417 }
418
419 impl ApplyFirst for ThunkBrand {}
420 impl ApplySecond for ThunkBrand {}
421
422 impl Semiapplicative for ThunkBrand {
423 #[document_signature]
425 #[document_type_parameters(
427 "The lifetime of the computation.",
428 "The brand of the cloneable function wrapper.",
429 "The type of the input.",
430 "The type of the result."
431 )]
432 #[document_parameters(
434 "The `Thunk` containing the function.",
435 "The `Thunk` containing the value."
436 )]
437 #[document_returns(
439 "A new `Thunk` instance containing the result of applying the function."
440 )]
441 #[document_examples]
442 fn apply<'a, FnBrand: 'a + CloneableFn, A: 'a + Clone, B: 'a>(
455 ff: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as CloneableFn>::Of<'a, A, B>>),
456 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
457 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
458 ff.bind(move |f| {
459 fa.map(
460 #[allow(clippy::redundant_closure)] move |a| f(a),
462 )
463 })
464 }
465 }
466
467 impl Semimonad for ThunkBrand {
468 #[document_signature]
470 #[document_type_parameters(
472 "The lifetime of the computation.",
473 "The type of the result of the first computation.",
474 "The type of the result of the new computation."
475 )]
476 #[document_parameters(
478 "The first `Thunk`.",
479 "The function to apply to the result of the computation."
480 )]
481 #[document_returns("A new `Thunk` instance representing the chained computation.")]
483 #[document_examples]
484 fn bind<'a, A: 'a, B: 'a>(
496 ma: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
497 func: impl Fn(A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) + 'a,
498 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
499 ma.bind(func)
500 }
501 }
502
503 impl MonadRec for ThunkBrand {
504 #[document_signature]
506 #[document_type_parameters(
508 "The lifetime of the computation.",
509 "The type of the initial value and loop state.",
510 "The type of the result."
511 )]
512 #[document_parameters("The step function.", "The initial value.")]
514 #[document_returns("The result of the computation.")]
516 #[document_examples]
518 fn tail_rec_m<'a, A: 'a, B: 'a>(
534 f: impl Fn(A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Step<A, B>>)
535 + Clone
536 + 'a,
537 a: A,
538 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
539 Thunk::new(move || {
540 let mut current = a;
541 loop {
542 match f(current).evaluate() {
543 Step::Loop(next) => current = next,
544 Step::Done(res) => break res,
545 }
546 }
547 })
548 }
549 }
550
551 impl Evaluable for ThunkBrand {
552 #[document_signature]
554 #[document_type_parameters(
556 "The lifetime of the computation.",
557 "The type of the value inside the thunk."
558 )]
559 #[document_parameters("The eval to run.")]
561 #[document_returns("The result of running the thunk.")]
563 #[document_examples]
565 fn evaluate<'a, A: 'a>(
578 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
579 ) -> A {
580 fa.evaluate()
581 }
582 }
583
584 impl Foldable for ThunkBrand {
585 #[document_signature]
587 #[document_type_parameters(
589 "The lifetime of the computation.",
590 "The brand of the cloneable function to use.",
591 "The type of the elements in the structure.",
592 "The type of the accumulator."
593 )]
594 #[document_parameters(
596 "The function to apply to each element and the accumulator.",
597 "The initial value of the accumulator.",
598 "The `Thunk` to fold."
599 )]
600 #[document_returns("The final accumulator value.")]
602 #[document_examples]
603 fn fold_right<'a, FnBrand, A: 'a + Clone, B: 'a>(
615 func: impl Fn(A, B) -> B + 'a,
616 initial: B,
617 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
618 ) -> B
619 where
620 FnBrand: CloneableFn + 'a, {
621 func(fa.evaluate(), initial)
622 }
623
624 #[document_signature]
626 #[document_type_parameters(
628 "The lifetime of the computation.",
629 "The brand of the cloneable function to use.",
630 "The type of the elements in the structure.",
631 "The type of the accumulator."
632 )]
633 #[document_parameters(
635 "The function to apply to the accumulator and each element.",
636 "The initial value of the accumulator.",
637 "The `Thunk` to fold."
638 )]
639 #[document_returns("The final accumulator value.")]
641 #[document_examples]
642 fn fold_left<'a, FnBrand, A: 'a + Clone, B: 'a>(
654 func: impl Fn(B, A) -> B + 'a,
655 initial: B,
656 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
657 ) -> B
658 where
659 FnBrand: CloneableFn + 'a, {
660 func(initial, fa.evaluate())
661 }
662
663 #[document_signature]
665 #[document_type_parameters(
667 "The lifetime of the computation.",
668 "The brand of the cloneable function to use.",
669 "The type of the elements in the structure.",
670 "The type of the monoid."
671 )]
672 #[document_parameters("The mapping function.", "The Thunk to fold.")]
674 #[document_returns("The monoid value.")]
676 #[document_examples]
678 fn fold_map<'a, FnBrand, A: 'a + Clone, M>(
690 func: impl Fn(A) -> M + 'a,
691 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
692 ) -> M
693 where
694 M: Monoid + 'a,
695 FnBrand: CloneableFn + 'a, {
696 func(fa.evaluate())
697 }
698 }
699
700 #[document_type_parameters(
701 "The lifetime of the computation.",
702 "The type of the value produced by the computation."
703 )]
704 impl<'a, A: Semigroup + 'a> Semigroup for Thunk<'a, A> {
705 #[document_signature]
707 #[document_parameters("The first `Thunk`.", "The second `Thunk`.")]
709 #[document_returns("A new `Thunk` containing the combined result.")]
711 #[document_examples]
713 fn append(
727 a: Self,
728 b: Self,
729 ) -> Self {
730 Thunk::new(move || Semigroup::append(a.evaluate(), b.evaluate()))
731 }
732 }
733
734 #[document_type_parameters(
735 "The lifetime of the computation.",
736 "The type of the value produced by the computation."
737 )]
738 impl<'a, A: Monoid + 'a> Monoid for Thunk<'a, A> {
739 #[document_signature]
741 #[document_returns("A `Thunk` producing the identity value of `A`.")]
743 #[document_examples]
745 fn empty() -> Self {
756 Thunk::new(|| Monoid::empty())
757 }
758 }
759}
760pub use inner::*;
761
762#[cfg(test)]
763mod tests {
764 use super::*;
765
766 #[test]
770 fn test_basic_execution() {
771 let thunk = Thunk::new(|| 42);
772 assert_eq!(thunk.evaluate(), 42);
773 }
774
775 #[test]
779 fn test_pure() {
780 let thunk = Thunk::pure(42);
781 assert_eq!(thunk.evaluate(), 42);
782 }
783
784 #[test]
788 fn test_borrowing() {
789 let x = 42;
790 let thunk = Thunk::new(|| &x);
791 assert_eq!(thunk.evaluate(), &42);
792 }
793
794 #[test]
798 fn test_map() {
799 let thunk = Thunk::pure(21).map(|x| x * 2);
800 assert_eq!(thunk.evaluate(), 42);
801 }
802
803 #[test]
807 fn test_bind() {
808 let thunk = Thunk::pure(21).bind(|x| Thunk::pure(x * 2));
809 assert_eq!(thunk.evaluate(), 42);
810 }
811
812 #[test]
816 fn test_defer() {
817 let thunk = Thunk::defer(|| Thunk::pure(42));
818 assert_eq!(thunk.evaluate(), 42);
819 }
820
821 #[test]
823 fn test_eval_from_memo() {
824 use crate::types::RcLazy;
825 let memo = RcLazy::new(|| 42);
826 let thunk = Thunk::from(memo);
827 assert_eq!(thunk.evaluate(), 42);
828 }
829
830 #[test]
834 fn test_eval_semigroup() {
835 use crate::{
836 brands::*,
837 classes::semigroup::append,
838 functions::*,
839 };
840 let t1 = pure::<ThunkBrand, _>("Hello".to_string());
841 let t2 = pure::<ThunkBrand, _>(" World".to_string());
842 let t3 = append(t1, t2);
843 assert_eq!(t3.evaluate(), "Hello World");
844 }
845
846 #[test]
850 fn test_eval_monoid() {
851 use crate::classes::monoid::empty;
852 let t: Thunk<String> = empty();
853 assert_eq!(t.evaluate(), "");
854 }
855}