Skip to main content

fp_library/types/
const_val.rs

1//! The `Const` functor, which ignores its second type parameter.
2
3#[fp_macros::document_module]
4mod inner {
5	use {
6		crate::{
7			Apply,
8			brands::ConstBrand,
9			classes::{
10				apply_first::ApplyFirst,
11				apply_second::ApplySecond,
12				clone_fn::CloneFn,
13				functor::Functor,
14				lift::Lift,
15				monoid::Monoid,
16				pointed::Pointed,
17				semiapplicative::Semiapplicative,
18				semigroup::Semigroup,
19			},
20			impl_kind,
21			kinds::*,
22		},
23		fp_macros::*,
24		std::marker::PhantomData,
25	};
26
27	/// The `Const` functor.
28	///
29	/// `Const<R, A>` stores a value of type `R` and ignores the type `A`.
30	#[document_type_parameters(
31		"The lifetime of the values.",
32		"The stored type.",
33		"The ignored type."
34	)]
35	#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
36	#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
37	pub struct Const<'a, R, A>(pub R, pub PhantomData<&'a A>);
38
39	#[document_type_parameters(
40		"The lifetime of the values.",
41		"The stored type.",
42		"The ignored type."
43	)]
44	#[document_parameters("The `Const` instance.")]
45	impl<'a, R, A> Const<'a, R, A> {
46		/// Creates a new `Const` instance.
47		#[document_signature]
48		#[document_parameters("The value to store.")]
49		#[document_returns("A new `Const` instance.")]
50		#[document_examples]
51		///
52		/// ```
53		/// use fp_library::types::const_val::Const;
54		///
55		/// let c: Const<i32, String> = Const::new(42);
56		/// assert_eq!(c.0, 42);
57		/// ```
58		pub fn new(r: R) -> Self {
59			Const(r, PhantomData)
60		}
61
62		/// Maps over the phantom type parameter, preserving the stored value.
63		///
64		/// Since `Const` ignores its second type parameter, the function is never called.
65		/// This is the inherent method form of [`Functor::map`](crate::classes::functor::Functor::map).
66		#[document_signature]
67		#[document_type_parameters("The new phantom type.")]
68		#[document_parameters("The function to map (ignored).")]
69		#[document_returns(
70			"A new `Const` instance with the same stored value but a different phantom type."
71		)]
72		#[document_examples]
73		///
74		/// ```
75		/// use fp_library::types::const_val::Const;
76		///
77		/// let c: Const<i32, String> = Const::new(42);
78		/// let mapped: Const<i32, bool> = c.map(|s: String| s.is_empty());
79		/// assert_eq!(mapped.0, 42);
80		/// ```
81		pub fn map<B>(
82			self,
83			_f: impl FnOnce(A) -> B,
84		) -> Const<'a, R, B> {
85			Const::new(self.0)
86		}
87
88		/// Combines two `Const` values by appending their stored values, discarding the phantom types.
89		///
90		/// This is the inherent method form of [`Lift::lift2`](crate::classes::lift::Lift::lift2).
91		#[document_signature]
92		#[document_type_parameters("The second phantom type.", "The result phantom type.")]
93		#[document_parameters("The other `Const` instance.", "The function to lift (ignored).")]
94		#[document_returns("A new `Const` instance with the appended stored values.")]
95		#[document_examples]
96		///
97		/// ```
98		/// use fp_library::types::const_val::Const;
99		///
100		/// let c1: Const<String, i32> = Const::new("Hello".to_string());
101		/// let c2: Const<String, i32> = Const::new(" World".to_string());
102		/// let lifted = c1.lift2(c2, |a: i32, b: i32| a + b);
103		/// assert_eq!(lifted.0, "Hello World");
104		/// ```
105		pub fn lift2<B, C>(
106			self,
107			other: Const<'a, R, B>,
108			_f: impl FnOnce(A, B) -> C,
109		) -> Const<'a, R, C>
110		where
111			R: Semigroup, {
112			Const::new(R::append(self.0, other.0))
113		}
114
115		/// Combines the stored values of two `Const` instances, keeping the phantom type of the first.
116		///
117		/// This is the inherent method form of [`ApplyFirst::apply_first`](crate::classes::apply_first::ApplyFirst::apply_first).
118		#[document_signature]
119		#[document_type_parameters("The phantom type of the second `Const` instance.")]
120		#[document_parameters("The second `Const` instance.")]
121		#[document_returns("A new `Const` instance with the appended stored values.")]
122		#[document_examples]
123		///
124		/// ```
125		/// use fp_library::types::const_val::Const;
126		///
127		/// let c1: Const<String, i32> = Const::new("Hello".to_string());
128		/// let c2: Const<String, bool> = Const::new(" World".to_string());
129		/// let result = c1.apply_first(c2);
130		/// assert_eq!(result.0, "Hello World");
131		/// ```
132		pub fn apply_first<B>(
133			self,
134			other: Const<'a, R, B>,
135		) -> Const<'a, R, A>
136		where
137			R: Semigroup, {
138			Const::new(R::append(self.0, other.0))
139		}
140
141		/// Combines the stored values of two `Const` instances, keeping the phantom type of the second.
142		///
143		/// This is the inherent method form of [`ApplySecond::apply_second`](crate::classes::apply_second::ApplySecond::apply_second).
144		#[document_signature]
145		#[document_type_parameters("The phantom type of the second `Const` instance.")]
146		#[document_parameters("The second `Const` instance.")]
147		#[document_returns("A new `Const` instance with the appended stored values.")]
148		#[document_examples]
149		///
150		/// ```
151		/// use fp_library::types::const_val::Const;
152		///
153		/// let c1: Const<String, i32> = Const::new("Hello".to_string());
154		/// let c2: Const<String, bool> = Const::new(" World".to_string());
155		/// let result = c1.apply_second(c2);
156		/// assert_eq!(result.0, "Hello World");
157		/// ```
158		pub fn apply_second<B>(
159			self,
160			other: Const<'a, R, B>,
161		) -> Const<'a, R, B>
162		where
163			R: Semigroup, {
164			Const::new(R::append(self.0, other.0))
165		}
166
167		/// Creates a `Const` with the monoidal identity, ignoring the given value.
168		///
169		/// This is the inherent method form of [`Pointed::pure`](crate::classes::pointed::Pointed::pure).
170		#[document_signature]
171		#[document_parameters("The value to wrap (ignored).")]
172		#[document_returns("A new `Const` instance with the empty value of the stored type.")]
173		#[document_examples]
174		///
175		/// ```
176		/// use fp_library::types::const_val::Const;
177		///
178		/// let c: Const<String, i32> = Const::pure(42);
179		/// assert_eq!(c.0, "".to_string());
180		/// ```
181		pub fn pure(_a: A) -> Self
182		where
183			R: Monoid, {
184			Const::new(R::empty())
185		}
186	}
187
188	impl_kind! {
189		impl<R: 'static> for ConstBrand<R> {
190			type Of<'a, A: 'a>: 'a = Const<'a, R, A>;
191		}
192	}
193
194	#[document_type_parameters("The stored type.")]
195	impl<R: 'static> Functor for ConstBrand<R> {
196		#[document_signature]
197		#[document_type_parameters(
198			"The lifetime of the values.",
199			"The input type.",
200			"The output type."
201		)]
202		#[document_parameters(
203			"The function to map (ignored).",
204			"The `Const` instance to map over."
205		)]
206		#[document_returns("A new `Const` instance with the same stored value.")]
207		#[document_examples]
208		///
209		/// ```
210		/// use fp_library::{
211		/// 	brands::ConstBrand,
212		/// 	classes::functor::Functor,
213		/// 	types::const_val::Const,
214		/// };
215		///
216		/// let c: Const<i32, String> = Const::new(42);
217		/// let mapped = ConstBrand::map(|s: String| s.len(), c);
218		/// assert_eq!(mapped.0, 42);
219		/// ```
220		fn map<'a, A: 'a, B: 'a>(
221			_f: impl Fn(A) -> B + 'a,
222			fa: Apply!(<Self as Kind!( type Of<'b, T: 'b>: 'b; )>::Of<'a, A>),
223		) -> Apply!(<Self as Kind!( type Of<'b, T: 'b>: 'b; )>::Of<'a, B>) {
224			fa.map(_f)
225		}
226	}
227
228	#[document_type_parameters("The stored type.")]
229	impl<R: 'static + Semigroup> Lift for ConstBrand<R> {
230		#[document_signature]
231		#[document_type_parameters(
232			"The lifetime of the values.",
233			"The first input type.",
234			"The second input type.",
235			"The output type."
236		)]
237		#[document_parameters(
238			"The function to lift (ignored).",
239			"The first `Const` instance.",
240			"The second `Const` instance."
241		)]
242		#[document_returns("A new `Const` instance with the combined stored values.")]
243		#[document_examples]
244		///
245		/// ```
246		/// use fp_library::{
247		/// 	brands::ConstBrand,
248		/// 	classes::lift::Lift,
249		/// 	types::const_val::Const,
250		/// };
251		///
252		/// let c1: Const<String, i32> = Const::new("Hello".to_string());
253		/// let c2: Const<String, i32> = Const::new(" World".to_string());
254		/// let lifted = ConstBrand::lift2(|a: i32, b: i32| a + b, c1, c2);
255		/// assert_eq!(lifted.0, "Hello World");
256		/// ```
257		fn lift2<'a, A, B, C>(
258			_func: impl Fn(A, B) -> C + 'a,
259			fa: Apply!(<Self as Kind!( type Of<'b, T: 'b>: 'b; )>::Of<'a, A>),
260			fb: Apply!(<Self as Kind!( type Of<'b, T: 'b>: 'b; )>::Of<'a, B>),
261		) -> Apply!(<Self as Kind!( type Of<'b, T: 'b>: 'b; )>::Of<'a, C>)
262		where
263			A: Clone + 'a,
264			B: Clone + 'a,
265			C: 'a, {
266			fa.lift2(fb, _func)
267		}
268	}
269
270	#[document_type_parameters("The stored type.")]
271	impl<R: 'static + Semigroup> Semiapplicative for ConstBrand<R> {
272		#[document_signature]
273		#[document_type_parameters(
274			"The lifetime of the values.",
275			"The function brand.",
276			"The input type.",
277			"The output type."
278		)]
279		#[document_parameters(
280			"The `Const` instance containing a function.",
281			"The `Const` instance containing a value."
282		)]
283		#[document_returns("A new `Const` instance with the combined stored values.")]
284		#[document_examples]
285		///
286		/// ```
287		/// use fp_library::{
288		/// 	brands::{
289		/// 		ConstBrand,
290		/// 		RcFnBrand,
291		/// 	},
292		/// 	classes::{
293		/// 		clone_fn::CloneFn,
294		/// 		semiapplicative::Semiapplicative,
295		/// 	},
296		/// 	types::const_val::Const,
297		/// };
298		///
299		/// let c1 = Const::<String, _>::new("Hello".to_string());
300		/// let c2 = Const::<String, i32>::new(" World".to_string());
301		/// let applied = ConstBrand::<String>::apply::<RcFnBrand, i32, i32>(c1, c2);
302		/// assert_eq!(applied.0, "Hello World");
303		/// ```
304		fn apply<'a, FnBrand: 'a + CloneFn, A: 'a + Clone, B: 'a>(
305			ff: Apply!(<Self as Kind!( type Of<'b, T: 'b>: 'b; )>::Of<'a, <FnBrand as CloneFn>::Of<'a, A, B>>),
306			fa: Apply!(<Self as Kind!( type Of<'b, T: 'b>: 'b; )>::Of<'a, A>),
307		) -> Apply!(<Self as Kind!( type Of<'b, T: 'b>: 'b; )>::Of<'a, B>) {
308			Const::new(R::append(ff.0, fa.0))
309		}
310	}
311
312	#[document_type_parameters("The stored type.")]
313	impl<R: 'static + Semigroup> ApplyFirst for ConstBrand<R> {
314		#[document_signature]
315		#[document_type_parameters(
316			"The lifetime of the values.",
317			"The first type.",
318			"The second type."
319		)]
320		#[document_parameters("The first `Const` instance.", "The second `Const` instance.")]
321		#[document_returns("A new `Const` instance with the combined stored values.")]
322		#[document_examples]
323		///
324		/// ```
325		/// use fp_library::{
326		/// 	brands::ConstBrand,
327		/// 	classes::apply_first::ApplyFirst,
328		/// 	types::const_val::Const,
329		/// };
330		///
331		/// let c1: Const<String, i32> = Const::new("Hello".to_string());
332		/// let c2: Const<String, i32> = Const::new(" World".to_string());
333		/// let applied = ConstBrand::apply_first(c1, c2);
334		/// assert_eq!(applied.0, "Hello World");
335		/// ```
336		fn apply_first<'a, A: 'a, B: 'a>(
337			fa: Apply!(<Self as Kind!( type Of<'b, T: 'b>: 'b; )>::Of<'a, A>),
338			fb: Apply!(<Self as Kind!( type Of<'b, T: 'b>: 'b; )>::Of<'a, B>),
339		) -> Apply!(<Self as Kind!( type Of<'b, T: 'b>: 'b; )>::Of<'a, A>) {
340			fa.apply_first(fb)
341		}
342	}
343
344	#[document_type_parameters("The stored type.")]
345	impl<R: 'static + Semigroup> ApplySecond for ConstBrand<R> {
346		#[document_signature]
347		#[document_type_parameters(
348			"The lifetime of the values.",
349			"The first type.",
350			"The second type."
351		)]
352		#[document_parameters("The first `Const` instance.", "The second `Const` instance.")]
353		#[document_returns("A new `Const` instance with the combined stored values.")]
354		#[document_examples]
355		///
356		/// ```
357		/// use fp_library::{
358		/// 	brands::ConstBrand,
359		/// 	classes::apply_second::ApplySecond,
360		/// 	types::const_val::Const,
361		/// };
362		///
363		/// let c1: Const<String, i32> = Const::new("Hello".to_string());
364		/// let c2: Const<String, i32> = Const::new(" World".to_string());
365		/// let applied = ConstBrand::apply_second(c1, c2);
366		/// assert_eq!(applied.0, "Hello World");
367		/// ```
368		fn apply_second<'a, A: 'a, B: 'a>(
369			fa: Apply!(<Self as Kind!( type Of<'b, T: 'b>: 'b; )>::Of<'a, A>),
370			fb: Apply!(<Self as Kind!( type Of<'b, T: 'b>: 'b; )>::Of<'a, B>),
371		) -> Apply!(<Self as Kind!( type Of<'b, T: 'b>: 'b; )>::Of<'a, B>) {
372			fa.apply_second(fb)
373		}
374	}
375
376	#[document_type_parameters("The stored type.")]
377	impl<R: 'static + Monoid> Pointed for ConstBrand<R> {
378		#[document_signature]
379		#[document_type_parameters("The lifetime of the values.", "The type to wrap (ignored).")]
380		#[document_parameters("The value to wrap (ignored).")]
381		#[document_returns("A new `Const` instance with the empty value of the stored type.")]
382		#[document_examples]
383		///
384		/// ```
385		/// use fp_library::{
386		/// 	brands::ConstBrand,
387		/// 	classes::pointed::Pointed,
388		/// 	types::const_val::Const,
389		/// };
390		///
391		/// let c: Const<String, i32> = ConstBrand::pure(42);
392		/// assert_eq!(c.0, "".to_string());
393		/// ```
394		fn pure<'a, A: 'a>(_a: A) -> Apply!(<Self as Kind!( type Of<'b, T: 'b>: 'b; )>::Of<'a, A>) {
395			Const::pure(_a)
396		}
397	}
398}
399pub use inner::*;
400
401// `A` is only `PhantomData<&'a A>` - always Clone/Copy - so we only need `R: Clone`/`R: Copy`.
402impl<'a, R: Clone, A> Clone for Const<'a, R, A> {
403	fn clone(&self) -> Self {
404		Const(self.0.clone(), std::marker::PhantomData)
405	}
406}
407impl<'a, R: Copy, A> Copy for Const<'a, R, A> {}