1#[fp_macros::document_module]
135mod inner {
136 use {
137 crate::{
138 Apply,
139 brands::CoyonedaBrand,
140 classes::*,
141 impl_kind,
142 kinds::*,
143 types::CoyonedaExplicit,
144 },
145 fp_macros::*,
146 };
147
148 #[document_type_parameters(
155 "The lifetime of the values.",
156 "The brand of the underlying type constructor.",
157 "The output type of the accumulated mapping function."
158 )]
159 #[document_parameters("The boxed trait object to consume.")]
160 trait CoyonedaInner<'a, F, A: 'a>: 'a
161 where
162 F: Kind_cdc7cd43dac7585f + 'a, {
163 #[document_signature]
165 #[document_returns("The underlying functor value with accumulated functions applied.")]
167 #[document_examples]
168 fn lower(self: Box<Self>) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
179 where
180 F: Functor;
181 }
182
183 struct CoyonedaBase<'a, F, A: 'a>
187 where
188 F: Kind_cdc7cd43dac7585f + 'a, {
189 fa: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
190 }
191
192 #[document_type_parameters(
193 "The lifetime of the values.",
194 "The brand of the underlying type constructor.",
195 "The type of the value inside the functor."
196 )]
197 #[document_parameters("The base layer instance.")]
198 impl<'a, F, A: 'a> CoyonedaInner<'a, F, A> for CoyonedaBase<'a, F, A>
199 where
200 F: Kind_cdc7cd43dac7585f + 'a,
201 {
202 #[document_signature]
204 #[document_returns("The underlying functor value, unchanged.")]
206 #[document_examples]
207 fn lower(self: Box<Self>) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
218 where
219 F: Functor, {
220 self.fa
221 }
222 }
223
224 struct CoyonedaMapLayer<'a, F, B: 'a, A: 'a, Func: Fn(B) -> A + 'a>
233 where
234 F: Kind_cdc7cd43dac7585f + 'a, {
235 inner: Box<dyn CoyonedaInner<'a, F, B> + 'a>,
236 func: Func,
237 }
238
239 #[document_type_parameters(
240 "The lifetime of the values.",
241 "The brand of the underlying type constructor.",
242 "The input type of this layer's mapping function.",
243 "The output type of this layer's mapping function.",
244 "The type of this layer's mapping function."
245 )]
246 #[document_parameters("The map layer instance.")]
247 impl<'a, F, B: 'a, A: 'a, Func: Fn(B) -> A + 'a> CoyonedaInner<'a, F, A>
248 for CoyonedaMapLayer<'a, F, B, A, Func>
249 where
250 F: Kind_cdc7cd43dac7585f + 'a,
251 {
252 #[document_signature]
254 #[document_returns("The underlying functor value with this layer's function applied.")]
256 #[document_examples]
257 fn lower(self: Box<Self>) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
268 where
269 F: Functor, {
270 #[cfg(feature = "stacker")]
271 {
272 stacker::maybe_grow(32 * 1024, 1024 * 1024, || {
273 let lowered = self.inner.lower();
274 F::map(self.func, lowered)
275 })
276 }
277 #[cfg(not(feature = "stacker"))]
278 {
279 let lowered = self.inner.lower();
280 F::map(self.func, lowered)
281 }
282 }
283 }
284
285 struct CoyonedaNewLayer<'a, F, B: 'a, A: 'a, Func: Fn(B) -> A + 'a>
295 where
296 F: Kind_cdc7cd43dac7585f + 'a, {
297 fb: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
298 func: Func,
299 }
300
301 #[document_type_parameters(
302 "The lifetime of the values.",
303 "The brand of the underlying type constructor.",
304 "The type of the values in the underlying functor.",
305 "The output type of the mapping function.",
306 "The type of the mapping function."
307 )]
308 #[document_parameters("The new layer instance.")]
309 impl<'a, F, B: 'a, A: 'a, Func: Fn(B) -> A + 'a> CoyonedaInner<'a, F, A>
310 for CoyonedaNewLayer<'a, F, B, A, Func>
311 where
312 F: Kind_cdc7cd43dac7585f + 'a,
313 {
314 #[document_signature]
316 #[document_returns("The underlying functor value with the function applied.")]
318 #[document_examples]
319 fn lower(self: Box<Self>) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
330 where
331 F: Functor, {
332 F::map(self.func, self.fb)
333 }
334 }
335
336 #[document_type_parameters(
349 "The lifetime of the values.",
350 "The brand of the underlying type constructor.",
351 "The current output type."
352 )]
353 pub struct Coyoneda<'a, F, A: 'a>(Box<dyn CoyonedaInner<'a, F, A> + 'a>)
354 where
355 F: Kind_cdc7cd43dac7585f + 'a;
356
357 #[document_type_parameters(
358 "The lifetime of the values.",
359 "The brand of the underlying type constructor.",
360 "The current output type."
361 )]
362 #[document_parameters("The `Coyoneda` instance.")]
363 impl<'a, F, A: 'a> Coyoneda<'a, F, A>
364 where
365 F: Kind_cdc7cd43dac7585f + 'a,
366 {
367 #[document_signature]
373 #[document_type_parameters("The type of the values in the underlying functor.")]
375 #[document_parameters("The function to defer.", "The functor value.")]
377 #[document_returns("A `Coyoneda` wrapping the value with the deferred function.")]
379 #[document_examples]
380 pub fn new<B: 'a>(
391 f: impl Fn(B) -> A + 'a,
392 fb: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
393 ) -> Self {
394 Coyoneda(Box::new(CoyonedaNewLayer {
395 fb,
396 func: f,
397 }))
398 }
399
400 #[document_signature]
405 #[document_parameters("The functor value to lift.")]
407 #[document_returns("A `Coyoneda` wrapping the value.")]
409 #[document_examples]
410 pub fn lift(fa: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)) -> Self {
421 Coyoneda(Box::new(CoyonedaBase {
422 fa,
423 }))
424 }
425
426 #[document_signature]
432 #[document_returns("The underlying functor value with all accumulated functions applied.")]
434 #[document_examples]
435 pub fn lower(self) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
448 where
449 F: Functor, {
450 self.0.lower()
451 }
452
453 #[document_signature]
463 #[document_returns("A fresh `Coyoneda` with a single base layer.")]
465 #[document_examples]
466 pub fn collapse(self) -> Self
483 where
484 F: Functor, {
485 Coyoneda::lift(self.lower())
486 }
487
488 #[document_signature]
493 #[document_type_parameters("The new output type after applying the function.")]
495 #[document_parameters("The function to apply.")]
497 #[document_returns("A new `Coyoneda` with the function stored for deferred application.")]
499 #[document_examples]
500 pub fn map<B: 'a>(
512 self,
513 f: impl Fn(A) -> B + 'a,
514 ) -> Coyoneda<'a, F, B> {
515 Coyoneda(Box::new(CoyonedaMapLayer {
516 inner: self.0,
517 func: f,
518 }))
519 }
520
521 #[document_signature]
531 #[document_type_parameters("The brand of the target functor.")]
533 #[document_parameters("The natural transformation from `F` to `G`.")]
535 #[document_returns("A new `Coyoneda` over the target functor `G`.")]
537 #[document_examples]
538 pub fn hoist<G: Kind_cdc7cd43dac7585f + 'a>(
561 self,
562 nat: impl NaturalTransformation<F, G>,
563 ) -> Coyoneda<'a, G, A>
564 where
565 F: Functor, {
566 Coyoneda::lift(nat.transform(self.lower()))
567 }
568 }
569
570 impl_kind! {
573 impl<F: Kind_cdc7cd43dac7585f + 'static> for CoyonedaBrand<F> {
574 type Of<'a, A: 'a>: 'a = Coyoneda<'a, F, A>;
575 }
576 }
577
578 #[document_type_parameters("The brand of the underlying type constructor.")]
581 impl<F: Kind_cdc7cd43dac7585f + 'static> Functor for CoyonedaBrand<F> {
582 #[document_signature]
587 #[document_type_parameters(
589 "The lifetime of the values.",
590 "The type of the current output.",
591 "The type of the new output."
592 )]
593 #[document_parameters("The function to apply.", "The `Coyoneda` value.")]
595 #[document_returns("A new `Coyoneda` with the function stored for deferred application.")]
597 #[document_examples]
598 fn map<'a, A: 'a, B: 'a>(
611 func: impl Fn(A) -> B + 'a,
612 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
613 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
614 fa.map(func)
615 }
616 }
617
618 #[document_type_parameters("The brand of the underlying pointed functor.")]
621 impl<F: Pointed + 'static> Pointed for CoyonedaBrand<F> {
622 #[document_signature]
624 #[document_type_parameters("The lifetime of the value.", "The type of the value to wrap.")]
626 #[document_parameters("The value to wrap.")]
628 #[document_returns("A `Coyoneda` containing the pure value.")]
630 #[document_examples]
631 fn pure<'a, A: 'a>(a: A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>) {
643 Coyoneda::lift(F::pure(a))
644 }
645 }
646
647 #[document_type_parameters("The brand of the underlying foldable functor.")]
650 impl<F: Functor + Foldable + 'static> Foldable for CoyonedaBrand<F> {
651 #[document_signature]
661 #[document_type_parameters(
663 "The lifetime of the elements.",
664 "The brand of the cloneable function to use.",
665 "The type of the elements in the structure.",
666 "The type of the monoid."
667 )]
668 #[document_parameters(
670 "The function to map each element to a monoid.",
671 "The `Coyoneda` structure to fold."
672 )]
673 #[document_returns("The combined monoid value.")]
675 #[document_examples]
676 fn fold_map<'a, FnBrand, A: 'a + Clone, M>(
693 func: impl Fn(A) -> M + 'a,
694 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
695 ) -> M
696 where
697 M: Monoid + 'a,
698 FnBrand: LiftFn + 'a, {
699 F::fold_map::<FnBrand, A, M>(func, fa.lower())
700 }
701 }
702
703 #[document_type_parameters("The brand of the underlying type constructor.")]
706 impl<F: Functor + Lift + 'static> Lift for CoyonedaBrand<F> {
707 #[document_signature]
712 #[document_type_parameters(
714 "The lifetime of the values.",
715 "The type of the first value.",
716 "The type of the second value.",
717 "The type of the result."
718 )]
719 #[document_parameters(
721 "The binary function to apply.",
722 "The first `Coyoneda` value.",
723 "The second `Coyoneda` value."
724 )]
725 #[document_returns("A `Coyoneda` containing the result of applying the function.")]
727 #[document_examples]
728 fn lift2<'a, A, B, C>(
743 func: impl Fn(A, B) -> C + 'a,
744 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
745 fb: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
746 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>)
747 where
748 A: Clone + 'a,
749 B: Clone + 'a,
750 C: 'a, {
751 Coyoneda::lift(F::lift2(func, fa.lower(), fb.lower()))
752 }
753 }
754
755 #[document_type_parameters("The brand of the underlying type constructor.")]
758 impl<F: Functor + Lift + 'static> ApplyFirst for CoyonedaBrand<F> {}
759 #[document_type_parameters("The brand of the underlying type constructor.")]
760 impl<F: Functor + Lift + 'static> ApplySecond for CoyonedaBrand<F> {}
761
762 #[document_type_parameters("The brand of the underlying type constructor.")]
765 impl<F: Functor + Semiapplicative + 'static> Semiapplicative for CoyonedaBrand<F> {
766 #[document_signature]
771 #[document_type_parameters(
773 "The lifetime of the values.",
774 "The brand of the cloneable function wrapper.",
775 "The type of the input value.",
776 "The type of the output value."
777 )]
778 #[document_parameters(
780 "The `Coyoneda` containing the function(s).",
781 "The `Coyoneda` containing the value(s)."
782 )]
783 #[document_returns("A `Coyoneda` containing the result(s) of applying the function(s).")]
785 #[document_examples]
786 fn apply<'a, FnBrand: 'a + CloneFn, A: 'a + Clone, B: 'a>(
801 ff: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as CloneFn>::Of<'a, A, B>>),
802 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
803 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
804 Coyoneda::lift(F::apply::<FnBrand, A, B>(ff.lower(), fa.lower()))
805 }
806 }
807
808 #[document_type_parameters("The brand of the underlying type constructor.")]
811 impl<F: Functor + Semimonad + 'static> Semimonad for CoyonedaBrand<F> {
812 #[document_signature]
817 #[document_type_parameters(
819 "The lifetime of the values.",
820 "The type of the value in the input `Coyoneda`.",
821 "The type of the value in the output `Coyoneda`."
822 )]
823 #[document_parameters(
825 "The input `Coyoneda` value.",
826 "The function to apply to the inner value, returning a new `Coyoneda`."
827 )]
828 #[document_returns("A new `Coyoneda` containing the bound result.")]
830 #[document_examples]
831 fn bind<'a, A: 'a, B: 'a>(
846 ma: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
847 func: impl Fn(A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) + 'a,
848 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
849 Coyoneda::lift(F::bind(ma.lower(), move |a| func(a).lower()))
850 }
851 }
852
853 #[document_type_parameters(
856 "The lifetime of the values.",
857 "The brand of the underlying foldable functor.",
858 "The type of the values."
859 )]
860 impl<'a, F, A: 'a> From<Coyoneda<'a, F, A>> for CoyonedaExplicit<'a, F, A, A, fn(A) -> A>
861 where
862 F: Kind_cdc7cd43dac7585f + Functor + 'a,
863 {
864 #[document_signature]
872 #[document_parameters("The `Coyoneda` to convert.")]
874 #[document_returns(
876 "A `CoyonedaExplicit` in identity position (B = A) wrapping the lowered value."
877 )]
878 #[document_examples]
879 fn from(coyo: Coyoneda<'a, F, A>) -> Self {
891 CoyonedaExplicit::lift(coyo.lower())
892 }
893 }
894
895 #[document_type_parameters(
898 "The lifetime of the values.",
899 "The brand of the underlying type constructor.",
900 "The current output type."
901 )]
902 #[document_parameters("The `Coyoneda` instance.")]
903 impl<'a, F, A: 'a> core::fmt::Debug for Coyoneda<'a, F, A>
904 where
905 F: Kind_cdc7cd43dac7585f + 'a,
906 {
907 #[document_signature]
912 #[document_parameters("The formatter.")]
914 #[document_returns("The formatting result.")]
916 #[document_examples]
917 fn fmt(
928 &self,
929 f: &mut core::fmt::Formatter<'_>,
930 ) -> core::fmt::Result {
931 f.write_str("Coyoneda(<opaque>)")
932 }
933 }
934}
935
936pub use inner::*;
937
938#[cfg(test)]
939mod tests {
940 use crate::{
941 brands::*,
942 classes::*,
943 functions::*,
944 types::*,
945 };
946
947 #[test]
948 fn lift_lower_identity_option() {
949 let coyo = Coyoneda::<OptionBrand, _>::lift(Some(42));
950 assert_eq!(coyo.lower(), Some(42));
951 }
952
953 #[test]
954 fn lift_lower_identity_none() {
955 let coyo = Coyoneda::<OptionBrand, i32>::lift(None);
956 assert_eq!(coyo.lower(), None);
957 }
958
959 #[test]
960 fn lift_lower_identity_vec() {
961 let coyo = Coyoneda::<VecBrand, _>::lift(vec![1, 2, 3]);
962 assert_eq!(coyo.lower(), vec![1, 2, 3]);
963 }
964
965 #[test]
966 fn new_constructor() {
967 let coyo = Coyoneda::<VecBrand, _>::new(|x: i32| x * 2, vec![1, 2, 3]);
968 assert_eq!(coyo.lower(), vec![2, 4, 6]);
969 }
970
971 #[test]
972 fn new_is_equivalent_to_lift_then_map() {
973 let f = |x: i32| x.to_string();
974 let v = vec![1, 2, 3];
975
976 let via_new = Coyoneda::<VecBrand, _>::new(f, v.clone()).lower();
977 let via_lift_map = Coyoneda::<VecBrand, _>::lift(v).map(f).lower();
978
979 assert_eq!(via_new, via_lift_map);
980 }
981
982 #[test]
983 fn single_map_option() {
984 let result = Coyoneda::<OptionBrand, _>::lift(Some(5)).map(|x| x * 2).lower();
985 assert_eq!(result, Some(10));
986 }
987
988 #[test]
989 fn chained_maps_vec() {
990 let result = Coyoneda::<VecBrand, _>::lift(vec![1, 2, 3])
991 .map(|x| x + 1)
992 .map(|x| x * 2)
993 .map(|x| x.to_string())
994 .lower();
995 assert_eq!(result, vec!["4", "6", "8"]);
996 }
997
998 #[test]
999 fn functor_identity_law() {
1000 let coyo = Coyoneda::<VecBrand, _>::lift(vec![1, 2, 3]);
1002 let result = explicit::map::<CoyonedaBrand<VecBrand>, _, _, _, _>(identity, coyo).lower();
1003 assert_eq!(result, vec![1, 2, 3]);
1004 }
1005
1006 #[test]
1007 fn functor_composition_law() {
1008 let f = |x: i32| x + 1;
1010 let g = |x: i32| x * 2;
1011
1012 let coyo1 = Coyoneda::<VecBrand, _>::lift(vec![1, 2, 3]);
1013 let left =
1014 explicit::map::<CoyonedaBrand<VecBrand>, _, _, _, _>(compose(f, g), coyo1).lower();
1015
1016 let coyo2 = Coyoneda::<VecBrand, _>::lift(vec![1, 2, 3]);
1017 let right = explicit::map::<CoyonedaBrand<VecBrand>, _, _, _, _>(
1018 f,
1019 explicit::map::<CoyonedaBrand<VecBrand>, _, _, _, _>(g, coyo2),
1020 )
1021 .lower();
1022
1023 assert_eq!(left, right);
1024 }
1025
1026 #[test]
1027 fn many_chained_maps() {
1028 let mut coyo = Coyoneda::<VecBrand, _>::lift(vec![0i64]);
1029 for _ in 0 .. 100 {
1030 coyo = coyo.map(|x| x + 1);
1031 }
1032 assert_eq!(coyo.lower(), vec![100i64]);
1033 }
1034
1035 #[test]
1038 fn collapse_preserves_value() {
1039 let coyo =
1040 Coyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x + 1).map(|x| x * 2).collapse();
1041 assert_eq!(coyo.lower(), vec![4, 6, 8]);
1042 }
1043
1044 #[test]
1045 fn collapse_then_map() {
1046 let coyo =
1047 Coyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x + 1).collapse().map(|x| x * 2);
1048 assert_eq!(coyo.lower(), vec![4, 6, 8]);
1049 }
1050
1051 #[test]
1052 fn collapse_periodic_in_loop() {
1053 let mut coyo = Coyoneda::<VecBrand, _>::lift(vec![0i64]);
1054 for i in 0 .. 50 {
1055 coyo = coyo.map(|x| x + 1);
1056 if i % 25 == 24 {
1057 coyo = coyo.collapse();
1058 }
1059 }
1060 assert_eq!(coyo.lower(), vec![50i64]);
1061 }
1062
1063 #[test]
1064 fn map_on_none_stays_none() {
1065 let result =
1066 Coyoneda::<OptionBrand, _>::lift(None::<i32>).map(|x| x + 1).map(|x| x * 2).lower();
1067 assert_eq!(result, None);
1068 }
1069
1070 #[test]
1071 fn map_free_function_dispatches_to_brand() {
1072 let coyo = Coyoneda::<OptionBrand, _>::lift(Some(10));
1073 let result: Coyoneda<OptionBrand, String> =
1074 explicit::map::<CoyonedaBrand<OptionBrand>, _, _, _, _>(|x: i32| x.to_string(), coyo);
1075 assert_eq!(result.lower(), Some("10".to_string()));
1076 }
1077
1078 #[test]
1079 fn lift_lower_roundtrip_preserves_value() {
1080 let original = vec![10, 20, 30];
1081 let roundtrip = Coyoneda::<VecBrand, _>::lift(original.clone()).lower();
1082 assert_eq!(roundtrip, original);
1083 }
1084
1085 #[test]
1088 fn pointed_pure_option() {
1089 let coyo: Coyoneda<OptionBrand, i32> = pure::<CoyonedaBrand<OptionBrand>, _>(42);
1090 assert_eq!(coyo.lower(), Some(42));
1091 }
1092
1093 #[test]
1094 fn pointed_pure_vec() {
1095 let coyo: Coyoneda<VecBrand, i32> = pure::<CoyonedaBrand<VecBrand>, _>(42);
1096 assert_eq!(coyo.lower(), vec![42]);
1097 }
1098
1099 struct VecToOption;
1102 impl NaturalTransformation<VecBrand, OptionBrand> for VecToOption {
1103 fn transform<'a, A: 'a>(
1104 &self,
1105 fa: Vec<A>,
1106 ) -> Option<A> {
1107 fa.into_iter().next()
1108 }
1109 }
1110
1111 #[test]
1112 fn hoist_vec_to_option() {
1113 let coyo = Coyoneda::<VecBrand, _>::lift(vec![10, 20, 30]);
1114 let hoisted: Coyoneda<OptionBrand, i32> = coyo.hoist(VecToOption);
1115 assert_eq!(hoisted.lower(), Some(10));
1116 }
1117
1118 #[test]
1119 fn hoist_with_maps() {
1120 let coyo = Coyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x * 10);
1121 let hoisted = coyo.hoist(VecToOption);
1122 assert_eq!(hoisted.lower(), Some(10));
1123 }
1124
1125 #[test]
1126 fn hoist_then_map() {
1127 let coyo = Coyoneda::<VecBrand, _>::lift(vec![5, 10, 15]);
1128 let hoisted = coyo.hoist(VecToOption).map(|x: i32| x.to_string());
1129 assert_eq!(hoisted.lower(), Some("5".to_string()));
1130 }
1131
1132 #[test]
1135 fn fold_map_on_lifted_vec() {
1136 let coyo = Coyoneda::<VecBrand, _>::lift(vec![1, 2, 3]);
1137 let result = explicit::fold_map::<RcFnBrand, CoyonedaBrand<VecBrand>, _, _, _, _>(
1138 |x: i32| x.to_string(),
1139 coyo,
1140 );
1141 assert_eq!(result, "123".to_string());
1142 }
1143
1144 #[test]
1145 fn fold_map_on_mapped_coyoneda() {
1146 let coyo = Coyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x * 10);
1147 let result = explicit::fold_map::<RcFnBrand, CoyonedaBrand<VecBrand>, _, _, _, _>(
1148 |x: i32| x.to_string(),
1149 coyo,
1150 );
1151 assert_eq!(result, "102030".to_string());
1152 }
1153
1154 #[test]
1155 fn fold_right_on_coyoneda() {
1156 let coyo = Coyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x * 2);
1157 let result = explicit::fold_right::<RcFnBrand, CoyonedaBrand<VecBrand>, _, _, _, _>(
1158 |a: i32, b: i32| a + b,
1159 0,
1160 coyo,
1161 );
1162 assert_eq!(result, 12); }
1164
1165 #[test]
1166 fn fold_left_on_coyoneda() {
1167 let coyo = Coyoneda::<OptionBrand, _>::lift(Some(5)).map(|x| x + 1);
1168 let result = explicit::fold_left::<RcFnBrand, CoyonedaBrand<OptionBrand>, _, _, _, _>(
1169 |acc: i32, a: i32| acc + a,
1170 10,
1171 coyo,
1172 );
1173 assert_eq!(result, 16); }
1175
1176 #[test]
1177 fn fold_map_on_none_is_empty() {
1178 let coyo = Coyoneda::<OptionBrand, i32>::lift(None).map(|x| x + 1);
1179 let result = explicit::fold_map::<RcFnBrand, CoyonedaBrand<OptionBrand>, _, _, _, _>(
1180 |x: i32| x.to_string(),
1181 coyo,
1182 );
1183 assert_eq!(result, String::new());
1184 }
1185
1186 #[test]
1189 fn lift2_option_both_some() {
1190 let a = Coyoneda::<OptionBrand, _>::lift(Some(3));
1191 let b = Coyoneda::<OptionBrand, _>::lift(Some(4));
1192 let result =
1193 explicit::lift2::<CoyonedaBrand<OptionBrand>, _, _, _, _, _, _>(|x, y| x + y, a, b);
1194 assert_eq!(result.lower(), Some(7));
1195 }
1196
1197 #[test]
1198 fn lift2_option_one_none() {
1199 let a = Coyoneda::<OptionBrand, _>::lift(Some(3));
1200 let b = Coyoneda::<OptionBrand, i32>::lift(None);
1201 let result =
1202 explicit::lift2::<CoyonedaBrand<OptionBrand>, _, _, _, _, _, _>(|x, y| x + y, a, b);
1203 assert_eq!(result.lower(), None);
1204 }
1205
1206 #[test]
1207 fn lift2_vec() {
1208 let a = Coyoneda::<VecBrand, _>::lift(vec![1, 2]);
1209 let b = Coyoneda::<VecBrand, _>::lift(vec![10, 20]);
1210 let result =
1211 explicit::lift2::<CoyonedaBrand<VecBrand>, _, _, _, _, _, _>(|x, y| x + y, a, b);
1212 assert_eq!(result.lower(), vec![11, 21, 12, 22]);
1213 }
1214
1215 #[test]
1216 fn lift2_with_prior_maps() {
1217 let a = Coyoneda::<OptionBrand, _>::lift(Some(3)).map(|x| x * 2);
1218 let b = Coyoneda::<OptionBrand, _>::lift(Some(4)).map(|x| x + 1);
1219 let result =
1220 explicit::lift2::<CoyonedaBrand<OptionBrand>, _, _, _, _, _, _>(|x, y| x + y, a, b);
1221 assert_eq!(result.lower(), Some(11)); }
1223
1224 #[test]
1227 fn apply_option_some() {
1228 let ff =
1229 Coyoneda::<OptionBrand, _>::lift(Some(lift_fn_new::<RcFnBrand, _, _>(|x: i32| x * 2)));
1230 let fa = Coyoneda::<OptionBrand, _>::lift(Some(5));
1231 let result = apply::<RcFnBrand, CoyonedaBrand<OptionBrand>, _, _>(ff, fa);
1232 assert_eq!(result.lower(), Some(10));
1233 }
1234
1235 #[test]
1236 fn apply_option_none_fn() {
1237 let ff = Coyoneda::<OptionBrand, _>::lift(None::<<RcFnBrand as CloneFn>::Of<'_, i32, i32>>);
1238 let fa = Coyoneda::<OptionBrand, _>::lift(Some(5));
1239 let result = apply::<RcFnBrand, CoyonedaBrand<OptionBrand>, _, _>(ff, fa);
1240 assert_eq!(result.lower(), None);
1241 }
1242
1243 #[test]
1244 fn apply_vec() {
1245 let ff = Coyoneda::<VecBrand, _>::lift(vec![
1246 lift_fn_new::<RcFnBrand, _, _>(|x: i32| x + 1),
1247 lift_fn_new::<RcFnBrand, _, _>(|x: i32| x * 10),
1248 ]);
1249 let fa = Coyoneda::<VecBrand, _>::lift(vec![2i32, 3]);
1250 let result = apply::<RcFnBrand, CoyonedaBrand<VecBrand>, _, _>(ff, fa);
1251 assert_eq!(result.lower(), vec![3, 4, 20, 30]);
1252 }
1253
1254 #[test]
1257 fn bind_option_some() {
1258 let coyo = Coyoneda::<OptionBrand, _>::lift(Some(5));
1259 let result = explicit::bind::<CoyonedaBrand<OptionBrand>, _, _, _, _>(coyo, |x| {
1260 Coyoneda::<OptionBrand, _>::lift(Some(x * 2))
1261 });
1262 assert_eq!(result.lower(), Some(10));
1263 }
1264
1265 #[test]
1266 fn bind_option_none() {
1267 let coyo = Coyoneda::<OptionBrand, i32>::lift(None);
1268 let result = explicit::bind::<CoyonedaBrand<OptionBrand>, _, _, _, _>(coyo, |x| {
1269 Coyoneda::<OptionBrand, _>::lift(Some(x * 2))
1270 });
1271 assert_eq!(result.lower(), None);
1272 }
1273
1274 #[test]
1275 fn bind_vec() {
1276 let coyo = Coyoneda::<VecBrand, _>::lift(vec![1i32, 2, 3]);
1277 let result = explicit::bind::<CoyonedaBrand<VecBrand>, _, _, _, _>(coyo, |x| {
1278 Coyoneda::<VecBrand, _>::lift(vec![x, x * 10])
1279 });
1280 assert_eq!(result.lower(), vec![1, 10, 2, 20, 3, 30]);
1281 }
1282
1283 #[test]
1284 fn bind_after_map() {
1285 let coyo = Coyoneda::<OptionBrand, _>::lift(Some(3)).map(|x| x * 2);
1286 let result = explicit::bind::<CoyonedaBrand<OptionBrand>, _, _, _, _>(coyo, |x| {
1287 Coyoneda::<OptionBrand, _>::lift(Some(x + 1))
1288 });
1289 assert_eq!(result.lower(), Some(7)); }
1291
1292 #[test]
1295 fn from_coyoneda_to_explicit() {
1296 let coyo = Coyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x + 1);
1297 let explicit: CoyonedaExplicit<VecBrand, i32, i32, fn(i32) -> i32> = coyo.into();
1298 assert_eq!(explicit.lower(), vec![2, 3, 4]);
1299 }
1300
1301 #[test]
1302 fn from_coyoneda_then_map_lower() {
1303 let coyo = Coyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x + 1);
1304 let result = CoyonedaExplicit::<VecBrand, i32, i32, fn(i32) -> i32>::from(coyo)
1305 .map(|x| x * 2)
1306 .lower();
1307 assert_eq!(result, vec![4, 6, 8]);
1308 }
1309
1310 #[test]
1311 fn from_roundtrip_preserves_semantics() {
1312 let original = vec![1, 2, 3];
1313
1314 let explicit = CoyonedaExplicit::<VecBrand, _, _, _>::lift(original.clone()).map(|x| x + 1);
1316 let coyo: Coyoneda<VecBrand, i32> = explicit.into();
1317 let back: CoyonedaExplicit<VecBrand, i32, i32, fn(i32) -> i32> = coyo.into();
1318 assert_eq!(back.lower(), vec![2, 3, 4]);
1319 }
1320
1321 mod property {
1324 use {
1325 crate::{
1326 brands::*,
1327 functions::*,
1328 types::*,
1329 },
1330 quickcheck_macros::quickcheck,
1331 };
1332
1333 #[quickcheck]
1334 fn functor_identity_vec(v: Vec<i32>) -> bool {
1335 let coyo = Coyoneda::<VecBrand, _>::lift(v.clone());
1336 explicit::map::<CoyonedaBrand<VecBrand>, _, _, _, _>(identity, coyo).lower() == v
1337 }
1338
1339 #[quickcheck]
1340 fn functor_identity_option(x: Option<i32>) -> bool {
1341 let coyo = Coyoneda::<OptionBrand, _>::lift(x);
1342 explicit::map::<CoyonedaBrand<OptionBrand>, _, _, _, _>(identity, coyo).lower() == x
1343 }
1344
1345 #[quickcheck]
1346 fn functor_composition_vec(v: Vec<i32>) -> bool {
1347 let f = |x: i32| x.wrapping_add(1);
1348 let g = |x: i32| x.wrapping_mul(2);
1349
1350 let left = explicit::map::<CoyonedaBrand<VecBrand>, _, _, _, _>(
1351 compose(f, g),
1352 Coyoneda::<VecBrand, _>::lift(v.clone()),
1353 )
1354 .lower();
1355
1356 let right = explicit::map::<CoyonedaBrand<VecBrand>, _, _, _, _>(
1357 f,
1358 explicit::map::<CoyonedaBrand<VecBrand>, _, _, _, _>(
1359 g,
1360 Coyoneda::<VecBrand, _>::lift(v),
1361 ),
1362 )
1363 .lower();
1364
1365 left == right
1366 }
1367 }
1368}