1#[fp_macros::document_module]
57mod inner {
58 use {
59 crate::{
60 brands::ArcCoyonedaBrand,
61 classes::{
62 Lift,
63 NaturalTransformation,
64 *,
65 },
66 impl_kind,
67 kinds::*,
68 },
69 fp_macros::*,
70 std::sync::Arc,
71 };
72
73 #[document_type_parameters(
78 "The lifetime of the values.",
79 "The brand of the underlying type constructor.",
80 "The output type of the accumulated mapping function."
81 )]
82 #[document_parameters("The trait object reference.")]
83 trait ArcCoyonedaLowerRef<'a, F, A: 'a>: Send + Sync + 'a
84 where
85 F: Kind_cdc7cd43dac7585f + 'a, {
86 #[document_signature]
88 #[document_returns("The underlying functor value with accumulated functions applied.")]
90 #[document_examples]
91 fn lower_ref(&self) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
102 where
103 F: Functor;
104 }
105
106 struct ArcCoyonedaBase<'a, F, A: 'a>
111 where
112 F: Kind_cdc7cd43dac7585f<Of<'a, A>: Send + Sync> + 'a, {
113 fa: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
114 }
115
116 #[document_type_parameters(
117 "The lifetime of the values.",
118 "The brand of the underlying type constructor.",
119 "The type of the value inside the functor."
120 )]
121 #[document_parameters("The base layer instance.")]
122 impl<'a, F, A: 'a> ArcCoyonedaLowerRef<'a, F, A> for ArcCoyonedaBase<'a, F, A>
123 where
124 F: Kind_cdc7cd43dac7585f + 'a,
125 Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone + Send + Sync,
126 {
127 #[document_signature]
129 #[document_returns("A clone of the underlying functor value.")]
131 #[document_examples]
132 fn lower_ref(&self) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
143 where
144 F: Functor, {
145 self.fa.clone()
146 }
147 }
148
149 struct ArcCoyonedaMapLayer<'a, F, B: 'a, A: 'a>
154 where
155 F: Kind_cdc7cd43dac7585f + 'a, {
156 inner: Arc<dyn ArcCoyonedaLowerRef<'a, F, B> + 'a>,
157 func: Arc<dyn Fn(B) -> A + Send + Sync + 'a>,
158 }
159
160 #[document_type_parameters(
166 "The lifetime of the values.",
167 "The brand of the underlying type constructor.",
168 "The input type of this layer's mapping function.",
169 "The output type of this layer's mapping function."
170 )]
171 #[document_parameters("The map layer instance.")]
172 impl<'a, F, B: 'a, A: 'a> ArcCoyonedaLowerRef<'a, F, A> for ArcCoyonedaMapLayer<'a, F, B, A>
173 where
174 F: Kind_cdc7cd43dac7585f + 'a,
175 {
176 #[document_signature]
178 #[document_returns("The underlying functor value with this layer's function applied.")]
180 #[document_examples]
181 fn lower_ref(&self) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
192 where
193 F: Functor, {
194 #[cfg(feature = "stacker")]
195 {
196 stacker::maybe_grow(32 * 1024, 1024 * 1024, || {
197 let lowered = self.inner.lower_ref();
198 let func = self.func.clone();
199 F::map(move |b| (*func)(b), lowered)
200 })
201 }
202 #[cfg(not(feature = "stacker"))]
203 {
204 let lowered = self.inner.lower_ref();
205 let func = self.func.clone();
206 F::map(move |b| (*func)(b), lowered)
207 }
208 }
209 }
210
211 struct ArcCoyonedaNewLayer<'a, F, B: 'a, A: 'a>
217 where
218 F: Kind_cdc7cd43dac7585f<Of<'a, B>: Send + Sync> + 'a, {
219 fb: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
220 func: Arc<dyn Fn(B) -> A + Send + Sync + 'a>,
221 }
222
223 #[document_type_parameters(
224 "The lifetime of the values.",
225 "The brand of the underlying type constructor.",
226 "The input type of the stored function.",
227 "The output type of the stored function."
228 )]
229 #[document_parameters("The new layer instance.")]
230 impl<'a, F, B: 'a, A: 'a> ArcCoyonedaLowerRef<'a, F, A> for ArcCoyonedaNewLayer<'a, F, B, A>
231 where
232 F: Kind_cdc7cd43dac7585f + 'a,
233 Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>): Clone + Send + Sync,
234 {
235 #[document_signature]
237 #[document_returns("The underlying functor value with the stored function applied.")]
239 #[document_examples]
240 fn lower_ref(&self) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
251 where
252 F: Functor, {
253 let func = self.func.clone();
254 F::map(move |b| (*func)(b), self.fb.clone())
255 }
256 }
257
258 #[document_type_parameters(
268 "The lifetime of the values.",
269 "The brand of the underlying type constructor.",
270 "The current output type."
271 )]
272 pub struct ArcCoyoneda<'a, F, A: 'a>(Arc<dyn ArcCoyonedaLowerRef<'a, F, A> + 'a>)
273 where
274 F: Kind_cdc7cd43dac7585f + 'a;
275
276 #[document_type_parameters(
277 "The lifetime of the values.",
278 "The brand of the underlying type constructor.",
279 "The current output type."
280 )]
281 #[document_parameters("The `ArcCoyoneda` instance to clone.")]
282 impl<'a, F, A: 'a> Clone for ArcCoyoneda<'a, F, A>
283 where
284 F: Kind_cdc7cd43dac7585f + 'a,
285 {
286 #[document_signature]
288 #[document_returns("A new `ArcCoyoneda` sharing the same inner layers.")]
290 #[document_examples]
291 fn clone(&self) -> Self {
303 ArcCoyoneda(self.0.clone())
304 }
305 }
306
307 #[document_type_parameters(
308 "The lifetime of the values.",
309 "The brand of the underlying type constructor.",
310 "The current output type."
311 )]
312 #[document_parameters("The `ArcCoyoneda` instance.")]
313 impl<'a, F, A: 'a> ArcCoyoneda<'a, F, A>
314 where
315 F: Kind_cdc7cd43dac7585f + 'a,
316 {
317 #[document_signature]
323 #[document_parameters("The functor value to lift.")]
325 #[document_returns("An `ArcCoyoneda` wrapping the value.")]
327 #[document_examples]
328 pub fn lift(fa: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)) -> Self
339 where
340 Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone + Send + Sync, {
341 ArcCoyoneda(Arc::new(ArcCoyonedaBase {
342 fa,
343 }))
344 }
345
346 #[document_signature]
350 #[document_returns("The underlying functor value with all accumulated functions applied.")]
352 #[document_examples]
353 pub fn lower_ref(&self) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
364 where
365 F: Functor, {
366 self.0.lower_ref()
367 }
368
369 #[document_signature]
378 #[document_returns("A new `ArcCoyoneda` with a single base layer.")]
380 #[document_examples]
381 pub fn collapse(&self) -> ArcCoyoneda<'a, F, A>
393 where
394 F: Functor,
395 Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone + Send + Sync, {
396 ArcCoyoneda::lift(self.lower_ref())
397 }
398
399 #[document_signature]
404 #[document_type_parameters(
406 "The brand of the cloneable function to use.",
407 "The type of the monoid."
408 )]
409 #[document_parameters("The function to map each element to a monoid.")]
411 #[document_returns("The combined monoid value.")]
413 #[document_examples]
414 pub fn fold_map<FnBrand: LiftFn + 'a, M>(
428 &self,
429 func: impl Fn(A) -> M + 'a,
430 ) -> M
431 where
432 F: Functor + Foldable,
433 A: Clone,
434 M: Monoid + 'a, {
435 F::fold_map::<FnBrand, A, M>(func, self.lower_ref())
436 }
437
438 #[document_signature]
443 #[document_type_parameters("The new output type after applying the function.")]
445 #[document_parameters("The function to apply.")]
447 #[document_returns(
449 "A new `ArcCoyoneda` with the function stored for deferred application."
450 )]
451 #[document_examples]
452 pub fn map<B: 'a>(
463 self,
464 f: impl Fn(A) -> B + Send + Sync + 'a,
465 ) -> ArcCoyoneda<'a, F, B> {
466 ArcCoyoneda(Arc::new(ArcCoyonedaMapLayer {
467 inner: self.0,
468 func: Arc::new(f),
469 }))
470 }
471
472 #[document_signature]
477 #[document_type_parameters("The input type of the function.")]
479 #[document_parameters("The function to defer.", "The functor value.")]
481 #[document_returns("An `ArcCoyoneda` wrapping the value with the deferred function.")]
483 #[document_examples]
484 pub fn new<B: 'a>(
495 f: impl Fn(B) -> A + Send + Sync + 'a,
496 fb: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
497 ) -> Self
498 where
499 Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>): Clone + Send + Sync, {
500 ArcCoyoneda(Arc::new(ArcCoyonedaNewLayer {
501 fb,
502 func: Arc::new(f),
503 }))
504 }
505
506 #[document_signature]
513 #[document_type_parameters("The target functor brand.")]
515 #[document_parameters("The natural transformation from `F` to `G`.")]
517 #[document_returns("A new `ArcCoyoneda` over the target functor `G`.")]
519 #[document_examples]
520 pub fn hoist<G: Kind_cdc7cd43dac7585f + 'a>(
543 self,
544 nat: impl NaturalTransformation<F, G>,
545 ) -> ArcCoyoneda<'a, G, A>
546 where
547 F: Functor,
548 Apply!(<G as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone + Send + Sync, {
549 ArcCoyoneda::lift(nat.transform(self.lower_ref()))
550 }
551
552 #[document_signature]
556 #[document_parameters("The value to wrap.")]
558 #[document_returns("An `ArcCoyoneda` containing the pure value.")]
560 #[document_examples]
561 pub fn pure(a: A) -> Self
572 where
573 F: Pointed,
574 Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone + Send + Sync, {
575 ArcCoyoneda::lift(F::pure(a))
576 }
577
578 #[document_signature]
584 #[document_type_parameters("The output type of the bound computation.")]
586 #[document_parameters(
588 "The function to apply to the inner value, returning a new `ArcCoyoneda`."
589 )]
590 #[document_returns("A new `ArcCoyoneda` containing the bound result.")]
592 #[document_examples]
593 pub fn bind<B: 'a>(
605 self,
606 func: impl Fn(A) -> ArcCoyoneda<'a, F, B> + 'a,
607 ) -> ArcCoyoneda<'a, F, B>
608 where
609 F: Functor + Semimonad,
610 Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>): Clone + Send + Sync, {
611 ArcCoyoneda::lift(F::bind(self.lower_ref(), move |a| func(a).lower_ref()))
612 }
613
614 #[document_signature]
619 #[document_type_parameters(
621 "The brand of the cloneable function wrapper.",
622 "The type of the function input.",
623 "The type of the function output."
624 )]
625 #[document_parameters(
627 "The `ArcCoyoneda` containing the function(s).",
628 "The `ArcCoyoneda` containing the value(s)."
629 )]
630 #[document_returns("A new `ArcCoyoneda` containing the applied result(s).")]
632 #[document_examples]
633 pub fn apply<FnBrand: LiftFn + 'a, B: Clone + 'a, C: 'a>(
650 ff: ArcCoyoneda<'a, F, <FnBrand as CloneFn>::Of<'a, B, C>>,
651 fa: ArcCoyoneda<'a, F, B>,
652 ) -> ArcCoyoneda<'a, F, C>
653 where
654 F: Functor + Semiapplicative,
655 Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>): Clone + Send + Sync, {
656 ArcCoyoneda::lift(F::apply::<FnBrand, B, C>(ff.lower_ref(), fa.lower_ref()))
657 }
658
659 #[document_signature]
664 #[document_type_parameters("The type of the second value.", "The type of the result.")]
666 #[document_parameters("The binary function to apply.", "The second `ArcCoyoneda` value.")]
668 #[document_returns("An `ArcCoyoneda` containing the result.")]
670 #[document_examples]
671 pub fn lift2<B: Clone + 'a, C: 'a>(
684 self,
685 func: impl Fn(A, B) -> C + 'a,
686 fb: ArcCoyoneda<'a, F, B>,
687 ) -> ArcCoyoneda<'a, F, C>
688 where
689 F: Functor + Lift,
690 A: Clone,
691 Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>): Clone + Send + Sync, {
692 ArcCoyoneda::lift(F::lift2(func, self.lower_ref(), fb.lower_ref()))
693 }
694 }
695
696 impl_kind! {
699 impl<F: Kind_cdc7cd43dac7585f + 'static> for ArcCoyonedaBrand<F> {
700 type Of<'a, A: 'a>: 'a = ArcCoyoneda<'a, F, A>;
701 }
702 }
703
704 #[document_type_parameters("The brand of the underlying foldable functor.")]
724 impl<F: Functor + Foldable + 'static> Foldable for ArcCoyonedaBrand<F> {
725 #[document_signature]
729 #[document_type_parameters(
731 "The lifetime of the elements.",
732 "The brand of the cloneable function to use.",
733 "The type of the elements in the structure.",
734 "The type of the monoid."
735 )]
736 #[document_parameters(
738 "The function to map each element to a monoid.",
739 "The `ArcCoyoneda` structure to fold."
740 )]
741 #[document_returns("The combined monoid value.")]
743 #[document_examples]
744 fn fold_map<'a, FnBrand, A: 'a + Clone, M>(
760 func: impl Fn(A) -> M + 'a,
761 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
762 ) -> M
763 where
764 M: Monoid + 'a,
765 FnBrand: LiftFn + 'a, {
766 F::fold_map::<FnBrand, A, M>(func, fa.lower_ref())
767 }
768 }
769
770 #[document_type_parameters(
773 "The lifetime of the values.",
774 "The brand of the underlying type constructor.",
775 "The current output type."
776 )]
777 #[document_parameters("The `ArcCoyoneda` instance.")]
778 impl<'a, F, A: 'a> core::fmt::Debug for ArcCoyoneda<'a, F, A>
779 where
780 F: Kind_cdc7cd43dac7585f + 'a,
781 {
782 #[document_signature]
787 #[document_parameters("The formatter.")]
789 #[document_returns("The formatting result.")]
791 #[document_examples]
792 fn fmt(
803 &self,
804 f: &mut core::fmt::Formatter<'_>,
805 ) -> core::fmt::Result {
806 f.write_str("ArcCoyoneda(<opaque>)")
807 }
808 }
809
810 #[document_type_parameters(
813 "The lifetime of the values.",
814 "The brand of the underlying functor.",
815 "The type of the values."
816 )]
817 impl<'a, F, A: 'a> From<ArcCoyoneda<'a, F, A>> for crate::types::Coyoneda<'a, F, A>
818 where
819 F: Kind_cdc7cd43dac7585f + Functor + 'a,
820 {
821 #[document_signature]
826 #[document_parameters("The `ArcCoyoneda` to convert.")]
828 #[document_returns("A `Coyoneda` containing the lowered value.")]
830 #[document_examples]
831 fn from(arc: ArcCoyoneda<'a, F, A>) -> Self {
843 crate::types::Coyoneda::lift(arc.lower_ref())
844 }
845 }
846
847 const _: () = {
853 fn _assert_send<T: Send>() {}
854 fn _assert_sync<T: Sync>() {}
855
856 fn _check_base<'a, F: Kind_cdc7cd43dac7585f<Of<'a, A>: Send + Sync> + 'a, A: 'a>() {
858 _assert_send::<ArcCoyonedaBase<'a, F, A>>();
859 _assert_sync::<ArcCoyonedaBase<'a, F, A>>();
860 }
861
862 fn _check_map_layer<'a, F: Kind_cdc7cd43dac7585f + 'a, B: 'a, A: 'a>() {
865 _assert_send::<ArcCoyonedaMapLayer<'a, F, B, A>>();
866 _assert_sync::<ArcCoyonedaMapLayer<'a, F, B, A>>();
867 }
868
869 fn _check_new_layer<
871 'a,
872 F: Kind_cdc7cd43dac7585f<Of<'a, B>: Send + Sync> + 'a,
873 B: 'a,
874 A: 'a,
875 >() {
876 _assert_send::<ArcCoyonedaNewLayer<'a, F, B, A>>();
877 _assert_sync::<ArcCoyonedaNewLayer<'a, F, B, A>>();
878 }
879 };
880}
881
882pub use inner::*;
883
884#[cfg(test)]
885#[expect(clippy::unwrap_used, reason = "Tests use panicking operations for brevity and clarity")]
886mod tests {
887 use crate::{
888 brands::*,
889 functions::*,
890 types::*,
891 };
892
893 #[test]
894 fn lift_lower_ref_identity() {
895 let coyo = ArcCoyoneda::<OptionBrand, _>::lift(Some(42));
896 assert_eq!(coyo.lower_ref(), Some(42));
897 }
898
899 #[test]
900 fn chained_maps() {
901 let result = ArcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3])
902 .map(|x| x + 1)
903 .map(|x| x * 2)
904 .lower_ref();
905 assert_eq!(result, vec![4, 6, 8]);
906 }
907
908 #[test]
909 fn clone_and_lower() {
910 let coyo = ArcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x + 1);
911 let coyo2 = coyo.clone();
912 assert_eq!(coyo.lower_ref(), vec![2, 3, 4]);
913 assert_eq!(coyo2.lower_ref(), vec![2, 3, 4]);
914 }
915
916 #[test]
917 fn send_across_thread() {
918 let coyo = ArcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x * 10);
919 let handle = std::thread::spawn(move || coyo.lower_ref());
920 assert_eq!(handle.join().unwrap(), vec![10, 20, 30]);
921 }
922
923 #[test]
924 fn fold_map_on_mapped() {
925 let coyo = ArcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x * 10);
926 let result = explicit::fold_map::<RcFnBrand, ArcCoyonedaBrand<VecBrand>, _, _, _, _>(
927 |x: i32| x.to_string(),
928 coyo,
929 );
930 assert_eq!(result, "102030".to_string());
931 }
932
933 #[test]
934 fn map_on_none_stays_none() {
935 let result = ArcCoyoneda::<OptionBrand, _>::lift(None::<i32>).map(|x| x + 1).lower_ref();
936 assert_eq!(result, None);
937 }
938
939 mod property {
942 use {
943 crate::{
944 brands::*,
945 functions::*,
946 types::*,
947 },
948 quickcheck_macros::quickcheck,
949 };
950
951 #[quickcheck]
952 fn functor_identity_vec(v: Vec<i32>) -> bool {
953 let coyo = ArcCoyoneda::<VecBrand, _>::lift(v.clone());
954 coyo.map(identity).lower_ref() == v
955 }
956
957 #[quickcheck]
958 fn functor_identity_option(x: Option<i32>) -> bool {
959 let coyo = ArcCoyoneda::<OptionBrand, _>::lift(x);
960 coyo.map(identity).lower_ref() == x
961 }
962
963 #[quickcheck]
964 fn functor_composition_vec(v: Vec<i32>) -> bool {
965 let f = |x: i32| x.wrapping_add(1);
966 let g = |x: i32| x.wrapping_mul(2);
967
968 let left = ArcCoyoneda::<VecBrand, _>::lift(v.clone()).map(compose(f, g)).lower_ref();
969 let right = ArcCoyoneda::<VecBrand, _>::lift(v).map(g).map(f).lower_ref();
970 left == right
971 }
972
973 #[quickcheck]
974 fn functor_composition_option(x: Option<i32>) -> bool {
975 let f = |x: i32| x.wrapping_add(1);
976 let g = |x: i32| x.wrapping_mul(2);
977
978 let left = ArcCoyoneda::<OptionBrand, _>::lift(x).map(compose(f, g)).lower_ref();
979 let right = ArcCoyoneda::<OptionBrand, _>::lift(x).map(g).map(f).lower_ref();
980 left == right
981 }
982
983 #[quickcheck]
984 fn collapse_preserves_value(v: Vec<i32>) -> bool {
985 let coyo = ArcCoyoneda::<VecBrand, _>::lift(v)
986 .map(|x: i32| x.wrapping_add(1))
987 .map(|x: i32| x.wrapping_mul(2));
988 let before = coyo.lower_ref();
989 let after = coyo.collapse().lower_ref();
990 before == after
991 }
992
993 #[quickcheck]
994 fn foldable_consistency_vec(v: Vec<i32>) -> bool {
995 let coyo = ArcCoyoneda::<VecBrand, _>::lift(v.clone()).map(|x: i32| x.wrapping_add(1));
996 let via_coyoneda: String =
997 explicit::fold_map::<RcFnBrand, ArcCoyonedaBrand<VecBrand>, _, _, _, _>(
998 |x: i32| x.to_string(),
999 coyo,
1000 );
1001 let direct: String = explicit::fold_map::<RcFnBrand, VecBrand, _, _, _, _>(
1002 |x: i32| x.to_string(),
1003 v.iter().map(|x| x.wrapping_add(1)).collect::<Vec<_>>(),
1004 );
1005 via_coyoneda == direct
1006 }
1007 }
1008}