1use crate::compose::ComposeF;
5use crate::functor::Functor;
6#[cfg(any(feature = "std", feature = "alloc"))]
7use crate::hkt::EnvF;
8use crate::hkt::{HKT, HKT2, IdentityF};
9
10#[cfg(all(not(feature = "std"), feature = "alloc"))]
11use alloc::{boxed::Box, rc::Rc};
12#[cfg(feature = "std")]
13use std::rc::Rc;
14
15pub trait Adjunction<F: HKT, U: HKT> {
42 fn unit<A: Clone + 'static>(a: A) -> U::Of<F::Of<A>>;
44
45 fn counit<B: 'static>(fub: F::Of<U::Of<B>>) -> B;
47}
48
49pub fn left_adjunct<Adj, F, U, A, B>(f: impl Fn(F::Of<A>) -> B, a: A) -> U::Of<B>
56where
57 F: HKT,
58 U: Functor,
59 A: Clone + 'static,
60 Adj: Adjunction<F, U>,
61{
62 U::fmap(Adj::unit(a), f)
63}
64
65pub fn right_adjunct<Adj, F, U, A, B>(f: impl Fn(A) -> U::Of<B>, fa: F::Of<A>) -> B
72where
73 F: Functor,
74 U: HKT,
75 B: 'static,
76 Adj: Adjunction<F, U>,
77{
78 Adj::counit(F::fmap(fa, f))
79}
80
81pub struct IdentityAdj;
89
90impl Adjunction<IdentityF, IdentityF> for IdentityAdj {
91 fn unit<A: Clone + 'static>(a: A) -> A {
92 a
93 }
94
95 fn counit<B: 'static>(b: B) -> B {
96 b
97 }
98}
99
100#[cfg(any(feature = "std", feature = "alloc"))]
116pub struct CurryAdj<E>(core::marker::PhantomData<E>);
117
118#[cfg(any(feature = "std", feature = "alloc"))]
119impl<E: Clone + 'static> Adjunction<EnvF<E>, crate::hkt::ReaderF<E>> for CurryAdj<E> {
120 fn unit<A: Clone + 'static>(a: A) -> Box<dyn Fn(E) -> (E, A)> {
121 Box::new(move |e| (e, a.clone()))
122 }
123
124 fn counit<B: 'static>(fub: (E, Box<dyn Fn(E) -> B>)) -> B {
125 let (e, f) = fub;
126 f(e)
127 }
128}
129
130pub fn adjunction_pure<Adj, F, U, A>(a: A) -> U::Of<F::Of<A>>
138where
139 F: HKT,
140 U: HKT,
141 A: Clone + 'static,
142 Adj: Adjunction<F, U>,
143{
144 Adj::unit(a)
145}
146
147#[allow(clippy::type_complexity)]
148pub fn adjunction_join<Adj, F, U, A>(ufufa: U::Of<F::Of<U::Of<F::Of<A>>>>) -> U::Of<F::Of<A>>
156where
157 F: HKT,
158 U: Functor,
159 A: 'static,
160 Adj: Adjunction<F, U>,
161 F::Of<A>: 'static,
162 U::Of<F::Of<A>>: 'static,
163 F::Of<U::Of<F::Of<A>>>: 'static,
164{
165 U::fmap(ufufa, |fufa: F::Of<U::Of<F::Of<A>>>| Adj::counit(fufa))
166}
167
168#[allow(clippy::type_complexity)]
169pub fn adjunction_chain<Adj, F, U, A, B>(
175 ufa: U::Of<F::Of<A>>,
176 f: impl Fn(A) -> U::Of<F::Of<B>> + 'static,
177) -> U::Of<F::Of<B>>
178where
179 F: Functor,
180 U: Functor,
181 A: 'static,
182 B: 'static,
183 Adj: Adjunction<F, U>,
184 F::Of<B>: 'static,
185 U::Of<F::Of<B>>: 'static,
186 F::Of<U::Of<F::Of<B>>>: 'static,
187{
188 let mapped: U::Of<F::Of<U::Of<F::Of<B>>>> = ComposeF::<U, F>::fmap(ufa, f);
189 adjunction_join::<Adj, F, U, B>(mapped)
190}
191
192pub fn adjunction_extract<Adj, F, U, A>(fua: F::Of<U::Of<A>>) -> A
200where
201 F: HKT,
202 U: HKT,
203 A: 'static,
204 Adj: Adjunction<F, U>,
205{
206 Adj::counit(fua)
207}
208
209#[allow(clippy::type_complexity)]
210pub fn adjunction_duplicate<Adj, F, U, A>(fua: F::Of<U::Of<A>>) -> F::Of<U::Of<F::Of<U::Of<A>>>>
218where
219 F: Functor,
220 U: HKT,
221 A: 'static,
222 Adj: Adjunction<F, U>,
223 U::Of<A>: Clone + 'static,
224{
225 F::fmap(fua, |ua: U::Of<A>| Adj::unit(ua))
226}
227
228pub fn adjunction_extend<Adj, F, U, A, B>(
234 fua: F::Of<U::Of<A>>,
235 f: impl Fn(F::Of<U::Of<A>>) -> B + 'static,
236) -> F::Of<U::Of<B>>
237where
238 F: Functor,
239 U: Functor,
240 A: 'static,
241 B: Clone + 'static,
242 Adj: Adjunction<F, U>,
243 U::Of<A>: Clone + 'static,
244 F::Of<U::Of<A>>: 'static,
245{
246 let duplicated = adjunction_duplicate::<Adj, F, U, A>(fua);
247 ComposeF::<F, U>::fmap(duplicated, f)
248}
249
250#[cfg(any(feature = "std", feature = "alloc"))]
263pub fn curry_left_adjunct<E: Clone + 'static, A: Clone + 'static, B: 'static>(
264 f: impl Fn((E, A)) -> B + 'static,
265 a: A,
266) -> Box<dyn Fn(E) -> B> {
267 crate::hkt::ReaderF::<E>::fmap(CurryAdj::<E>::unit(a), f)
268}
269
270#[cfg(any(feature = "std", feature = "alloc"))]
275pub fn curry_right_adjunct<E: Clone + 'static, A: 'static, B: 'static>(
276 f: impl Fn(A) -> Box<dyn Fn(E) -> B> + 'static,
277 pair: (E, A),
278) -> B {
279 CurryAdj::<E>::counit(<EnvF<E> as Functor>::fmap(pair, f))
280}
281
282#[cfg(any(feature = "std", feature = "alloc"))]
288pub fn state_pure<E: Clone + 'static, A: Clone + 'static>(a: A) -> Box<dyn Fn(E) -> (E, A)> {
289 CurryAdj::<E>::unit(a)
290}
291
292#[cfg(any(feature = "std", feature = "alloc"))]
296pub fn state_fmap<E: Clone + 'static, A: 'static, B: 'static>(
297 f: impl Fn(A) -> B + 'static,
298 sa: Box<dyn Fn(E) -> (E, A)>,
299) -> Box<dyn Fn(E) -> (E, B)> {
300 crate::hkt::ReaderF::<E>::fmap(sa, move |(e, a)| (e, f(a)))
301}
302
303#[cfg(any(feature = "std", feature = "alloc"))]
311pub fn state_chain<E: Clone + 'static, A: 'static, B: 'static>(
312 ma: Box<dyn Fn(E) -> (E, A)>,
313 f: impl Fn(A) -> Box<dyn Fn(E) -> (E, B)> + 'static,
314) -> Box<dyn Fn(E) -> (E, B)> {
315 Box::new(move |e| {
316 let (e2, a) = ma(e);
317 f(a)(e2)
318 })
319}
320
321#[cfg(any(feature = "std", feature = "alloc"))]
323pub fn state_get<E: Clone + 'static>() -> Box<dyn Fn(E) -> (E, E)> {
324 Box::new(|e: E| {
325 let e2 = e.clone();
326 (e, e2)
327 })
328}
329
330#[cfg(any(feature = "std", feature = "alloc"))]
332pub fn state_put<E: Clone + 'static>(e: E) -> Box<dyn Fn(E) -> (E, ())> {
333 Box::new(move |_| (e.clone(), ()))
334}
335
336#[cfg(any(feature = "std", feature = "alloc"))]
338pub fn state_modify<E: Clone + 'static>(f: impl Fn(E) -> E + 'static) -> Box<dyn Fn(E) -> (E, ())> {
339 Box::new(move |e| (f(e), ()))
340}
341
342#[cfg(any(feature = "std", feature = "alloc"))]
348pub fn store_extract<E: Clone + 'static, A: 'static>(store: (E, Box<dyn Fn(E) -> A>)) -> A {
349 CurryAdj::<E>::counit(store)
350}
351
352#[cfg(any(feature = "std", feature = "alloc"))]
356pub fn store_extend<E: Clone + 'static, A: 'static, B: 'static>(
357 store: (E, Box<dyn Fn(E) -> A>),
358 f: impl Fn((E, &dyn Fn(E) -> A)) -> B + 'static,
359) -> (E, Box<dyn Fn(E) -> B>) {
360 let pos = store.0.clone();
361 let peek = store.1;
362 let new_peek: Box<dyn Fn(E) -> B> = Box::new(move |e| f((e, peek.as_ref())));
363 (pos, new_peek)
364}
365
366#[cfg(any(feature = "std", feature = "alloc"))]
370pub fn store_peek<E: Clone + 'static, A: 'static>(pos: E, store: &(E, Box<dyn Fn(E) -> A>)) -> A {
371 (store.1)(pos)
372}
373
374#[cfg(any(feature = "std", feature = "alloc"))]
378pub fn store_pos<E: Clone, A>(store: &(E, Box<dyn Fn(E) -> A>)) -> E {
379 store.0.clone()
380}
381
382#[cfg(any(feature = "std", feature = "alloc"))]
395pub struct ContF<R>(core::marker::PhantomData<R>);
396
397#[cfg(any(feature = "std", feature = "alloc"))]
398impl<R: 'static> HKT for ContF<R> {
399 type Of<T> = Box<dyn Fn(T) -> R>;
400}
401
402#[cfg(any(feature = "std", feature = "alloc"))]
403impl<R: 'static> crate::contravariant::Contravariant for ContF<R> {
404 fn contramap<A: 'static, B>(fa: Self::Of<A>, f: impl Fn(B) -> A + 'static) -> Self::Of<B> {
405 Box::new(move |b| fa(f(b)))
406 }
407}
408
409#[cfg(any(feature = "std", feature = "alloc"))]
425pub trait ContravariantAdjunction<F: HKT, G: HKT> {
426 fn unit<A: Clone + 'static>(a: A) -> G::Of<F::Of<A>>;
430
431 fn counit<B: Clone + 'static>(b: B) -> F::Of<G::Of<B>>;
435}
436
437#[cfg(any(feature = "std", feature = "alloc"))]
445pub struct ContAdj<R>(core::marker::PhantomData<R>);
446
447#[cfg(any(feature = "std", feature = "alloc"))]
448impl<R: 'static> ContravariantAdjunction<ContF<R>, ContF<R>> for ContAdj<R> {
449 fn unit<A: Clone + 'static>(a: A) -> Box<dyn Fn(Box<dyn Fn(A) -> R>) -> R> {
450 Box::new(move |k: Box<dyn Fn(A) -> R>| k(a.clone()))
451 }
452
453 fn counit<B: Clone + 'static>(b: B) -> Box<dyn Fn(Box<dyn Fn(B) -> R>) -> R> {
454 Box::new(move |k: Box<dyn Fn(B) -> R>| k(b.clone()))
455 }
456}
457
458#[cfg(any(feature = "std", feature = "alloc"))]
462#[allow(clippy::type_complexity)]
463pub fn cont_pure<R: 'static, A: Clone + 'static>(a: A) -> Box<dyn Fn(Box<dyn Fn(A) -> R>) -> R> {
464 ContAdj::<R>::unit(a)
465}
466
467#[cfg(any(feature = "std", feature = "alloc"))]
471#[allow(clippy::type_complexity)]
472pub fn cont_fmap<R: 'static, A: 'static, B: 'static>(
473 f: impl Fn(A) -> B + 'static,
474 m: Box<dyn Fn(Box<dyn Fn(A) -> R>) -> R>,
475) -> Box<dyn Fn(Box<dyn Fn(B) -> R>) -> R> {
476 let f_rc = Rc::new(f);
477 Box::new(move |k: Box<dyn Fn(B) -> R>| {
478 let f_inner = f_rc.clone();
479 let k_rc = Rc::new(k);
480 let k_composed: Box<dyn Fn(A) -> R> = Box::new(move |a| k_rc(f_inner(a)));
481 m(k_composed)
482 })
483}
484
485#[cfg(any(feature = "std", feature = "alloc"))]
489#[allow(clippy::type_complexity)]
490pub fn cont_chain<R: 'static, A: 'static, B: 'static>(
491 m: Box<dyn Fn(Box<dyn Fn(A) -> R>) -> R>,
492 f: impl Fn(A) -> Box<dyn Fn(Box<dyn Fn(B) -> R>) -> R> + 'static,
493) -> Box<dyn Fn(Box<dyn Fn(B) -> R>) -> R> {
494 let f_rc = Rc::new(f);
495 Box::new(move |k: Box<dyn Fn(B) -> R>| {
496 let k_rc = Rc::new(k);
497 let k_inner = k_rc.clone();
498 let f_inner = f_rc.clone();
499 let inner: Box<dyn Fn(A) -> R> = Box::new(move |a| {
500 let cont_b = f_inner(a);
501 let k_ref = k_inner.clone();
502 let k_box: Box<dyn Fn(B) -> R> = Box::new(move |b| k_ref(b));
503 cont_b(k_box)
504 });
505 m(inner)
506 })
507}
508
509#[cfg(any(feature = "std", feature = "alloc"))]
516#[allow(clippy::type_complexity)]
517pub fn cont_call_cc<R: 'static, A: Clone + 'static, B: 'static>(
518 f: impl Fn(
519 Box<dyn Fn(A) -> Box<dyn Fn(Box<dyn Fn(B) -> R>) -> R>>,
520 ) -> Box<dyn Fn(Box<dyn Fn(A) -> R>) -> R>
521 + 'static,
522) -> Box<dyn Fn(Box<dyn Fn(A) -> R>) -> R> {
523 Box::new(move |k: Box<dyn Fn(A) -> R>| {
524 let k_rc = Rc::new(k);
525 let k_for_escape = k_rc.clone();
526 let escape: Box<dyn Fn(A) -> Box<dyn Fn(Box<dyn Fn(B) -> R>) -> R>> =
527 Box::new(move |a: A| -> Box<dyn Fn(Box<dyn Fn(B) -> R>) -> R> {
528 let k_esc = k_for_escape.clone();
529 Box::new(move |_: Box<dyn Fn(B) -> R>| k_esc(a.clone()))
530 });
531 let k_box: Box<dyn Fn(A) -> R> = Box::new(move |a| k_rc(a));
532 f(escape)(k_box)
533 })
534}
535
536#[cfg(any(feature = "std", feature = "alloc"))]
540pub fn cont_run<R, A>(m: &dyn Fn(Box<dyn Fn(A) -> R>) -> R, k: impl Fn(A) -> R + 'static) -> R {
541 m(Box::new(k))
542}
543
544pub trait ProfunctorFunctor {
557 type Applied<P: HKT2>: HKT2;
559}
560
561pub trait ProfunctorAdjunction<F: ProfunctorFunctor, U: ProfunctorFunctor> {
575 fn unit<P: HKT2, A: 'static, B: 'static>(
577 pab: P::P<A, B>,
578 ) -> <U::Applied<F::Applied<P>> as HKT2>::P<A, B>;
579
580 fn counit<Q: HKT2, A: 'static, B: 'static>(
582 fuqab: <F::Applied<U::Applied<Q>> as HKT2>::P<A, B>,
583 ) -> Q::P<A, B>;
584}
585
586pub struct ProfunctorIdentityF;
588
589impl ProfunctorFunctor for ProfunctorIdentityF {
590 type Applied<P: HKT2> = P;
591}
592
593pub struct ProfunctorIdentityAdj;
597
598impl ProfunctorAdjunction<ProfunctorIdentityF, ProfunctorIdentityF> for ProfunctorIdentityAdj {
599 fn unit<P: HKT2, A: 'static, B: 'static>(pab: P::P<A, B>) -> P::P<A, B> {
600 pab
601 }
602
603 fn counit<Q: HKT2, A: 'static, B: 'static>(fuqab: Q::P<A, B>) -> Q::P<A, B> {
604 fuqab
605 }
606}
607
608#[cfg(test)]
609mod tests {
610 use super::*;
611
612 #[test]
615 fn identity_adj_unit() {
616 let result = IdentityAdj::unit(42);
617 assert_eq!(result, 42);
618 }
619
620 #[test]
621 fn identity_adj_counit() {
622 let result = IdentityAdj::counit(42);
623 assert_eq!(result, 42);
624 }
625
626 #[test]
627 fn identity_adj_left_adjunct() {
628 let f = |x: i32| x + 1;
629 let result = left_adjunct::<IdentityAdj, IdentityF, IdentityF, _, _>(f, 5);
630 assert_eq!(result, 6);
631 }
632
633 #[test]
634 fn identity_adj_right_adjunct() {
635 let f = |x: i32| x * 2;
636 let result = right_adjunct::<IdentityAdj, IdentityF, IdentityF, _, _>(f, 5);
637 assert_eq!(result, 10);
638 }
639
640 #[test]
641 fn identity_adj_monad_pure() {
642 let result = adjunction_pure::<IdentityAdj, IdentityF, IdentityF, i32>(42);
643 assert_eq!(result, 42);
644 }
645
646 #[test]
647 fn identity_adj_monad_join() {
648 let result = adjunction_join::<IdentityAdj, IdentityF, IdentityF, i32>(42);
649 assert_eq!(result, 42);
650 }
651
652 #[test]
653 fn identity_adj_monad_chain() {
654 let result = adjunction_chain::<IdentityAdj, IdentityF, IdentityF, i32, i32>(5, |x| x + 1);
655 assert_eq!(result, 6);
656 }
657
658 #[test]
659 fn identity_adj_comonad_extract() {
660 let result = adjunction_extract::<IdentityAdj, IdentityF, IdentityF, i32>(42);
661 assert_eq!(result, 42);
662 }
663
664 #[test]
665 fn identity_adj_comonad_duplicate() {
666 let result = adjunction_duplicate::<IdentityAdj, IdentityF, IdentityF, i32>(42);
667 assert_eq!(result, 42);
668 }
669
670 #[cfg(any(feature = "std", feature = "alloc"))]
673 mod curry_adj_tests {
674 use super::*;
675
676 #[test]
677 fn curry_adj_unit() {
678 let reader = CurryAdj::<i32>::unit(42i32);
679 assert_eq!(reader(10), (10, 42));
680 assert_eq!(reader(0), (0, 42));
681 }
682
683 #[test]
684 fn curry_adj_counit() {
685 let env_reader: (i32, Box<dyn Fn(i32) -> String>) =
686 (5, Box::new(|e| format!("env={}", e)));
687 let result = CurryAdj::<i32>::counit(env_reader);
688 assert_eq!(result, "env=5");
689 }
690
691 #[test]
692 fn curry_adj_monad_pure() {
693 let state_fn = state_pure::<i32, String>("hello".to_string());
694 assert_eq!(state_fn(42), (42, "hello".to_string()));
695 }
696
697 #[test]
698 fn curry_adj_state_chain() {
699 let get_and_inc = state_chain(
701 state_get::<i32>(),
702 |old: i32| -> Box<dyn Fn(i32) -> (i32, i32)> { Box::new(move |e| (e + 1, old)) },
703 );
704 assert_eq!(get_and_inc(10), (11, 10));
705 assert_eq!(get_and_inc(0), (1, 0));
706 }
707
708 #[test]
709 fn curry_adj_state_get() {
710 let getter = state_get::<i32>();
711 assert_eq!(getter(42), (42, 42));
712 }
713
714 #[test]
715 fn curry_adj_state_put() {
716 let putter = state_put(99i32);
717 assert_eq!(putter(0), (99, ()));
718 assert_eq!(putter(42), (99, ()));
719 }
720
721 #[test]
722 fn curry_adj_store_extract() {
723 let store: (i32, Box<dyn Fn(i32) -> String>) = (5, Box::new(|e| format!("val={}", e)));
724 let result = store_extract(store);
725 assert_eq!(result, "val=5");
726 }
727
728 #[test]
729 fn curry_adj_store_extend() {
730 let store: (i32, Box<dyn Fn(i32) -> i32>) = (3, Box::new(|e| e * 2));
731 let extended = store_extend(store, |(pos, peek)| peek(pos) + 1);
732 assert_eq!(extended.0, 3); assert_eq!((extended.1)(3), 7); assert_eq!((extended.1)(5), 11); }
736
737 #[test]
738 fn curry_adj_state_monad_left_identity() {
739 let a = 42i32;
741 let f = |x: i32| -> Box<dyn Fn(i32) -> (i32, i32)> { Box::new(move |e| (e, x + 1)) };
742 let chained = state_chain(state_pure(a), f);
743 let direct = f(a);
744 for e in [0, 1, 10, -5] {
745 assert_eq!(chained(e), direct(e));
746 }
747 }
748
749 #[test]
750 fn curry_adj_state_monad_right_identity() {
751 let m_fn = |e: i32| (e + 1, e * 2);
753 let chained = state_chain(
754 Box::new(move |e: i32| (e + 1, e * 2)) as Box<dyn Fn(i32) -> (i32, i32)>,
755 |a| state_pure(a),
756 );
757 for e in [0, 1, 10, -5] {
758 assert_eq!(chained(e), m_fn(e));
759 }
760 }
761
762 #[test]
765 fn reader_fmap() {
766 let reader: Box<dyn Fn(i32) -> i32> = Box::new(|e| e * 2);
767 let mapped = crate::hkt::ReaderF::<i32>::fmap(reader, |x| x + 1);
768 assert_eq!(mapped(5), 11); }
770
771 #[test]
772 fn reader_pure() {
773 let reader = crate::hkt::ReaderF::<i32>::pure(42);
774 assert_eq!(reader(0), 42);
775 assert_eq!(reader(999), 42);
776 }
777
778 #[test]
779 fn reader_chain() {
780 let reader: Box<dyn Fn(i32) -> i32> = Box::new(|e| e + 1);
781 let chained = crate::hkt::ReaderF::<i32>::chain(reader, |a| {
782 Box::new(move |e| a * e) as Box<dyn Fn(i32) -> i32>
783 });
784 assert_eq!(chained(5), 30);
786 }
787
788 #[test]
789 fn reader_ask() {
790 let reader = crate::hkt::ReaderF::<String>::ask();
791 assert_eq!(reader("hello".to_string()), "hello".to_string());
792 }
793
794 #[test]
795 fn reader_local() {
796 let reader: Box<dyn Fn(i32) -> i32> = Box::new(|e| e * 2);
797 let localized = crate::hkt::ReaderF::<i32>::local(|e| e + 10, reader);
798 assert_eq!(localized(5), 30); }
800
801 #[test]
804 fn curry_left_adjunct_test() {
805 let f = |pair: (i32, i32)| pair.0 + pair.1;
806 let reader = curry_left_adjunct(f, 10i32);
807 assert_eq!(reader(5), 15); assert_eq!(reader(3), 13); }
810
811 #[test]
812 fn curry_right_adjunct_test() {
813 let f = |a: i32| -> Box<dyn Fn(i32) -> i32> { Box::new(move |e| e * a) };
814 let result = curry_right_adjunct(f, (3i32, 5i32));
815 assert_eq!(result, 15); }
817
818 #[test]
821 fn state_fmap_test() {
822 let sa: Box<dyn Fn(i32) -> (i32, i32)> = Box::new(|e| (e + 1, e * 2));
823 let mapped = state_fmap(|x| x + 100, sa);
824 assert_eq!(mapped(5), (6, 110)); }
826
827 #[test]
828 fn state_modify_test() {
829 let modifier = state_modify(|e: i32| e + 10);
830 assert_eq!(modifier(5), (15, ()));
831 }
832
833 #[test]
836 fn store_peek_test() {
837 let store: (i32, Box<dyn Fn(i32) -> String>) = (3, Box::new(|e| format!("v{}", e)));
838 assert_eq!(store_peek(3, &store), "v3");
839 assert_eq!(store_peek(7, &store), "v7");
840 }
841
842 #[test]
843 fn store_pos_test() {
844 let store: (i32, Box<dyn Fn(i32) -> i32>) = (42, Box::new(|e| e * 2));
845 assert_eq!(store_pos(&store), 42);
846 }
847 }
848}
849
850#[cfg(test)]
851mod law_tests {
852 use super::*;
853 use proptest::prelude::*;
854
855 proptest! {
856 #[test]
859 fn identity_adj_triangle_1(x in any::<i32>()) {
860 let result = right_adjunct::<IdentityAdj, IdentityF, IdentityF, _, _>(
861 IdentityAdj::unit, x,
862 );
863 prop_assert_eq!(result, x);
864 }
865
866 #[test]
869 fn identity_adj_triangle_2(x in any::<i32>()) {
870 let result = left_adjunct::<IdentityAdj, IdentityF, IdentityF, _, _>(
871 IdentityAdj::counit, x,
872 );
873 prop_assert_eq!(result, x);
874 }
875
876 #[test]
878 fn identity_adj_adjuncts_inverse(x in any::<i16>()) {
879 let f = |a: i16| a.wrapping_add(1);
880 let la = left_adjunct::<IdentityAdj, IdentityF, IdentityF, _, _>(f, x);
881 prop_assert_eq!(la, f(x));
882
883 let g = |a: i16| a.wrapping_mul(2);
884 let ra = right_adjunct::<IdentityAdj, IdentityF, IdentityF, _, _>(g, x);
885 prop_assert_eq!(ra, g(x));
886 }
887
888 #[test]
891 fn identity_adj_monad_left_identity(x in any::<i32>()) {
892 let f = |a: i32| a.wrapping_add(1);
893 let pure_x = adjunction_pure::<IdentityAdj, IdentityF, IdentityF, i32>(x);
894 let result = adjunction_chain::<IdentityAdj, IdentityF, IdentityF, i32, i32>(
895 pure_x, f,
896 );
897 prop_assert_eq!(result, f(x));
898 }
899
900 #[test]
902 fn identity_adj_monad_right_identity(x in any::<i32>()) {
903 let result = adjunction_chain::<IdentityAdj, IdentityF, IdentityF, i32, i32>(
904 x,
905 adjunction_pure::<IdentityAdj, IdentityF, IdentityF, i32>,
906 );
907 prop_assert_eq!(result, x);
908 }
909
910 #[test]
913 fn identity_adj_comonad_extract_duplicate(x in any::<i32>()) {
914 let dup = adjunction_duplicate::<IdentityAdj, IdentityF, IdentityF, i32>(x);
915 let result = adjunction_extract::<IdentityAdj, IdentityF, IdentityF, i32>(dup);
916 prop_assert_eq!(result, x);
917 }
918 }
919
920 #[cfg(any(feature = "std", feature = "alloc"))]
921 mod curry_adj_law_tests {
922 use super::*;
923
924 proptest! {
925 #[test]
928 fn curry_adj_triangle_counit_fmap_unit(e in -100i32..100, a in -100i32..100) {
929 let mapped = <EnvF<i32> as crate::functor::Functor>::fmap(
930 (e, a),
931 |x: i32| -> Box<dyn Fn(i32) -> (i32, i32)> {
932 CurryAdj::<i32>::unit(x)
933 },
934 );
935 let result = CurryAdj::<i32>::counit(mapped);
936 prop_assert_eq!(result, (e, a));
937 }
938
939 #[test]
941 fn curry_adj_state_left_identity(a in -100i32..100, e in -100i32..100) {
942 let f = |x: i32| -> Box<dyn Fn(i32) -> (i32, i32)> {
943 Box::new(move |env| (env, x.wrapping_add(1)))
944 };
945 let chained = state_chain(state_pure(a), f);
946 let expected = f(a);
947 prop_assert_eq!(chained(e), expected(e));
948 }
949
950 #[test]
952 fn curry_adj_state_right_identity(e in -100i32..100) {
953 let m_fn = move |env: i32| (env.wrapping_add(1), env.wrapping_mul(2));
954 let chained = state_chain(
955 Box::new(m_fn) as Box<dyn Fn(i32) -> (i32, i32)>,
956 |a: i32| state_pure(a),
957 );
958 prop_assert_eq!(chained(e), m_fn(e));
959 }
960
961 #[test]
963 fn curry_adj_state_associativity(e in -100i32..100) {
964 let _m: Box<dyn Fn(i32) -> (i32, i32)> =
965 Box::new(|env| (env, 10));
966 let f = |x: i32| -> Box<dyn Fn(i32) -> (i32, i32)> {
967 Box::new(move |env| (env.wrapping_add(1), x.wrapping_add(1)))
968 };
969 let g = |x: i32| -> Box<dyn Fn(i32) -> (i32, i32)> {
970 Box::new(move |env| (env, x.wrapping_mul(2)))
971 };
972
973 let left = state_chain(state_chain(
974 Box::new(|env: i32| (env, 10)) as Box<dyn Fn(i32) -> (i32, i32)>,
975 f,
976 ), g);
977 let right = state_chain(
978 Box::new(|env: i32| (env, 10)) as Box<dyn Fn(i32) -> (i32, i32)>,
979 move |a: i32| {
980 let inner_f = |x: i32| -> Box<dyn Fn(i32) -> (i32, i32)> {
981 Box::new(move |env| (env.wrapping_add(1), x.wrapping_add(1)))
982 };
983 let inner_g = |x: i32| -> Box<dyn Fn(i32) -> (i32, i32)> {
984 Box::new(move |env| (env, x.wrapping_mul(2)))
985 };
986 state_chain(inner_f(a), inner_g)
987 },
988 );
989 prop_assert_eq!(left(e), right(e));
990 }
991
992 #[test]
994 fn curry_adj_store_extract_law(pos in -100i32..100) {
995 let store: (i32, Box<dyn Fn(i32) -> i32>) =
996 (pos, Box::new(|e| e.wrapping_mul(2)));
997 let result = store_extract(store);
998 prop_assert_eq!(result, pos.wrapping_mul(2));
999 }
1000 }
1001 }
1002
1003 #[cfg(any(feature = "std", feature = "alloc"))]
1006 mod contravariant_adj_tests {
1007 use super::*;
1008
1009 #[test]
1010 fn cont_adj_unit() {
1011 let k: Box<dyn Fn(i32) -> i32> = Box::new(|x| x + 1);
1012 let m = ContAdj::<i32>::unit(42);
1013 assert_eq!(m(k), 43);
1014 }
1015
1016 #[test]
1017 fn cont_adj_counit() {
1018 let k: Box<dyn Fn(i32) -> i32> = Box::new(|x| x * 2);
1019 let m = ContAdj::<i32>::counit(10);
1020 assert_eq!(m(k), 20);
1021 }
1022
1023 #[test]
1024 fn cont_adj_self_adjoint() {
1025 let k: Box<dyn Fn(i32) -> String> = Box::new(|x| format!("{}", x));
1027 let unit_result = ContAdj::<String>::unit(42);
1028 let counit_result = ContAdj::<String>::counit(42);
1029 let k2: Box<dyn Fn(i32) -> String> = Box::new(|x| format!("{}", x));
1030 assert_eq!(unit_result(k), counit_result(k2));
1031 }
1032
1033 #[test]
1034 fn cont_pure_test() {
1035 let m = cont_pure::<i32, _>(42);
1036 assert_eq!(cont_run(&*m, |x| x + 1), 43);
1037 }
1038
1039 #[test]
1040 fn cont_fmap_test() {
1041 let m = cont_pure::<i32, _>(10);
1042 let mapped = cont_fmap(|x: i32| x * 3, m);
1043 assert_eq!(cont_run(&*mapped, |x| x + 1), 31); }
1045
1046 #[test]
1047 fn cont_chain_test() {
1048 let m = cont_pure::<i32, _>(5);
1049 let chained = cont_chain(m, |x: i32| cont_pure(x + 10));
1050 assert_eq!(cont_run(&*chained, |x| x * 2), 30); }
1052
1053 #[test]
1054 fn cont_chain_sequencing() {
1055 let m1 = cont_pure::<i32, _>(3);
1057 let m2 = cont_chain(m1, |x| {
1058 let doubled = x * 2; cont_chain(cont_pure(doubled), |y| cont_pure(y + 1)) });
1061 assert_eq!(cont_run(&*m2, |x| x), 7);
1062 }
1063
1064 #[test]
1065 fn cont_call_cc_no_escape() {
1066 let m = cont_call_cc::<i32, i32, i32>(|_escape| cont_pure(42));
1068 assert_eq!(cont_run(&*m, |x| x), 42);
1069 }
1070
1071 #[test]
1072 fn cont_call_cc_with_escape() {
1073 let m = cont_call_cc::<i32, i32, i32>(|escape| {
1075 let escaped = escape(10);
1077 cont_chain(escaped, |_| cont_pure(999))
1079 });
1080 assert_eq!(cont_run(&*m, |x| x), 10);
1081 }
1082
1083 #[test]
1084 fn contf_contramap() {
1085 use crate::contravariant::Contravariant;
1086 let f: Box<dyn Fn(i32) -> bool> = Box::new(|x| x > 0);
1087 let g = ContF::<bool>::contramap(f, |s: &str| s.len() as i32);
1088 assert!(g("hello"));
1089 assert!(!g(""));
1090 }
1091 }
1092
1093 #[cfg(any(feature = "std", feature = "alloc"))]
1094 mod contravariant_adj_law_tests {
1095 use super::*;
1096 use proptest::prelude::*;
1097
1098 proptest! {
1099 #[test]
1101 fn cont_monad_left_identity(a in -100i32..100) {
1102 let f = |x: i32| -> Box<dyn Fn(Box<dyn Fn(i32) -> i32>) -> i32> {
1103 cont_pure(x.wrapping_add(1))
1104 };
1105 let chained = cont_chain(cont_pure(a), f);
1106 let expected = f(a);
1107 prop_assert_eq!(
1109 cont_run(&*chained, |x| x),
1110 cont_run(&*expected, |x| x)
1111 );
1112 }
1113
1114 #[test]
1116 fn cont_monad_right_identity(a in -100i32..100) {
1117 let m = cont_pure::<i32, _>(a);
1118 let chained = cont_chain(m, |x: i32| cont_pure(x));
1119 let m2 = cont_pure::<i32, _>(a);
1120 prop_assert_eq!(
1121 cont_run(&*chained, |x| x),
1122 cont_run(&*m2, |x| x)
1123 );
1124 }
1125
1126 #[test]
1128 fn cont_functor_identity(a in -100i32..100) {
1129 let m = cont_pure::<i32, _>(a);
1130 let mapped = cont_fmap(|x: i32| x, m);
1131 let m2 = cont_pure::<i32, _>(a);
1132 prop_assert_eq!(
1133 cont_run(&*mapped, |x| x),
1134 cont_run(&*m2, |x| x)
1135 );
1136 }
1137
1138 #[test]
1140 fn cont_functor_composition(a in -100i32..100) {
1141 let f = |x: i32| x.wrapping_add(1);
1142 let g = |x: i32| x.wrapping_mul(2);
1143
1144 let m1 = cont_pure::<i32, _>(a);
1145 let left = cont_fmap(move |x| g(f(x)), m1);
1146
1147 let m2 = cont_pure::<i32, _>(a);
1148 let right = cont_fmap(g, cont_fmap(f, m2));
1149
1150 prop_assert_eq!(
1151 cont_run(&*left, |x| x),
1152 cont_run(&*right, |x| x)
1153 );
1154 }
1155 }
1156 }
1157
1158 mod profunctor_adj_tests {
1161 use super::*;
1162 use crate::hkt::TupleF;
1163
1164 #[test]
1165 fn profunctor_identity_adj_unit() {
1166 let val: (i32, String) = (42, "hello".to_string());
1167 let result = ProfunctorIdentityAdj::unit::<TupleF, i32, String>(val.clone());
1168 assert_eq!(result, val);
1169 }
1170
1171 #[test]
1172 fn profunctor_identity_adj_counit() {
1173 let val: (i32, String) = (42, "hello".to_string());
1174 let result = ProfunctorIdentityAdj::counit::<TupleF, i32, String>(val.clone());
1175 assert_eq!(result, val);
1176 }
1177
1178 #[test]
1179 fn profunctor_identity_adj_roundtrip() {
1180 let val: (i32, i32) = (1, 2);
1182 let result =
1183 ProfunctorIdentityAdj::counit::<TupleF, i32, i32>(ProfunctorIdentityAdj::unit::<
1184 TupleF,
1185 i32,
1186 i32,
1187 >(val));
1188 assert_eq!(result, (1, 2));
1189 }
1190 }
1191
1192 mod profunctor_adj_law_tests {
1193 use super::*;
1194 use crate::hkt::TupleF;
1195 use proptest::prelude::*;
1196
1197 proptest! {
1198 #[test]
1200 fn profunctor_identity_adj_triangle(a in any::<i32>(), b in any::<i32>()) {
1201 let val: (i32, i32) = (a, b);
1202 let result = ProfunctorIdentityAdj::counit::<TupleF, i32, i32>(
1203 ProfunctorIdentityAdj::unit::<TupleF, i32, i32>(val),
1204 );
1205 prop_assert_eq!(result, (a, b));
1206 }
1207 }
1208 }
1209}