1use crate::compose::ComposeF;
2use crate::functor::Functor;
3#[cfg(any(feature = "std", feature = "alloc"))]
4use crate::hkt::EnvF;
5use crate::hkt::{HKT, HKT2, IdentityF};
6
7#[cfg(all(not(feature = "std"), feature = "alloc"))]
8use alloc::rc::Rc;
9#[cfg(feature = "std")]
10use std::rc::Rc;
11
12pub trait Adjunction<F: HKT, U: HKT> {
39 fn unit<A: Clone + 'static>(a: A) -> U::Of<F::Of<A>>;
41
42 fn counit<B: 'static>(fub: F::Of<U::Of<B>>) -> B;
44}
45
46pub fn left_adjunct<Adj, F, U, A, B>(f: impl Fn(F::Of<A>) -> B, a: A) -> U::Of<B>
53where
54 F: HKT,
55 U: Functor,
56 A: Clone + 'static,
57 Adj: Adjunction<F, U>,
58{
59 U::fmap(Adj::unit(a), f)
60}
61
62pub fn right_adjunct<Adj, F, U, A, B>(f: impl Fn(A) -> U::Of<B>, fa: F::Of<A>) -> B
69where
70 F: Functor,
71 U: HKT,
72 B: 'static,
73 Adj: Adjunction<F, U>,
74{
75 Adj::counit(F::fmap(fa, f))
76}
77
78pub struct IdentityAdj;
86
87impl Adjunction<IdentityF, IdentityF> for IdentityAdj {
88 fn unit<A: Clone + 'static>(a: A) -> A {
89 a
90 }
91
92 fn counit<B: 'static>(b: B) -> B {
93 b
94 }
95}
96
97#[cfg(any(feature = "std", feature = "alloc"))]
113pub struct CurryAdj<E>(core::marker::PhantomData<E>);
114
115#[cfg(any(feature = "std", feature = "alloc"))]
116impl<E: Clone + 'static> Adjunction<EnvF<E>, crate::hkt::ReaderF<E>> for CurryAdj<E> {
117 fn unit<A: Clone + 'static>(a: A) -> Box<dyn Fn(E) -> (E, A)> {
118 Box::new(move |e| (e, a.clone()))
119 }
120
121 fn counit<B: 'static>(fub: (E, Box<dyn Fn(E) -> B>)) -> B {
122 let (e, f) = fub;
123 f(e)
124 }
125}
126
127pub fn adjunction_pure<Adj, F, U, A>(a: A) -> U::Of<F::Of<A>>
135where
136 F: HKT,
137 U: HKT,
138 A: Clone + 'static,
139 Adj: Adjunction<F, U>,
140{
141 Adj::unit(a)
142}
143
144#[allow(clippy::type_complexity)]
145pub fn adjunction_join<Adj, F, U, A>(ufufa: U::Of<F::Of<U::Of<F::Of<A>>>>) -> U::Of<F::Of<A>>
153where
154 F: HKT,
155 U: Functor,
156 A: 'static,
157 Adj: Adjunction<F, U>,
158 F::Of<A>: 'static,
159 U::Of<F::Of<A>>: 'static,
160 F::Of<U::Of<F::Of<A>>>: 'static,
161{
162 U::fmap(ufufa, |fufa: F::Of<U::Of<F::Of<A>>>| Adj::counit(fufa))
163}
164
165#[allow(clippy::type_complexity)]
166pub fn adjunction_chain<Adj, F, U, A, B>(
172 ufa: U::Of<F::Of<A>>,
173 f: impl Fn(A) -> U::Of<F::Of<B>> + 'static,
174) -> U::Of<F::Of<B>>
175where
176 F: Functor,
177 U: Functor,
178 A: 'static,
179 B: 'static,
180 Adj: Adjunction<F, U>,
181 F::Of<B>: 'static,
182 U::Of<F::Of<B>>: 'static,
183 F::Of<U::Of<F::Of<B>>>: 'static,
184{
185 let mapped: U::Of<F::Of<U::Of<F::Of<B>>>> = ComposeF::<U, F>::fmap(ufa, f);
186 adjunction_join::<Adj, F, U, B>(mapped)
187}
188
189pub fn adjunction_extract<Adj, F, U, A>(fua: F::Of<U::Of<A>>) -> A
197where
198 F: HKT,
199 U: HKT,
200 A: 'static,
201 Adj: Adjunction<F, U>,
202{
203 Adj::counit(fua)
204}
205
206#[allow(clippy::type_complexity)]
207pub fn adjunction_duplicate<Adj, F, U, A>(fua: F::Of<U::Of<A>>) -> F::Of<U::Of<F::Of<U::Of<A>>>>
215where
216 F: Functor,
217 U: HKT,
218 A: 'static,
219 Adj: Adjunction<F, U>,
220 U::Of<A>: Clone + 'static,
221{
222 F::fmap(fua, |ua: U::Of<A>| Adj::unit(ua))
223}
224
225pub fn adjunction_extend<Adj, F, U, A, B>(
231 fua: F::Of<U::Of<A>>,
232 f: impl Fn(F::Of<U::Of<A>>) -> B + 'static,
233) -> F::Of<U::Of<B>>
234where
235 F: Functor,
236 U: Functor,
237 A: 'static,
238 B: Clone + 'static,
239 Adj: Adjunction<F, U>,
240 U::Of<A>: Clone + 'static,
241 F::Of<U::Of<A>>: 'static,
242{
243 let duplicated = adjunction_duplicate::<Adj, F, U, A>(fua);
244 ComposeF::<F, U>::fmap(duplicated, f)
245}
246
247#[cfg(any(feature = "std", feature = "alloc"))]
260pub fn curry_left_adjunct<E: Clone + 'static, A: Clone + 'static, B: 'static>(
261 f: impl Fn((E, A)) -> B + 'static,
262 a: A,
263) -> Box<dyn Fn(E) -> B> {
264 crate::hkt::ReaderF::<E>::fmap(CurryAdj::<E>::unit(a), f)
265}
266
267#[cfg(any(feature = "std", feature = "alloc"))]
272pub fn curry_right_adjunct<E: Clone + 'static, A: 'static, B: 'static>(
273 f: impl Fn(A) -> Box<dyn Fn(E) -> B> + 'static,
274 pair: (E, A),
275) -> B {
276 CurryAdj::<E>::counit(<EnvF<E> as Functor>::fmap(pair, f))
277}
278
279#[cfg(any(feature = "std", feature = "alloc"))]
285pub fn state_pure<E: Clone + 'static, A: Clone + 'static>(a: A) -> Box<dyn Fn(E) -> (E, A)> {
286 CurryAdj::<E>::unit(a)
287}
288
289#[cfg(any(feature = "std", feature = "alloc"))]
293pub fn state_fmap<E: Clone + 'static, A: 'static, B: 'static>(
294 f: impl Fn(A) -> B + 'static,
295 sa: Box<dyn Fn(E) -> (E, A)>,
296) -> Box<dyn Fn(E) -> (E, B)> {
297 crate::hkt::ReaderF::<E>::fmap(sa, move |(e, a)| (e, f(a)))
298}
299
300#[cfg(any(feature = "std", feature = "alloc"))]
308pub fn state_chain<E: Clone + 'static, A: 'static, B: 'static>(
309 ma: Box<dyn Fn(E) -> (E, A)>,
310 f: impl Fn(A) -> Box<dyn Fn(E) -> (E, B)> + 'static,
311) -> Box<dyn Fn(E) -> (E, B)> {
312 Box::new(move |e| {
313 let (e2, a) = ma(e);
314 f(a)(e2)
315 })
316}
317
318#[cfg(any(feature = "std", feature = "alloc"))]
320pub fn state_get<E: Clone + 'static>() -> Box<dyn Fn(E) -> (E, E)> {
321 Box::new(|e: E| {
322 let e2 = e.clone();
323 (e, e2)
324 })
325}
326
327#[cfg(any(feature = "std", feature = "alloc"))]
329pub fn state_put<E: Clone + 'static>(e: E) -> Box<dyn Fn(E) -> (E, ())> {
330 Box::new(move |_| (e.clone(), ()))
331}
332
333#[cfg(any(feature = "std", feature = "alloc"))]
335pub fn state_modify<E: Clone + 'static>(f: impl Fn(E) -> E + 'static) -> Box<dyn Fn(E) -> (E, ())> {
336 Box::new(move |e| (f(e), ()))
337}
338
339#[cfg(any(feature = "std", feature = "alloc"))]
345pub fn store_extract<E: Clone + 'static, A: 'static>(store: (E, Box<dyn Fn(E) -> A>)) -> A {
346 CurryAdj::<E>::counit(store)
347}
348
349#[cfg(any(feature = "std", feature = "alloc"))]
353pub fn store_extend<E: Clone + 'static, A: 'static, B: 'static>(
354 store: (E, Box<dyn Fn(E) -> A>),
355 f: impl Fn((E, &dyn Fn(E) -> A)) -> B + 'static,
356) -> (E, Box<dyn Fn(E) -> B>) {
357 let pos = store.0.clone();
358 let peek = store.1;
359 let new_peek: Box<dyn Fn(E) -> B> = Box::new(move |e| f((e, peek.as_ref())));
360 (pos, new_peek)
361}
362
363#[cfg(any(feature = "std", feature = "alloc"))]
367pub fn store_peek<E: Clone + 'static, A: 'static>(pos: E, store: &(E, Box<dyn Fn(E) -> A>)) -> A {
368 (store.1)(pos)
369}
370
371#[cfg(any(feature = "std", feature = "alloc"))]
375pub fn store_pos<E: Clone, A>(store: &(E, Box<dyn Fn(E) -> A>)) -> E {
376 store.0.clone()
377}
378
379#[cfg(any(feature = "std", feature = "alloc"))]
392pub struct ContF<R>(core::marker::PhantomData<R>);
393
394#[cfg(any(feature = "std", feature = "alloc"))]
395impl<R: 'static> HKT for ContF<R> {
396 type Of<T> = Box<dyn Fn(T) -> R>;
397}
398
399#[cfg(any(feature = "std", feature = "alloc"))]
400impl<R: 'static> crate::contravariant::Contravariant for ContF<R> {
401 fn contramap<A: 'static, B>(fa: Self::Of<A>, f: impl Fn(B) -> A + 'static) -> Self::Of<B> {
402 Box::new(move |b| fa(f(b)))
403 }
404}
405
406#[cfg(any(feature = "std", feature = "alloc"))]
422pub trait ContravariantAdjunction<F: HKT, G: HKT> {
423 fn unit<A: Clone + 'static>(a: A) -> G::Of<F::Of<A>>;
427
428 fn counit<B: Clone + 'static>(b: B) -> F::Of<G::Of<B>>;
432}
433
434#[cfg(any(feature = "std", feature = "alloc"))]
442pub struct ContAdj<R>(core::marker::PhantomData<R>);
443
444#[cfg(any(feature = "std", feature = "alloc"))]
445impl<R: 'static> ContravariantAdjunction<ContF<R>, ContF<R>> for ContAdj<R> {
446 fn unit<A: Clone + 'static>(a: A) -> Box<dyn Fn(Box<dyn Fn(A) -> R>) -> R> {
447 Box::new(move |k: Box<dyn Fn(A) -> R>| k(a.clone()))
448 }
449
450 fn counit<B: Clone + 'static>(b: B) -> Box<dyn Fn(Box<dyn Fn(B) -> R>) -> R> {
451 Box::new(move |k: Box<dyn Fn(B) -> R>| k(b.clone()))
452 }
453}
454
455#[cfg(any(feature = "std", feature = "alloc"))]
459#[allow(clippy::type_complexity)]
460pub fn cont_pure<R: 'static, A: Clone + 'static>(a: A) -> Box<dyn Fn(Box<dyn Fn(A) -> R>) -> R> {
461 ContAdj::<R>::unit(a)
462}
463
464#[cfg(any(feature = "std", feature = "alloc"))]
468#[allow(clippy::type_complexity)]
469pub fn cont_fmap<R: 'static, A: 'static, B: 'static>(
470 f: impl Fn(A) -> B + 'static,
471 m: Box<dyn Fn(Box<dyn Fn(A) -> R>) -> R>,
472) -> Box<dyn Fn(Box<dyn Fn(B) -> R>) -> R> {
473 let f_rc = Rc::new(f);
474 Box::new(move |k: Box<dyn Fn(B) -> R>| {
475 let f_inner = f_rc.clone();
476 let k_rc = Rc::new(k);
477 let k_composed: Box<dyn Fn(A) -> R> = Box::new(move |a| k_rc(f_inner(a)));
478 m(k_composed)
479 })
480}
481
482#[cfg(any(feature = "std", feature = "alloc"))]
486#[allow(clippy::type_complexity)]
487pub fn cont_chain<R: 'static, A: 'static, B: 'static>(
488 m: Box<dyn Fn(Box<dyn Fn(A) -> R>) -> R>,
489 f: impl Fn(A) -> Box<dyn Fn(Box<dyn Fn(B) -> R>) -> R> + 'static,
490) -> Box<dyn Fn(Box<dyn Fn(B) -> R>) -> R> {
491 let f_rc = Rc::new(f);
492 Box::new(move |k: Box<dyn Fn(B) -> R>| {
493 let k_rc = Rc::new(k);
494 let k_inner = k_rc.clone();
495 let f_inner = f_rc.clone();
496 let inner: Box<dyn Fn(A) -> R> = Box::new(move |a| {
497 let cont_b = f_inner(a);
498 let k_ref = k_inner.clone();
499 let k_box: Box<dyn Fn(B) -> R> = Box::new(move |b| k_ref(b));
500 cont_b(k_box)
501 });
502 m(inner)
503 })
504}
505
506#[cfg(any(feature = "std", feature = "alloc"))]
513#[allow(clippy::type_complexity)]
514pub fn cont_call_cc<R: 'static, A: Clone + 'static, B: 'static>(
515 f: impl Fn(
516 Box<dyn Fn(A) -> Box<dyn Fn(Box<dyn Fn(B) -> R>) -> R>>,
517 ) -> Box<dyn Fn(Box<dyn Fn(A) -> R>) -> R>
518 + 'static,
519) -> Box<dyn Fn(Box<dyn Fn(A) -> R>) -> R> {
520 Box::new(move |k: Box<dyn Fn(A) -> R>| {
521 let k_rc = Rc::new(k);
522 let k_for_escape = k_rc.clone();
523 let escape: Box<dyn Fn(A) -> Box<dyn Fn(Box<dyn Fn(B) -> R>) -> R>> =
524 Box::new(move |a: A| -> Box<dyn Fn(Box<dyn Fn(B) -> R>) -> R> {
525 let k_esc = k_for_escape.clone();
526 Box::new(move |_: Box<dyn Fn(B) -> R>| k_esc(a.clone()))
527 });
528 let k_box: Box<dyn Fn(A) -> R> = Box::new(move |a| k_rc(a));
529 f(escape)(k_box)
530 })
531}
532
533#[cfg(any(feature = "std", feature = "alloc"))]
537pub fn cont_run<R, A>(m: &dyn Fn(Box<dyn Fn(A) -> R>) -> R, k: impl Fn(A) -> R + 'static) -> R {
538 m(Box::new(k))
539}
540
541pub trait ProfunctorFunctor {
554 type Applied<P: HKT2>: HKT2;
556}
557
558pub trait ProfunctorAdjunction<F: ProfunctorFunctor, U: ProfunctorFunctor> {
572 fn unit<P: HKT2, A: 'static, B: 'static>(
574 pab: P::P<A, B>,
575 ) -> <U::Applied<F::Applied<P>> as HKT2>::P<A, B>;
576
577 fn counit<Q: HKT2, A: 'static, B: 'static>(
579 fuqab: <F::Applied<U::Applied<Q>> as HKT2>::P<A, B>,
580 ) -> Q::P<A, B>;
581}
582
583pub struct ProfunctorIdentityF;
585
586impl ProfunctorFunctor for ProfunctorIdentityF {
587 type Applied<P: HKT2> = P;
588}
589
590pub struct ProfunctorIdentityAdj;
594
595impl ProfunctorAdjunction<ProfunctorIdentityF, ProfunctorIdentityF> for ProfunctorIdentityAdj {
596 fn unit<P: HKT2, A: 'static, B: 'static>(pab: P::P<A, B>) -> P::P<A, B> {
597 pab
598 }
599
600 fn counit<Q: HKT2, A: 'static, B: 'static>(fuqab: Q::P<A, B>) -> Q::P<A, B> {
601 fuqab
602 }
603}
604
605#[cfg(test)]
606mod tests {
607 use super::*;
608
609 #[test]
612 fn identity_adj_unit() {
613 let result = IdentityAdj::unit(42);
614 assert_eq!(result, 42);
615 }
616
617 #[test]
618 fn identity_adj_counit() {
619 let result = IdentityAdj::counit(42);
620 assert_eq!(result, 42);
621 }
622
623 #[test]
624 fn identity_adj_left_adjunct() {
625 let f = |x: i32| x + 1;
626 let result = left_adjunct::<IdentityAdj, IdentityF, IdentityF, _, _>(f, 5);
627 assert_eq!(result, 6);
628 }
629
630 #[test]
631 fn identity_adj_right_adjunct() {
632 let f = |x: i32| x * 2;
633 let result = right_adjunct::<IdentityAdj, IdentityF, IdentityF, _, _>(f, 5);
634 assert_eq!(result, 10);
635 }
636
637 #[test]
638 fn identity_adj_monad_pure() {
639 let result = adjunction_pure::<IdentityAdj, IdentityF, IdentityF, i32>(42);
640 assert_eq!(result, 42);
641 }
642
643 #[test]
644 fn identity_adj_monad_join() {
645 let result = adjunction_join::<IdentityAdj, IdentityF, IdentityF, i32>(42);
646 assert_eq!(result, 42);
647 }
648
649 #[test]
650 fn identity_adj_monad_chain() {
651 let result = adjunction_chain::<IdentityAdj, IdentityF, IdentityF, i32, i32>(5, |x| x + 1);
652 assert_eq!(result, 6);
653 }
654
655 #[test]
656 fn identity_adj_comonad_extract() {
657 let result = adjunction_extract::<IdentityAdj, IdentityF, IdentityF, i32>(42);
658 assert_eq!(result, 42);
659 }
660
661 #[test]
662 fn identity_adj_comonad_duplicate() {
663 let result = adjunction_duplicate::<IdentityAdj, IdentityF, IdentityF, i32>(42);
664 assert_eq!(result, 42);
665 }
666
667 #[cfg(any(feature = "std", feature = "alloc"))]
670 mod curry_adj_tests {
671 use super::*;
672
673 #[test]
674 fn curry_adj_unit() {
675 let reader = CurryAdj::<i32>::unit(42i32);
676 assert_eq!(reader(10), (10, 42));
677 assert_eq!(reader(0), (0, 42));
678 }
679
680 #[test]
681 fn curry_adj_counit() {
682 let env_reader: (i32, Box<dyn Fn(i32) -> String>) =
683 (5, Box::new(|e| format!("env={}", e)));
684 let result = CurryAdj::<i32>::counit(env_reader);
685 assert_eq!(result, "env=5");
686 }
687
688 #[test]
689 fn curry_adj_monad_pure() {
690 let state_fn = state_pure::<i32, String>("hello".to_string());
691 assert_eq!(state_fn(42), (42, "hello".to_string()));
692 }
693
694 #[test]
695 fn curry_adj_state_chain() {
696 let get_and_inc = state_chain(
698 state_get::<i32>(),
699 |old: i32| -> Box<dyn Fn(i32) -> (i32, i32)> { Box::new(move |e| (e + 1, old)) },
700 );
701 assert_eq!(get_and_inc(10), (11, 10));
702 assert_eq!(get_and_inc(0), (1, 0));
703 }
704
705 #[test]
706 fn curry_adj_state_get() {
707 let getter = state_get::<i32>();
708 assert_eq!(getter(42), (42, 42));
709 }
710
711 #[test]
712 fn curry_adj_state_put() {
713 let putter = state_put(99i32);
714 assert_eq!(putter(0), (99, ()));
715 assert_eq!(putter(42), (99, ()));
716 }
717
718 #[test]
719 fn curry_adj_store_extract() {
720 let store: (i32, Box<dyn Fn(i32) -> String>) = (5, Box::new(|e| format!("val={}", e)));
721 let result = store_extract(store);
722 assert_eq!(result, "val=5");
723 }
724
725 #[test]
726 fn curry_adj_store_extend() {
727 let store: (i32, Box<dyn Fn(i32) -> i32>) = (3, Box::new(|e| e * 2));
728 let extended = store_extend(store, |(pos, peek)| peek(pos) + 1);
729 assert_eq!(extended.0, 3); assert_eq!((extended.1)(3), 7); assert_eq!((extended.1)(5), 11); }
733
734 #[test]
735 fn curry_adj_state_monad_left_identity() {
736 let a = 42i32;
738 let f = |x: i32| -> Box<dyn Fn(i32) -> (i32, i32)> { Box::new(move |e| (e, x + 1)) };
739 let chained = state_chain(state_pure(a), f);
740 let direct = f(a);
741 for e in [0, 1, 10, -5] {
742 assert_eq!(chained(e), direct(e));
743 }
744 }
745
746 #[test]
747 fn curry_adj_state_monad_right_identity() {
748 let m_fn = |e: i32| (e + 1, e * 2);
750 let chained = state_chain(
751 Box::new(move |e: i32| (e + 1, e * 2)) as Box<dyn Fn(i32) -> (i32, i32)>,
752 |a| state_pure(a),
753 );
754 for e in [0, 1, 10, -5] {
755 assert_eq!(chained(e), m_fn(e));
756 }
757 }
758
759 #[test]
762 fn reader_fmap() {
763 let reader: Box<dyn Fn(i32) -> i32> = Box::new(|e| e * 2);
764 let mapped = crate::hkt::ReaderF::<i32>::fmap(reader, |x| x + 1);
765 assert_eq!(mapped(5), 11); }
767
768 #[test]
769 fn reader_pure() {
770 let reader = crate::hkt::ReaderF::<i32>::pure(42);
771 assert_eq!(reader(0), 42);
772 assert_eq!(reader(999), 42);
773 }
774
775 #[test]
776 fn reader_chain() {
777 let reader: Box<dyn Fn(i32) -> i32> = Box::new(|e| e + 1);
778 let chained = crate::hkt::ReaderF::<i32>::chain(reader, |a| {
779 Box::new(move |e| a * e) as Box<dyn Fn(i32) -> i32>
780 });
781 assert_eq!(chained(5), 30);
783 }
784
785 #[test]
786 fn reader_ask() {
787 let reader = crate::hkt::ReaderF::<String>::ask();
788 assert_eq!(reader("hello".to_string()), "hello".to_string());
789 }
790
791 #[test]
792 fn reader_local() {
793 let reader: Box<dyn Fn(i32) -> i32> = Box::new(|e| e * 2);
794 let localized = crate::hkt::ReaderF::<i32>::local(|e| e + 10, reader);
795 assert_eq!(localized(5), 30); }
797
798 #[test]
801 fn curry_left_adjunct_test() {
802 let f = |pair: (i32, i32)| pair.0 + pair.1;
803 let reader = curry_left_adjunct(f, 10i32);
804 assert_eq!(reader(5), 15); assert_eq!(reader(3), 13); }
807
808 #[test]
809 fn curry_right_adjunct_test() {
810 let f = |a: i32| -> Box<dyn Fn(i32) -> i32> { Box::new(move |e| e * a) };
811 let result = curry_right_adjunct(f, (3i32, 5i32));
812 assert_eq!(result, 15); }
814
815 #[test]
818 fn state_fmap_test() {
819 let sa: Box<dyn Fn(i32) -> (i32, i32)> = Box::new(|e| (e + 1, e * 2));
820 let mapped = state_fmap(|x| x + 100, sa);
821 assert_eq!(mapped(5), (6, 110)); }
823
824 #[test]
825 fn state_modify_test() {
826 let modifier = state_modify(|e: i32| e + 10);
827 assert_eq!(modifier(5), (15, ()));
828 }
829
830 #[test]
833 fn store_peek_test() {
834 let store: (i32, Box<dyn Fn(i32) -> String>) = (3, Box::new(|e| format!("v{}", e)));
835 assert_eq!(store_peek(3, &store), "v3");
836 assert_eq!(store_peek(7, &store), "v7");
837 }
838
839 #[test]
840 fn store_pos_test() {
841 let store: (i32, Box<dyn Fn(i32) -> i32>) = (42, Box::new(|e| e * 2));
842 assert_eq!(store_pos(&store), 42);
843 }
844 }
845}
846
847#[cfg(test)]
848mod law_tests {
849 use super::*;
850 use proptest::prelude::*;
851
852 proptest! {
853 #[test]
856 fn identity_adj_triangle_1(x in any::<i32>()) {
857 let result = right_adjunct::<IdentityAdj, IdentityF, IdentityF, _, _>(
858 IdentityAdj::unit, x,
859 );
860 prop_assert_eq!(result, x);
861 }
862
863 #[test]
866 fn identity_adj_triangle_2(x in any::<i32>()) {
867 let result = left_adjunct::<IdentityAdj, IdentityF, IdentityF, _, _>(
868 IdentityAdj::counit, x,
869 );
870 prop_assert_eq!(result, x);
871 }
872
873 #[test]
875 fn identity_adj_adjuncts_inverse(x in any::<i16>()) {
876 let f = |a: i16| a.wrapping_add(1);
877 let la = left_adjunct::<IdentityAdj, IdentityF, IdentityF, _, _>(f, x);
878 prop_assert_eq!(la, f(x));
879
880 let g = |a: i16| a.wrapping_mul(2);
881 let ra = right_adjunct::<IdentityAdj, IdentityF, IdentityF, _, _>(g, x);
882 prop_assert_eq!(ra, g(x));
883 }
884
885 #[test]
888 fn identity_adj_monad_left_identity(x in any::<i32>()) {
889 let f = |a: i32| a.wrapping_add(1);
890 let pure_x = adjunction_pure::<IdentityAdj, IdentityF, IdentityF, i32>(x);
891 let result = adjunction_chain::<IdentityAdj, IdentityF, IdentityF, i32, i32>(
892 pure_x, f,
893 );
894 prop_assert_eq!(result, f(x));
895 }
896
897 #[test]
899 fn identity_adj_monad_right_identity(x in any::<i32>()) {
900 let result = adjunction_chain::<IdentityAdj, IdentityF, IdentityF, i32, i32>(
901 x,
902 adjunction_pure::<IdentityAdj, IdentityF, IdentityF, i32>,
903 );
904 prop_assert_eq!(result, x);
905 }
906
907 #[test]
910 fn identity_adj_comonad_extract_duplicate(x in any::<i32>()) {
911 let dup = adjunction_duplicate::<IdentityAdj, IdentityF, IdentityF, i32>(x);
912 let result = adjunction_extract::<IdentityAdj, IdentityF, IdentityF, i32>(dup);
913 prop_assert_eq!(result, x);
914 }
915 }
916
917 #[cfg(any(feature = "std", feature = "alloc"))]
918 mod curry_adj_law_tests {
919 use super::*;
920
921 proptest! {
922 #[test]
925 fn curry_adj_triangle_counit_fmap_unit(e in -100i32..100, a in -100i32..100) {
926 let mapped = <EnvF<i32> as crate::functor::Functor>::fmap(
927 (e, a),
928 |x: i32| -> Box<dyn Fn(i32) -> (i32, i32)> {
929 CurryAdj::<i32>::unit(x)
930 },
931 );
932 let result = CurryAdj::<i32>::counit(mapped);
933 prop_assert_eq!(result, (e, a));
934 }
935
936 #[test]
938 fn curry_adj_state_left_identity(a in -100i32..100, e in -100i32..100) {
939 let f = |x: i32| -> Box<dyn Fn(i32) -> (i32, i32)> {
940 Box::new(move |env| (env, x.wrapping_add(1)))
941 };
942 let chained = state_chain(state_pure(a), f);
943 let expected = f(a);
944 prop_assert_eq!(chained(e), expected(e));
945 }
946
947 #[test]
949 fn curry_adj_state_right_identity(e in -100i32..100) {
950 let m_fn = move |env: i32| (env.wrapping_add(1), env.wrapping_mul(2));
951 let chained = state_chain(
952 Box::new(m_fn) as Box<dyn Fn(i32) -> (i32, i32)>,
953 |a: i32| state_pure(a),
954 );
955 prop_assert_eq!(chained(e), m_fn(e));
956 }
957
958 #[test]
960 fn curry_adj_state_associativity(e in -100i32..100) {
961 let _m: Box<dyn Fn(i32) -> (i32, i32)> =
962 Box::new(|env| (env, 10));
963 let f = |x: i32| -> Box<dyn Fn(i32) -> (i32, i32)> {
964 Box::new(move |env| (env.wrapping_add(1), x.wrapping_add(1)))
965 };
966 let g = |x: i32| -> Box<dyn Fn(i32) -> (i32, i32)> {
967 Box::new(move |env| (env, x.wrapping_mul(2)))
968 };
969
970 let left = state_chain(state_chain(
971 Box::new(|env: i32| (env, 10)) as Box<dyn Fn(i32) -> (i32, i32)>,
972 f,
973 ), g);
974 let right = state_chain(
975 Box::new(|env: i32| (env, 10)) as Box<dyn Fn(i32) -> (i32, i32)>,
976 move |a: i32| {
977 let inner_f = |x: i32| -> Box<dyn Fn(i32) -> (i32, i32)> {
978 Box::new(move |env| (env.wrapping_add(1), x.wrapping_add(1)))
979 };
980 let inner_g = |x: i32| -> Box<dyn Fn(i32) -> (i32, i32)> {
981 Box::new(move |env| (env, x.wrapping_mul(2)))
982 };
983 state_chain(inner_f(a), inner_g)
984 },
985 );
986 prop_assert_eq!(left(e), right(e));
987 }
988
989 #[test]
991 fn curry_adj_store_extract_law(pos in -100i32..100) {
992 let store: (i32, Box<dyn Fn(i32) -> i32>) =
993 (pos, Box::new(|e| e.wrapping_mul(2)));
994 let result = store_extract(store);
995 prop_assert_eq!(result, pos.wrapping_mul(2));
996 }
997 }
998 }
999
1000 #[cfg(any(feature = "std", feature = "alloc"))]
1003 mod contravariant_adj_tests {
1004 use super::*;
1005
1006 #[test]
1007 fn cont_adj_unit() {
1008 let k: Box<dyn Fn(i32) -> i32> = Box::new(|x| x + 1);
1009 let m = ContAdj::<i32>::unit(42);
1010 assert_eq!(m(k), 43);
1011 }
1012
1013 #[test]
1014 fn cont_adj_counit() {
1015 let k: Box<dyn Fn(i32) -> i32> = Box::new(|x| x * 2);
1016 let m = ContAdj::<i32>::counit(10);
1017 assert_eq!(m(k), 20);
1018 }
1019
1020 #[test]
1021 fn cont_adj_self_adjoint() {
1022 let k: Box<dyn Fn(i32) -> String> = Box::new(|x| format!("{}", x));
1024 let unit_result = ContAdj::<String>::unit(42);
1025 let counit_result = ContAdj::<String>::counit(42);
1026 let k2: Box<dyn Fn(i32) -> String> = Box::new(|x| format!("{}", x));
1027 assert_eq!(unit_result(k), counit_result(k2));
1028 }
1029
1030 #[test]
1031 fn cont_pure_test() {
1032 let m = cont_pure::<i32, _>(42);
1033 assert_eq!(cont_run(&*m, |x| x + 1), 43);
1034 }
1035
1036 #[test]
1037 fn cont_fmap_test() {
1038 let m = cont_pure::<i32, _>(10);
1039 let mapped = cont_fmap(|x: i32| x * 3, m);
1040 assert_eq!(cont_run(&*mapped, |x| x + 1), 31); }
1042
1043 #[test]
1044 fn cont_chain_test() {
1045 let m = cont_pure::<i32, _>(5);
1046 let chained = cont_chain(m, |x: i32| cont_pure(x + 10));
1047 assert_eq!(cont_run(&*chained, |x| x * 2), 30); }
1049
1050 #[test]
1051 fn cont_chain_sequencing() {
1052 let m1 = cont_pure::<i32, _>(3);
1054 let m2 = cont_chain(m1, |x| {
1055 let doubled = x * 2; cont_chain(cont_pure(doubled), |y| cont_pure(y + 1)) });
1058 assert_eq!(cont_run(&*m2, |x| x), 7);
1059 }
1060
1061 #[test]
1062 fn cont_call_cc_no_escape() {
1063 let m = cont_call_cc::<i32, i32, i32>(|_escape| cont_pure(42));
1065 assert_eq!(cont_run(&*m, |x| x), 42);
1066 }
1067
1068 #[test]
1069 fn cont_call_cc_with_escape() {
1070 let m = cont_call_cc::<i32, i32, i32>(|escape| {
1072 let escaped = escape(10);
1074 cont_chain(escaped, |_| cont_pure(999))
1076 });
1077 assert_eq!(cont_run(&*m, |x| x), 10);
1078 }
1079
1080 #[test]
1081 fn contf_contramap() {
1082 use crate::contravariant::Contravariant;
1083 let f: Box<dyn Fn(i32) -> bool> = Box::new(|x| x > 0);
1084 let g = ContF::<bool>::contramap(f, |s: &str| s.len() as i32);
1085 assert!(g("hello"));
1086 assert!(!g(""));
1087 }
1088 }
1089
1090 #[cfg(any(feature = "std", feature = "alloc"))]
1091 mod contravariant_adj_law_tests {
1092 use super::*;
1093 use proptest::prelude::*;
1094
1095 proptest! {
1096 #[test]
1098 fn cont_monad_left_identity(a in -100i32..100) {
1099 let f = |x: i32| -> Box<dyn Fn(Box<dyn Fn(i32) -> i32>) -> i32> {
1100 cont_pure(x.wrapping_add(1))
1101 };
1102 let chained = cont_chain(cont_pure(a), f);
1103 let expected = f(a);
1104 prop_assert_eq!(
1106 cont_run(&*chained, |x| x),
1107 cont_run(&*expected, |x| x)
1108 );
1109 }
1110
1111 #[test]
1113 fn cont_monad_right_identity(a in -100i32..100) {
1114 let m = cont_pure::<i32, _>(a);
1115 let chained = cont_chain(m, |x: i32| cont_pure(x));
1116 let m2 = cont_pure::<i32, _>(a);
1117 prop_assert_eq!(
1118 cont_run(&*chained, |x| x),
1119 cont_run(&*m2, |x| x)
1120 );
1121 }
1122
1123 #[test]
1125 fn cont_functor_identity(a in -100i32..100) {
1126 let m = cont_pure::<i32, _>(a);
1127 let mapped = cont_fmap(|x: i32| x, m);
1128 let m2 = cont_pure::<i32, _>(a);
1129 prop_assert_eq!(
1130 cont_run(&*mapped, |x| x),
1131 cont_run(&*m2, |x| x)
1132 );
1133 }
1134
1135 #[test]
1137 fn cont_functor_composition(a in -100i32..100) {
1138 let f = |x: i32| x.wrapping_add(1);
1139 let g = |x: i32| x.wrapping_mul(2);
1140
1141 let m1 = cont_pure::<i32, _>(a);
1142 let left = cont_fmap(move |x| g(f(x)), m1);
1143
1144 let m2 = cont_pure::<i32, _>(a);
1145 let right = cont_fmap(g, cont_fmap(f, m2));
1146
1147 prop_assert_eq!(
1148 cont_run(&*left, |x| x),
1149 cont_run(&*right, |x| x)
1150 );
1151 }
1152 }
1153 }
1154
1155 mod profunctor_adj_tests {
1158 use super::*;
1159 use crate::hkt::TupleF;
1160
1161 #[test]
1162 fn profunctor_identity_adj_unit() {
1163 let val: (i32, String) = (42, "hello".to_string());
1164 let result = ProfunctorIdentityAdj::unit::<TupleF, i32, String>(val.clone());
1165 assert_eq!(result, val);
1166 }
1167
1168 #[test]
1169 fn profunctor_identity_adj_counit() {
1170 let val: (i32, String) = (42, "hello".to_string());
1171 let result = ProfunctorIdentityAdj::counit::<TupleF, i32, String>(val.clone());
1172 assert_eq!(result, val);
1173 }
1174
1175 #[test]
1176 fn profunctor_identity_adj_roundtrip() {
1177 let val: (i32, i32) = (1, 2);
1179 let result =
1180 ProfunctorIdentityAdj::counit::<TupleF, i32, i32>(ProfunctorIdentityAdj::unit::<
1181 TupleF,
1182 i32,
1183 i32,
1184 >(val));
1185 assert_eq!(result, (1, 2));
1186 }
1187 }
1188
1189 mod profunctor_adj_law_tests {
1190 use super::*;
1191 use crate::hkt::TupleF;
1192 use proptest::prelude::*;
1193
1194 proptest! {
1195 #[test]
1197 fn profunctor_identity_adj_triangle(a in any::<i32>(), b in any::<i32>()) {
1198 let val: (i32, i32) = (a, b);
1199 let result = ProfunctorIdentityAdj::counit::<TupleF, i32, i32>(
1200 ProfunctorIdentityAdj::unit::<TupleF, i32, i32>(val),
1201 );
1202 prop_assert_eq!(result, (a, b));
1203 }
1204 }
1205 }
1206}