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