fp_library/types/
endofunction.rs

1//! Implementations for [`Endofunction`], a wrapper for endofunctions (functions from a set to the same set) that enables monoidal operations.
2
3use core::fmt;
4use std::{
5	fmt::{Debug, Formatter},
6	hash::Hash,
7	marker::PhantomData,
8};
9
10use crate::{
11	classes::{
12		Category, ClonableFn, Monoid, Semigroup, clonable_fn::ApplyFn, monoid::Monoid1L0T,
13		semigroup::Semigroup1L0T,
14	},
15	functions::{compose, identity},
16	hkt::Kind1L0T,
17};
18
19/// A wrapper for endofunctions (functions from a set to the same set) that enables monoidal operations.
20///
21/// `Endofunction a` represents a function `a -> a`.
22///
23/// It exists to provide a monoid instance where:
24///
25/// * The binary operation [append][Semigroup::append] is [function composition][crate::functions::compose].
26/// * The identity element [empty][Monoid::empty] is the [identity function][crate::functions::identity].
27///
28/// The wrapped function can be accessed directly via the [`.0` field][Endofunction#structfield.0].
29///
30/// # Examples
31///
32/// ```
33/// use fp_library::{
34///     brands::{EndofunctionBrand, RcFnBrand},
35///     functions::{append, empty},
36///     classes::ClonableFn,
37///     types::Endofunction,
38/// };
39/// use std::rc::Rc;
40///
41/// // Create Endofunctions
42/// let f = Endofunction(<RcFnBrand as ClonableFn>::new(|x: i32| x * 2));
43/// let g = Endofunction(<RcFnBrand as ClonableFn>::new(|x: i32| x + 1));
44///
45/// // Compose functions (f after g)
46/// let fg = append::<RcFnBrand, EndofunctionBrand<RcFnBrand, i32>>(f)(g);
47/// assert_eq!(fg.0(3), 8); // double(increment(3)) = 8
48///
49/// // Identity element
50/// let id = empty::<EndofunctionBrand<RcFnBrand, i32>>();
51/// assert_eq!(id.0(42), 42);
52/// ```
53pub struct Endofunction<'a, ClonableFnBrand: ClonableFn, A: 'a>(
54	pub ApplyFn<'a, ClonableFnBrand, A, A>,
55);
56
57impl<'a, ClonableFnBrand: ClonableFn, A> Endofunction<'a, ClonableFnBrand, A> {
58	pub fn new(a: ApplyFn<'a, ClonableFnBrand, A, A>) -> Self {
59		Self(a)
60	}
61}
62
63impl<'a, ClonableFnBrand: ClonableFn, A> Clone for Endofunction<'a, ClonableFnBrand, A> {
64	fn clone(&self) -> Self {
65		Endofunction(self.0.clone())
66	}
67}
68
69impl<'a, ClonableFnBrand: ClonableFn, A> Debug for Endofunction<'a, ClonableFnBrand, A>
70where
71	ApplyFn<'a, ClonableFnBrand, A, A>: Debug,
72{
73	fn fmt(
74		&self,
75		fmt: &mut Formatter<'_>,
76	) -> fmt::Result {
77		fmt.debug_tuple("Endofunction").field(&self.0).finish()
78	}
79}
80
81impl<'a, ClonableFnBrand: ClonableFn, A> Eq for Endofunction<'a, ClonableFnBrand, A> where
82	ApplyFn<'a, ClonableFnBrand, A, A>: Eq
83{
84}
85
86impl<'a, ClonableFnBrand: ClonableFn, A> Hash for Endofunction<'a, ClonableFnBrand, A>
87where
88	ApplyFn<'a, ClonableFnBrand, A, A>: Hash,
89{
90	fn hash<H: std::hash::Hasher>(
91		&self,
92		state: &mut H,
93	) {
94		self.0.hash(state);
95	}
96}
97
98impl<'a, ClonableFnBrand: ClonableFn, A> Ord for Endofunction<'a, ClonableFnBrand, A>
99where
100	ApplyFn<'a, ClonableFnBrand, A, A>: Ord,
101{
102	fn cmp(
103		&self,
104		other: &Self,
105	) -> std::cmp::Ordering {
106		self.0.cmp(&other.0)
107	}
108}
109
110impl<'a, ClonableFnBrand: ClonableFn, A> PartialEq for Endofunction<'a, ClonableFnBrand, A>
111where
112	ApplyFn<'a, ClonableFnBrand, A, A>: PartialEq,
113{
114	fn eq(
115		&self,
116		other: &Self,
117	) -> bool {
118		self.0 == other.0
119	}
120}
121
122impl<'a, ClonableFnBrand: ClonableFn, A> PartialOrd for Endofunction<'a, ClonableFnBrand, A>
123where
124	ApplyFn<'a, ClonableFnBrand, A, A>: PartialOrd,
125{
126	fn partial_cmp(
127		&self,
128		other: &Self,
129	) -> Option<std::cmp::Ordering> {
130		self.0.partial_cmp(&other.0)
131	}
132}
133
134impl<'b, ClonableFnBrand: 'b + ClonableFn, A> Semigroup<'b>
135	for Endofunction<'b, ClonableFnBrand, A>
136{
137	/// # Examples
138	///
139	/// ```
140	/// use fp_library::{
141	///     brands::{EndofunctionBrand, RcFnBrand},
142	///     functions::append,
143	///     classes::ClonableFn,
144	///     types::Endofunction,
145	/// };
146	/// use std::rc::Rc;
147	///
148	/// let double = <RcFnBrand as ClonableFn>::new(|x: i32| x * 2);
149	/// let increment = <RcFnBrand as ClonableFn>::new(|x: i32| x + 1);
150	///
151	/// assert_eq!(
152	///     (append::<RcFnBrand, EndofunctionBrand<RcFnBrand, i32>>(Endofunction(double))(Endofunction(increment.clone()))).0(3),
153	///     8
154	/// );
155	/// assert_eq!(
156	///     (append::<RcFnBrand, EndofunctionBrand<RcFnBrand, i32>>(Endofunction(increment.clone()))(Endofunction(increment))).0(3),
157	///     5
158	/// );
159	/// ```
160	fn append<'a, CFB: 'a + 'b + ClonableFn>(a: Self) -> ApplyFn<'a, CFB, Self, Self>
161	where
162		Self: Sized,
163		'b: 'a,
164	{
165		CFB::new(move |b: Self| {
166			Endofunction(compose::<'b, ClonableFnBrand, _, _, _>(a.0.clone())(b.0))
167		})
168	}
169}
170
171impl<'a, ClonableFnBrand: 'a + ClonableFn, A> Monoid<'a> for Endofunction<'a, ClonableFnBrand, A> {
172	/// # Examples
173	///
174	/// ```
175	/// use fp_library::{
176	///     brands::{EndofunctionBrand, RcFnBrand},
177	///     functions::empty,
178	///     types::Endofunction,
179	/// };
180	///
181	/// assert_eq!(empty::<EndofunctionBrand<RcFnBrand, i32>>().0(5), 5);
182	/// assert_eq!(empty::<EndofunctionBrand<RcFnBrand, String>>().0("test".to_string()), "test");
183	/// ```
184	fn empty() -> Self {
185		Endofunction(ClonableFnBrand::new(identity))
186	}
187}
188
189#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
190pub struct EndofunctionBrand<CategoryBrand: Category, A>(PhantomData<(CategoryBrand, A)>);
191
192impl<ClonableFnBrand: ClonableFn, A: 'static> Kind1L0T for EndofunctionBrand<ClonableFnBrand, A> {
193	type Output<'a> = Endofunction<'a, ClonableFnBrand, A>;
194}
195
196impl<ClonableFnBrand: 'static + ClonableFn, A: 'static> Semigroup1L0T
197	for EndofunctionBrand<ClonableFnBrand, A>
198where
199	for<'a> ApplyFn<'a, ClonableFnBrand, A, A>: Clone,
200{
201}
202
203impl<ClonableFnBrand: 'static + ClonableFn, A: 'static> Monoid1L0T
204	for EndofunctionBrand<ClonableFnBrand, A>
205where
206	for<'a> ApplyFn<'a, ClonableFnBrand, A, A>: Clone,
207{
208}