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 Extend,
17 Extract,
18 Foldable,
19 FoldableWithIndex,
20 Functor,
21 FunctorWithIndex,
22 LazyConfig,
23 Lift,
24 MonadRec,
25 Monoid,
26 Pointed,
27 Semiapplicative,
28 Semigroup,
29 Semimonad,
30 WithIndex,
31 },
32 impl_kind,
33 kinds::*,
34 types::{
35 ArcLazyConfig,
36 Lazy,
37 RcLazyConfig,
38 SendThunk,
39 Trampoline,
40 },
41 },
42 core::ops::ControlFlow,
43 fp_macros::*,
44 std::fmt,
45 };
46
47 #[document_type_parameters(
105 "The lifetime of the computation.",
106 "The type of the value produced by the computation."
107 )]
108 pub struct Thunk<'a, A>(
110 Box<dyn FnOnce() -> A + 'a>,
112 );
113
114 #[document_type_parameters(
115 "The lifetime of the computation.",
116 "The type of the value produced by the computation."
117 )]
118 #[document_parameters("The thunk instance.")]
119 impl<'a, A: 'a> Thunk<'a, A> {
120 #[document_signature]
122 #[document_parameters("The thunk to wrap.")]
124 #[document_returns("A new `Thunk` instance.")]
126 #[document_examples]
128 #[inline]
136 pub fn new(f: impl FnOnce() -> A + 'a) -> Self {
137 Thunk(Box::new(f))
138 }
139
140 #[document_signature]
142 #[document_parameters("The value to wrap.")]
144 #[document_returns("A new `Thunk` instance containing the value.")]
146 #[document_examples]
148 #[inline]
160 pub fn pure(a: A) -> Self
161 where
162 A: 'a, {
163 Thunk::new(move || a)
164 }
165
166 #[document_signature]
168 #[document_parameters("The thunk that returns a `Thunk`.")]
170 #[document_returns("A new `Thunk` instance.")]
172 #[document_examples]
174 #[inline]
186 pub fn defer(f: impl FnOnce() -> Thunk<'a, A> + 'a) -> Self {
187 Thunk::new(move || f().evaluate())
188 }
189
190 #[document_signature]
200 #[document_type_parameters("The type of the result of the new computation.")]
202 #[document_parameters("The function to apply to the result of the computation.")]
204 #[document_returns("A new `Thunk` instance representing the chained computation.")]
206 #[document_examples]
208 #[inline]
219 pub fn bind<B: 'a>(
220 self,
221 f: impl FnOnce(A) -> Thunk<'a, B> + 'a,
222 ) -> Thunk<'a, B> {
223 Thunk::new(move || {
224 let a = (self.0)();
225 let thunk_b = f(a);
226 (thunk_b.0)()
227 })
228 }
229
230 #[document_signature]
239 #[document_type_parameters("The type of the result of the transformation.")]
241 #[document_parameters("The function to apply to the result of the computation.")]
243 #[document_returns("A new `Thunk` instance with the transformed result.")]
245 #[document_examples]
247 #[inline]
258 pub fn map<B: 'a>(
259 self,
260 f: impl FnOnce(A) -> B + 'a,
261 ) -> Thunk<'a, B> {
262 Thunk::new(move || f((self.0)()))
263 }
264
265 #[document_signature]
267 #[document_returns("The result of the computation.")]
269 #[document_examples]
271 #[inline]
282 pub fn evaluate(self) -> A {
283 (self.0)()
284 }
285
286 #[document_signature]
291 #[document_returns("A memoized `Lazy` value that evaluates this thunk on first access.")]
293 #[document_examples]
295 #[inline]
304 pub fn into_rc_lazy(self) -> Lazy<'a, A, RcLazyConfig> {
305 Lazy::from(self)
306 }
307
308 #[document_signature]
316 #[document_returns("A thread-safe `ArcLazy` containing the eagerly evaluated result.")]
318 #[document_examples]
320 #[inline]
329 pub fn into_arc_lazy(self) -> Lazy<'a, A, ArcLazyConfig>
330 where
331 A: Send + Sync + 'a, {
332 let val = self.evaluate();
333 Lazy::<'a, A, ArcLazyConfig>::new(move || val)
334 }
335 }
336
337 #[document_type_parameters(
338 "The lifetime of the computation.",
339 "The type of the value produced by the computation.",
340 "The memoization configuration."
341 )]
342 impl<'a, A, Config> From<Lazy<'a, A, Config>> for Thunk<'a, A>
343 where
344 A: Clone + 'a,
345 Config: LazyConfig,
346 {
347 #[document_signature]
352 #[document_parameters("The lazy value to convert.")]
353 #[document_returns("A thunk that evaluates the lazy value.")]
354 #[document_examples]
355 fn from(lazy: Lazy<'a, A, Config>) -> Self {
363 Thunk::new(move || lazy.evaluate().clone())
364 }
365 }
366
367 #[document_type_parameters(
368 "The lifetime of the computation.",
369 "The type of the value produced by the computation."
370 )]
371 impl<'a, A: 'a> From<SendThunk<'a, A>> for Thunk<'a, A> {
372 #[document_signature]
379 #[document_parameters("The send thunk to convert.")]
380 #[document_returns("A `Thunk` wrapping the same deferred computation.")]
381 #[document_examples]
382 fn from(send_thunk: SendThunk<'a, A>) -> Self {
390 Thunk(send_thunk.into_inner())
391 }
392 }
393
394 #[document_type_parameters("The type of the value produced by the computation.")]
395 impl<A: 'static> From<crate::types::Trampoline<A>> for Thunk<'static, A> {
396 #[document_signature]
397 #[document_parameters("The trampoline to convert.")]
398 #[document_returns("A thunk that evaluates the trampoline.")]
399 #[document_examples]
400 fn from(trampoline: crate::types::Trampoline<A>) -> Self {
408 Thunk::new(move || trampoline.evaluate())
409 }
410 }
411
412 #[document_type_parameters("The type of the value produced by the computation.")]
413 impl<A: 'static> From<Thunk<'static, A>> for Trampoline<A> {
414 #[document_signature]
419 #[document_parameters("The thunk to convert.")]
420 #[document_returns("A trampoline that evaluates the thunk.")]
421 #[document_examples]
422 fn from(thunk: Thunk<'static, A>) -> Self {
430 Trampoline::new(move || thunk.evaluate())
431 }
432 }
433
434 impl_kind! {
435 for ThunkBrand {
436 type Of<'a, A: 'a>: 'a = Thunk<'a, A>;
437 }
438 }
439
440 #[document_type_parameters(
441 "The lifetime of the computation.",
442 "The type of the value produced by the computation."
443 )]
444 impl<'a, A: 'a> Deferrable<'a> for Thunk<'a, A> {
445 #[document_signature]
447 #[document_parameters("A thunk that produces the thunk.")]
449 #[document_returns("The deferred thunk.")]
451 #[document_examples]
453 fn defer(f: impl FnOnce() -> Self + 'a) -> Self
466 where
467 Self: Sized, {
468 Thunk::defer(f)
469 }
470 }
471
472 impl Functor for ThunkBrand {
473 #[document_signature]
475 #[document_type_parameters(
477 "The lifetime of the computation.",
478 "The type of the value inside the `Thunk`.",
479 "The type of the result of the transformation."
480 )]
481 #[document_parameters(
483 "The function to apply to the result of the computation.",
484 "The `Thunk` instance."
485 )]
486 #[document_returns("A new `Thunk` instance with the transformed result.")]
488 #[document_examples]
489 fn map<'a, A: 'a, B: 'a>(
501 func: impl Fn(A) -> B + 'a,
502 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
503 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
504 fa.map(func)
505 }
506 }
507
508 impl Pointed for ThunkBrand {
509 #[document_signature]
511 #[document_type_parameters(
513 "The lifetime of the computation.",
514 "The type of the value to wrap."
515 )]
516 #[document_parameters("The value to wrap.")]
518 #[document_returns("A new `Thunk` instance containing the value.")]
520 #[document_examples]
522 fn pure<'a, A: 'a>(a: A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>) {
534 Thunk::pure(a)
535 }
536 }
537
538 impl Lift for ThunkBrand {
539 #[document_signature]
541 #[document_type_parameters(
543 "The lifetime of the computation.",
544 "The type of the first value.",
545 "The type of the second value.",
546 "The type of the result."
547 )]
548 #[document_parameters(
550 "The binary function to apply.",
551 "The first `Thunk`.",
552 "The second `Thunk`."
553 )]
554 #[document_returns(
556 "A new `Thunk` instance containing the result of applying the function."
557 )]
558 #[document_examples]
559 fn lift2<'a, A, B, C>(
572 func: impl Fn(A, B) -> C + 'a,
573 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
574 fb: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
575 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>)
576 where
577 A: Clone + 'a,
578 B: Clone + 'a,
579 C: 'a, {
580 fa.bind(move |a| fb.map(move |b| func(a, b)))
581 }
582 }
583
584 impl ApplyFirst for ThunkBrand {}
585 impl ApplySecond for ThunkBrand {}
586
587 impl Semiapplicative for ThunkBrand {
588 #[document_signature]
590 #[document_type_parameters(
592 "The lifetime of the computation.",
593 "The brand of the cloneable function wrapper.",
594 "The type of the input.",
595 "The type of the result."
596 )]
597 #[document_parameters(
599 "The `Thunk` containing the function.",
600 "The `Thunk` containing the value."
601 )]
602 #[document_returns(
604 "A new `Thunk` instance containing the result of applying the function."
605 )]
606 #[document_examples]
607 fn apply<'a, FnBrand: 'a + CloneableFn, A: 'a + Clone, B: 'a>(
620 ff: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as CloneableFn>::Of<'a, A, B>>),
621 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
622 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
623 ff.bind(move |f| {
624 fa.map(
625 #[allow(clippy::redundant_closure)] move |a| f(a),
627 )
628 })
629 }
630 }
631
632 impl Semimonad for ThunkBrand {
633 #[document_signature]
635 #[document_type_parameters(
637 "The lifetime of the computation.",
638 "The type of the result of the first computation.",
639 "The type of the result of the new computation."
640 )]
641 #[document_parameters(
643 "The first `Thunk`.",
644 "The function to apply to the result of the computation."
645 )]
646 #[document_returns("A new `Thunk` instance representing the chained computation.")]
648 #[document_examples]
649 fn bind<'a, A: 'a, B: 'a>(
661 ma: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
662 func: impl Fn(A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) + 'a,
663 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
664 ma.bind(func)
665 }
666 }
667
668 impl MonadRec for ThunkBrand {
669 #[document_signature]
676 #[document_type_parameters(
678 "The lifetime of the computation.",
679 "The type of the initial value and loop state.",
680 "The type of the result."
681 )]
682 #[document_parameters("The step function.", "The initial value.")]
684 #[document_returns("The result of the computation.")]
686 #[document_examples]
688 fn tail_rec_m<'a, A: 'a, B: 'a>(
711 f: impl Fn(
712 A,
713 )
714 -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, ControlFlow<B, A>>)
715 + 'a,
716 a: A,
717 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
718 Thunk::new(move || {
719 let mut current = a;
720 loop {
721 match f(current).evaluate() {
722 ControlFlow::Continue(next) => current = next,
723 ControlFlow::Break(res) => break res,
724 }
725 }
726 })
727 }
728 }
729
730 impl Extract for ThunkBrand {
731 #[document_signature]
733 #[document_type_parameters(
735 "The lifetime of the computation.",
736 "The type of the value inside the thunk."
737 )]
738 #[document_parameters("The thunk to extract from.")]
740 #[document_returns("The result of running the thunk.")]
742 #[document_examples]
744 fn extract<'a, A: 'a>(
757 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
758 ) -> A {
759 fa.evaluate()
760 }
761 }
762
763 impl Extend for ThunkBrand {
764 #[document_signature]
770 #[document_type_parameters(
772 "The lifetime of the computation.",
773 "The type of the value inside the thunk.",
774 "The result type of the extension function."
775 )]
776 #[document_parameters(
778 "The function that consumes a whole thunk and produces a value.",
779 "The thunk to extend over."
780 )]
781 #[document_returns("A new thunk containing the deferred result of applying the function.")]
783 #[document_examples]
785 fn extend<'a, A: 'a + Clone, B: 'a>(
798 f: impl Fn(Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)) -> B + 'a,
799 wa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
800 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
801 Thunk::new(move || f(wa))
802 }
803 }
804
805 impl Foldable for ThunkBrand {
806 #[document_signature]
808 #[document_type_parameters(
810 "The lifetime of the computation.",
811 "The brand of the cloneable function to use.",
812 "The type of the elements in the structure.",
813 "The type of the accumulator."
814 )]
815 #[document_parameters(
817 "The function to apply to each element and the accumulator.",
818 "The initial value of the accumulator.",
819 "The `Thunk` to fold."
820 )]
821 #[document_returns("The final accumulator value.")]
823 #[document_examples]
824 fn fold_right<'a, FnBrand, A: 'a + Clone, B: 'a>(
836 func: impl Fn(A, B) -> B + 'a,
837 initial: B,
838 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
839 ) -> B
840 where
841 FnBrand: CloneableFn + 'a, {
842 func(fa.evaluate(), initial)
843 }
844
845 #[document_signature]
847 #[document_type_parameters(
849 "The lifetime of the computation.",
850 "The brand of the cloneable function to use.",
851 "The type of the elements in the structure.",
852 "The type of the accumulator."
853 )]
854 #[document_parameters(
856 "The function to apply to the accumulator and each element.",
857 "The initial value of the accumulator.",
858 "The `Thunk` to fold."
859 )]
860 #[document_returns("The final accumulator value.")]
862 #[document_examples]
863 fn fold_left<'a, FnBrand, A: 'a + Clone, B: 'a>(
875 func: impl Fn(B, A) -> B + 'a,
876 initial: B,
877 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
878 ) -> B
879 where
880 FnBrand: CloneableFn + 'a, {
881 func(initial, fa.evaluate())
882 }
883
884 #[document_signature]
886 #[document_type_parameters(
888 "The lifetime of the computation.",
889 "The brand of the cloneable function to use.",
890 "The type of the elements in the structure.",
891 "The type of the monoid."
892 )]
893 #[document_parameters("The mapping function.", "The Thunk to fold.")]
895 #[document_returns("The monoid value.")]
897 #[document_examples]
899 fn fold_map<'a, FnBrand, A: 'a + Clone, M>(
911 func: impl Fn(A) -> M + 'a,
912 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
913 ) -> M
914 where
915 M: Monoid + 'a,
916 FnBrand: CloneableFn + 'a, {
917 func(fa.evaluate())
918 }
919 }
920
921 impl WithIndex for ThunkBrand {
922 type Index = ();
923 }
924
925 impl FunctorWithIndex for ThunkBrand {
926 #[document_signature]
928 #[document_type_parameters(
929 "The lifetime of the computation.",
930 "The type of the value inside the thunk.",
931 "The type of the result of applying the function."
932 )]
933 #[document_parameters(
934 "The function to apply to the value and its index.",
935 "The thunk to map over."
936 )]
937 #[document_returns("A new thunk containing the result of applying the function.")]
938 #[document_examples]
939 fn map_with_index<'a, A: 'a, B: 'a>(
951 f: impl Fn((), A) -> B + 'a,
952 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
953 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
954 fa.map(move |a| f((), a))
955 }
956 }
957
958 impl FoldableWithIndex for ThunkBrand {
959 #[document_signature]
961 #[document_type_parameters(
962 "The lifetime of the computation.",
963 "The type of the value inside the thunk.",
964 "The monoid type."
965 )]
966 #[document_parameters(
967 "The function to apply to the value and its index.",
968 "The thunk to fold."
969 )]
970 #[document_returns("The monoid value.")]
971 #[document_examples]
972 fn fold_map_with_index<'a, A: 'a + Clone, R: Monoid>(
985 f: impl Fn((), A) -> R + 'a,
986 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
987 ) -> R {
988 f((), fa.evaluate())
989 }
990 }
991
992 #[document_type_parameters(
993 "The lifetime of the computation.",
994 "The type of the value produced by the computation."
995 )]
996 impl<'a, A: Semigroup + 'a> Semigroup for Thunk<'a, A> {
997 #[document_signature]
999 #[document_parameters("The first `Thunk`.", "The second `Thunk`.")]
1001 #[document_returns("A new `Thunk` containing the combined result.")]
1003 #[document_examples]
1005 fn append(
1019 a: Self,
1020 b: Self,
1021 ) -> Self {
1022 Thunk::new(move || Semigroup::append(a.evaluate(), b.evaluate()))
1023 }
1024 }
1025
1026 #[document_type_parameters(
1027 "The lifetime of the computation.",
1028 "The type of the value produced by the computation."
1029 )]
1030 impl<'a, A: Monoid + 'a> Monoid for Thunk<'a, A> {
1031 #[document_signature]
1033 #[document_returns("A `Thunk` producing the identity value of `A`.")]
1035 #[document_examples]
1037 fn empty() -> Self {
1048 Thunk::new(|| Monoid::empty())
1049 }
1050 }
1051
1052 #[document_type_parameters(
1053 "The lifetime of the computation.",
1054 "The type of the computed value."
1055 )]
1056 #[document_parameters("The thunk to format.")]
1057 impl<'a, A> fmt::Debug for Thunk<'a, A> {
1058 #[document_signature]
1060 #[document_parameters("The formatter.")]
1061 #[document_returns("The formatting result.")]
1062 #[document_examples]
1063 fn fmt(
1070 &self,
1071 f: &mut fmt::Formatter<'_>,
1072 ) -> fmt::Result {
1073 f.write_str("Thunk(<unevaluated>)")
1074 }
1075 }
1076}
1077pub use inner::*;
1078
1079#[cfg(test)]
1080mod tests {
1081 use {
1082 super::*,
1083 crate::{
1084 brands::*,
1085 classes::{
1086 monoid::empty,
1087 semigroup::append,
1088 },
1089 functions::*,
1090 },
1091 quickcheck_macros::quickcheck,
1092 };
1093
1094 #[test]
1098 fn test_basic_execution() {
1099 let thunk = Thunk::new(|| 42);
1100 assert_eq!(thunk.evaluate(), 42);
1101 }
1102
1103 #[test]
1107 fn test_pure() {
1108 let thunk = Thunk::pure(42);
1109 assert_eq!(thunk.evaluate(), 42);
1110 }
1111
1112 #[test]
1116 fn test_borrowing() {
1117 let x = 42;
1118 let thunk = Thunk::new(|| &x);
1119 assert_eq!(thunk.evaluate(), &42);
1120 }
1121
1122 #[test]
1126 fn test_map() {
1127 let thunk = Thunk::pure(21).map(|x| x * 2);
1128 assert_eq!(thunk.evaluate(), 42);
1129 }
1130
1131 #[test]
1135 fn test_bind() {
1136 let thunk = Thunk::pure(21).bind(|x| Thunk::pure(x * 2));
1137 assert_eq!(thunk.evaluate(), 42);
1138 }
1139
1140 #[test]
1144 fn test_defer() {
1145 let thunk = Thunk::defer(|| Thunk::pure(42));
1146 assert_eq!(thunk.evaluate(), 42);
1147 }
1148
1149 #[test]
1151 fn test_thunk_from_memo() {
1152 use crate::types::RcLazy;
1153 let memo = RcLazy::new(|| 42);
1154 let thunk = Thunk::from(memo);
1155 assert_eq!(thunk.evaluate(), 42);
1156 }
1157
1158 #[test]
1163 fn test_thunk_from_send_thunk() {
1164 use crate::types::SendThunk;
1165 let send_thunk = SendThunk::new(|| 21 * 2);
1166 let thunk = Thunk::from(send_thunk);
1167 assert_eq!(thunk.evaluate(), 42);
1168 }
1169
1170 #[test]
1174 fn test_thunk_semigroup() {
1175 use crate::{
1176 brands::*,
1177 classes::semigroup::append,
1178 functions::*,
1179 };
1180 let t1 = pure::<ThunkBrand, _>("Hello".to_string());
1181 let t2 = pure::<ThunkBrand, _>(" World".to_string());
1182 let t3 = append(t1, t2);
1183 assert_eq!(t3.evaluate(), "Hello World");
1184 }
1185
1186 #[test]
1190 fn test_thunk_monoid() {
1191 use crate::classes::monoid::empty;
1192 let t: Thunk<String> = empty();
1193 assert_eq!(t.evaluate(), "");
1194 }
1195
1196 #[test]
1200 fn test_thunk_from_trampoline() {
1201 use crate::types::Trampoline;
1202
1203 let task = Trampoline::pure(42);
1204 let thunk = Thunk::from(task);
1205 assert_eq!(thunk.evaluate(), 42);
1206 }
1207
1208 #[test]
1212 fn test_thunk_from_trampoline_lazy() {
1213 use crate::types::Trampoline;
1214
1215 let task = Trampoline::new(|| 21 * 2);
1216 let thunk = Thunk::from(task);
1217 assert_eq!(thunk.evaluate(), 42);
1218 }
1219
1220 #[test]
1224 fn test_thunk_to_trampoline() {
1225 use crate::types::Trampoline;
1226 let thunk = Thunk::new(|| 42);
1227 let trampoline = Trampoline::from(thunk);
1228 assert_eq!(trampoline.evaluate(), 42);
1229 }
1230
1231 #[test]
1235 fn test_thunk_to_trampoline_chained() {
1236 use crate::types::Trampoline;
1237 let thunk = Thunk::pure(10).map(|x| x * 3).bind(|x| Thunk::pure(x + 12));
1238 let trampoline = Trampoline::from(thunk);
1239 assert_eq!(trampoline.evaluate(), 42);
1240 }
1241
1242 #[test]
1247 fn test_thunk_to_trampoline_non_send() {
1248 use {
1249 crate::types::Trampoline,
1250 std::rc::Rc,
1251 };
1252 let thunk = Thunk::new(|| Rc::new(42));
1253 let trampoline = Trampoline::from(thunk);
1254 assert_eq!(*trampoline.evaluate(), 42);
1255 }
1256
1257 #[quickcheck]
1263 fn functor_identity(x: i32) -> bool {
1264 map::<ThunkBrand, _, _>(identity, pure::<ThunkBrand, _>(x)).evaluate() == x
1265 }
1266
1267 #[quickcheck]
1269 fn functor_composition(x: i32) -> bool {
1270 let f = |a: i32| a.wrapping_add(1);
1271 let g = |a: i32| a.wrapping_mul(2);
1272 let lhs = map::<ThunkBrand, _, _>(move |a| f(g(a)), pure::<ThunkBrand, _>(x)).evaluate();
1273 let rhs = map::<ThunkBrand, _, _>(f, map::<ThunkBrand, _, _>(g, pure::<ThunkBrand, _>(x)))
1274 .evaluate();
1275 lhs == rhs
1276 }
1277
1278 #[quickcheck]
1282 fn monad_left_identity(a: i32) -> bool {
1283 let f = |x: i32| pure::<ThunkBrand, _>(x.wrapping_mul(2));
1284 let lhs = bind::<ThunkBrand, _, _>(pure::<ThunkBrand, _>(a), f).evaluate();
1285 let rhs = f(a).evaluate();
1286 lhs == rhs
1287 }
1288
1289 #[quickcheck]
1291 fn monad_right_identity(x: i32) -> bool {
1292 let lhs =
1293 bind::<ThunkBrand, _, _>(pure::<ThunkBrand, _>(x), pure::<ThunkBrand, _>).evaluate();
1294 lhs == x
1295 }
1296
1297 #[quickcheck]
1299 fn monad_associativity(x: i32) -> bool {
1300 let f = |a: i32| pure::<ThunkBrand, _>(a.wrapping_add(1));
1301 let g = |a: i32| pure::<ThunkBrand, _>(a.wrapping_mul(3));
1302 let m = pure::<ThunkBrand, _>(x);
1303 let m2 = pure::<ThunkBrand, _>(x);
1304 let lhs = bind::<ThunkBrand, _, _>(bind::<ThunkBrand, _, _>(m, f), g).evaluate();
1305 let rhs =
1306 bind::<ThunkBrand, _, _>(m2, move |a| bind::<ThunkBrand, _, _>(f(a), g)).evaluate();
1307 lhs == rhs
1308 }
1309
1310 #[quickcheck]
1314 fn semigroup_associativity(
1315 a: String,
1316 b: String,
1317 c: String,
1318 ) -> bool {
1319 let ta = pure::<ThunkBrand, _>(a.clone());
1320 let tb = pure::<ThunkBrand, _>(b.clone());
1321 let tc = pure::<ThunkBrand, _>(c.clone());
1322 let ta2 = pure::<ThunkBrand, _>(a);
1323 let tb2 = pure::<ThunkBrand, _>(b);
1324 let tc2 = pure::<ThunkBrand, _>(c);
1325 let lhs = append(append(ta, tb), tc).evaluate();
1326 let rhs = append(ta2, append(tb2, tc2)).evaluate();
1327 lhs == rhs
1328 }
1329
1330 #[quickcheck]
1334 fn monoid_left_identity(x: String) -> bool {
1335 let a = pure::<ThunkBrand, _>(x.clone());
1336 let lhs: Thunk<String> = append(empty(), a);
1337 lhs.evaluate() == x
1338 }
1339
1340 #[quickcheck]
1342 fn monoid_right_identity(x: String) -> bool {
1343 let a = pure::<ThunkBrand, _>(x.clone());
1344 let rhs: Thunk<String> = append(a, empty());
1345 rhs.evaluate() == x
1346 }
1347
1348 #[test]
1352 fn test_foldable_via_brand() {
1353 let thunk = pure::<ThunkBrand, _>(10);
1354 let result = fold_right::<RcFnBrand, ThunkBrand, _, _>(|x, acc| x + acc, 5, thunk);
1355 assert_eq!(result, 15);
1356 }
1357
1358 #[test]
1360 fn test_lift2_via_brand() {
1361 let t1 = pure::<ThunkBrand, _>(10);
1362 let t2 = pure::<ThunkBrand, _>(20);
1363 let result = lift2::<ThunkBrand, _, _, _>(|a, b| a + b, t1, t2);
1364 assert_eq!(result.evaluate(), 30);
1365 }
1366
1367 #[test]
1369 fn test_apply_via_brand() {
1370 let func = pure::<ThunkBrand, _>(cloneable_fn_new::<RcFnBrand, _, _>(|x: i32| x * 2));
1371 let val = pure::<ThunkBrand, _>(21);
1372 let result = apply::<RcFnBrand, ThunkBrand, _, _>(func, val);
1373 assert_eq!(result.evaluate(), 42);
1374 }
1375
1376 #[test]
1378 fn test_extract_via_brand() {
1379 let thunk = pure::<ThunkBrand, _>(42);
1380 let result = extract::<ThunkBrand, _>(thunk);
1381 assert_eq!(result, 42);
1382 }
1383
1384 #[test]
1388 fn test_memoize_caching() {
1389 use std::cell::Cell;
1390
1391 let counter = Cell::new(0usize);
1392 let thunk = Thunk::new(|| {
1393 counter.set(counter.get() + 1);
1394 42
1395 });
1396 let lazy = thunk.into_rc_lazy();
1397
1398 assert_eq!(counter.get(), 0);
1399 assert_eq!(*lazy.evaluate(), 42);
1400 assert_eq!(counter.get(), 1);
1401 assert_eq!(*lazy.evaluate(), 42);
1402 assert_eq!(counter.get(), 1);
1403 }
1404
1405 #[test]
1407 fn test_memoize_arc_caching() {
1408 use std::sync::atomic::{
1409 AtomicUsize,
1410 Ordering,
1411 };
1412
1413 let counter = AtomicUsize::new(0);
1414 let thunk = Thunk::new(|| {
1415 counter.fetch_add(1, Ordering::SeqCst);
1416 42
1417 });
1418 let lazy = thunk.into_arc_lazy();
1419
1420 assert_eq!(counter.load(Ordering::SeqCst), 1);
1423 assert_eq!(*lazy.evaluate(), 42);
1424 assert_eq!(counter.load(Ordering::SeqCst), 1);
1425 assert_eq!(*lazy.evaluate(), 42);
1426 assert_eq!(counter.load(Ordering::SeqCst), 1);
1427 }
1428
1429 #[test]
1434 fn test_tail_rec_m_stack_safety() {
1435 use {
1436 crate::{
1437 brands::ThunkBrand,
1438 classes::monad_rec::tail_rec_m,
1439 functions::pure,
1440 },
1441 core::ops::ControlFlow,
1442 };
1443
1444 let iterations: i64 = 200_000;
1445 let result = tail_rec_m::<ThunkBrand, _, _>(
1446 |acc| {
1447 pure::<ThunkBrand, _>(
1448 if acc < iterations {
1449 ControlFlow::Continue(acc + 1)
1450 } else {
1451 ControlFlow::Break(acc)
1452 },
1453 )
1454 },
1455 0i64,
1456 );
1457 assert_eq!(result.evaluate(), iterations);
1458 }
1459
1460 #[test]
1464 fn test_functor_with_index() {
1465 use crate::{
1466 brands::ThunkBrand,
1467 classes::functor_with_index::FunctorWithIndex,
1468 functions::pure,
1469 };
1470
1471 let thunk = pure::<ThunkBrand, _>(21);
1472 let result = ThunkBrand::map_with_index(|(), x| x * 2, thunk);
1473 assert_eq!(result.evaluate(), 42);
1474 }
1475
1476 #[test]
1480 fn test_functor_with_index_identity() {
1481 use crate::{
1482 brands::ThunkBrand,
1483 classes::functor_with_index::FunctorWithIndex,
1484 functions::pure,
1485 };
1486
1487 let thunk = pure::<ThunkBrand, _>(42);
1488 let result = ThunkBrand::map_with_index(|_, a: i32| a, thunk);
1489 assert_eq!(result.evaluate(), 42);
1490 }
1491
1492 #[test]
1496 fn test_functor_with_index_compat_with_functor() {
1497 use crate::{
1498 brands::ThunkBrand,
1499 classes::functor_with_index::FunctorWithIndex,
1500 functions::{
1501 map,
1502 pure,
1503 },
1504 };
1505
1506 let f = |a: i32| a * 3 + 1;
1507 let thunk1 = pure::<ThunkBrand, _>(10);
1508 let thunk2 = pure::<ThunkBrand, _>(10);
1509 let via_map = map::<ThunkBrand, _, _>(f, thunk1).evaluate();
1510 let via_map_with_index = ThunkBrand::map_with_index(|_, a| f(a), thunk2).evaluate();
1511 assert_eq!(via_map, via_map_with_index);
1512 }
1513
1514 #[test]
1518 fn test_foldable_with_index() {
1519 use crate::{
1520 brands::ThunkBrand,
1521 classes::foldable_with_index::FoldableWithIndex,
1522 functions::pure,
1523 };
1524
1525 let thunk = pure::<ThunkBrand, _>(42);
1526 let result: String = ThunkBrand::fold_map_with_index(|(), a: i32| a.to_string(), thunk);
1527 assert_eq!(result, "42");
1528 }
1529
1530 #[test]
1534 fn test_foldable_with_index_compat_with_foldable() {
1535 use crate::{
1536 brands::*,
1537 classes::foldable_with_index::FoldableWithIndex,
1538 functions::{
1539 fold_map,
1540 pure,
1541 },
1542 };
1543
1544 let f = |a: i32| a.to_string();
1545 let thunk1 = pure::<ThunkBrand, _>(99);
1546 let thunk2 = pure::<ThunkBrand, _>(99);
1547 let via_fold_map = fold_map::<RcFnBrand, ThunkBrand, _, _>(f, thunk1);
1548 let via_fold_map_with_index: String = ThunkBrand::fold_map_with_index(|_, a| f(a), thunk2);
1549 assert_eq!(via_fold_map, via_fold_map_with_index);
1550 }
1551
1552 #[quickcheck]
1556 fn extract_pure(x: i32) -> bool {
1557 extract::<ThunkBrand, _>(pure::<ThunkBrand, _>(x)) == x
1558 }
1559
1560 #[quickcheck]
1562 fn comonad_left_identity(x: i32) -> bool {
1563 use crate::classes::extend::extend;
1564 let f = |w: Thunk<i32>| w.evaluate().wrapping_mul(3);
1565 let wa = pure::<ThunkBrand, _>(x);
1566 let wa2 = pure::<ThunkBrand, _>(x);
1567 extract::<ThunkBrand, _>(extend::<ThunkBrand, _, _>(f, wa)) == f(wa2)
1568 }
1569
1570 #[quickcheck]
1572 fn comonad_right_identity(x: i32) -> bool {
1573 use crate::classes::extend::extend;
1574 let wa = pure::<ThunkBrand, _>(x);
1575 extract::<ThunkBrand, _>(extend::<ThunkBrand, _, _>(extract::<ThunkBrand, _>, wa)) == x
1576 }
1577
1578 #[quickcheck]
1581 fn extend_associativity(x: i32) -> bool {
1582 use crate::classes::extend::extend;
1583 let g = |w: Thunk<i32>| w.evaluate().wrapping_mul(2);
1584 let f = |w: Thunk<i32>| w.evaluate().wrapping_add(1);
1585 let lhs = extract::<ThunkBrand, _>(extend::<ThunkBrand, _, _>(
1586 f,
1587 extend::<ThunkBrand, _, _>(g, pure::<ThunkBrand, _>(x)),
1588 ));
1589 let rhs = extract::<ThunkBrand, _>(extend::<ThunkBrand, _, _>(
1590 |w: Thunk<i32>| f(extend::<ThunkBrand, _, _>(g, w)),
1591 pure::<ThunkBrand, _>(x),
1592 ));
1593 lhs == rhs
1594 }
1595
1596 #[test]
1598 fn extend_test() {
1599 use crate::classes::extend::extend;
1600 let thunk = Thunk::new(|| 21);
1601 let result = extend::<ThunkBrand, _, _>(|w: Thunk<i32>| w.evaluate() * 2, thunk);
1602 assert_eq!(result.evaluate(), 42);
1603 }
1604
1605 #[quickcheck]
1607 fn comonad_map_extract(x: i32) -> bool {
1608 let f = |a: i32| a.wrapping_mul(3).wrapping_add(7);
1609 let fa = Thunk::new(|| x);
1610 let fa2 = Thunk::new(|| x);
1611 let lhs = extract::<ThunkBrand, _>(map::<ThunkBrand, _, _>(f, fa));
1612 let rhs = f(extract::<ThunkBrand, _>(fa2));
1613 lhs == rhs
1614 }
1615}