fp_library/types/
option.rs

1//! Implementations for [`Option`].
2
3use crate::{
4	Apply,
5	brands::OptionBrand,
6	classes::{
7		applicative::Applicative, apply_first::ApplyFirst, apply_second::ApplySecond,
8		clonable_fn::ClonableFn, foldable::Foldable, functor::Functor, lift::Lift, monoid::Monoid,
9		pointed::Pointed, semiapplicative::Semiapplicative, semimonad::Semimonad,
10		traversable::Traversable,
11	},
12	impl_kind,
13	kinds::*,
14};
15
16impl_kind! {
17	for OptionBrand {
18		type Of<'a, A: 'a>: 'a = Option<A>;
19	}
20}
21
22impl Functor for OptionBrand {
23	/// Maps a function over the value in the option.
24	///
25	/// # Type Signature
26	///
27	/// `forall a b. Functor Option => (a -> b, Option a) -> Option b`
28	///
29	/// # Parameters
30	///
31	/// * `f`: The function to apply to the value.
32	/// * `fa`: The option to map over.
33	///
34	/// # Returns
35	///
36	/// A new option containing the result of applying the function, or `None`.
37	///
38	/// # Examples
39	///
40	/// ```
41	/// use fp_library::classes::functor::map;
42	/// use fp_library::brands::OptionBrand;
43	///
44	/// assert_eq!(map::<OptionBrand, _, _, _>(|x: i32| x * 2, Some(5)), Some(10));
45	/// assert_eq!(map::<OptionBrand, _, _, _>(|x: i32| x * 2, None), None);
46	/// ```
47	fn map<'a, A: 'a, B: 'a, F>(
48		f: F,
49		fa: Apply!(
50			brand: Self,
51			signature: ('a, A: 'a) -> 'a,
52		),
53	) -> Apply!(
54		brand: Self,
55		signature: ('a, B: 'a) -> 'a,
56	)
57	where
58		F: Fn(A) -> B + 'a,
59	{
60		fa.map(f)
61	}
62}
63
64impl Lift for OptionBrand {
65	/// Lifts a binary function into the option context.
66	///
67	/// # Type Signature
68	///
69	/// `forall a b c. Lift Option => ((a, b) -> c, Option a, Option b) -> Option c`
70	///
71	/// # Parameters
72	///
73	/// * `f`: The binary function to apply.
74	/// * `fa`: The first option.
75	/// * `fb`: The second option.
76	///
77	/// # Returns
78	///
79	/// `Some(f(a, b))` if both options are `Some`, otherwise `None`.
80	///
81	/// # Examples
82	///
83	/// ```
84	/// use fp_library::classes::lift::lift2;
85	/// use fp_library::brands::OptionBrand;
86	///
87	/// assert_eq!(lift2::<OptionBrand, _, _, _, _>(|x: i32, y: i32| x + y, Some(1), Some(2)), Some(3));
88	/// assert_eq!(lift2::<OptionBrand, _, _, _, _>(|x: i32, y: i32| x + y, Some(1), None), None);
89	/// ```
90	fn lift2<'a, A, B, C, F>(
91		f: F,
92		fa: Apply!(brand: Self, signature: ('a, A: 'a) -> 'a),
93		fb: Apply!(brand: Self, signature: ('a, B: 'a) -> 'a),
94	) -> Apply!(brand: Self, signature: ('a, C: 'a) -> 'a)
95	where
96		F: Fn(A, B) -> C + 'a,
97		A: 'a,
98		B: 'a,
99		C: 'a,
100	{
101		fa.zip(fb).map(|(a, b)| f(a, b))
102	}
103}
104
105impl Pointed for OptionBrand {
106	/// Wraps a value in an option.
107	///
108	/// # Type Signature
109	///
110	/// `forall a. Pointed Option => a -> Option a`
111	///
112	/// # Parameters
113	///
114	/// * `a`: The value to wrap.
115	///
116	/// # Returns
117	///
118	/// `Some(a)`.
119	///
120	/// # Examples
121	///
122	/// ```
123	/// use fp_library::classes::pointed::pure;
124	/// use fp_library::brands::OptionBrand;
125	///
126	/// assert_eq!(pure::<OptionBrand, _>(5), Some(5));
127	/// ```
128	fn pure<'a, A: 'a>(a: A) -> Apply!(brand: Self, signature: ('a, A: 'a) -> 'a) {
129		Some(a)
130	}
131}
132
133impl ApplyFirst for OptionBrand {}
134impl ApplySecond for OptionBrand {}
135
136impl Semiapplicative for OptionBrand {
137	/// Applies a wrapped function to a wrapped value.
138	///
139	/// # Type Signature
140	///
141	/// `forall a b. Semiapplicative Option => (Option (a -> b), Option a) -> Option b`
142	///
143	/// # Parameters
144	///
145	/// * `ff`: The option containing the function.
146	/// * `fa`: The option containing the value.
147	///
148	/// # Returns
149	///
150	/// `Some(f(a))` if both are `Some`, otherwise `None`.
151	///
152	/// # Examples
153	///
154	/// ```
155	/// use fp_library::classes::semiapplicative::apply;
156	/// use fp_library::classes::clonable_fn::ClonableFn;
157	/// use fp_library::brands::{OptionBrand};
158	/// use fp_library::brands::RcFnBrand;
159	/// use std::rc::Rc;
160	///
161	/// let f = Some(<RcFnBrand as ClonableFn>::new(|x: i32| x * 2));
162	/// assert_eq!(apply::<OptionBrand, _, _, RcFnBrand>(f, Some(5)), Some(10));
163	/// ```
164	fn apply<'a, A: 'a + Clone, B: 'a, FnBrand: 'a + ClonableFn>(
165		ff: Apply!(brand: Self, signature: ('a, Apply!(brand: FnBrand, kind: ClonableFn, lifetimes: ('a), types: (A, B)): 'a) -> 'a),
166		fa: Apply!(brand: Self, signature: ('a, A: 'a) -> 'a),
167	) -> Apply!(brand: Self, signature: ('a, B: 'a) -> 'a) {
168		match (ff, fa) {
169			(Some(f), Some(a)) => Some(f(a)),
170			_ => None,
171		}
172	}
173}
174
175impl Semimonad for OptionBrand {
176	/// Chains option computations.
177	///
178	/// # Type Signature
179	///
180	/// `forall a b. Semimonad Option => (Option a, a -> Option b) -> Option b`
181	///
182	/// # Parameters
183	///
184	/// * `ma`: The first option.
185	/// * `f`: The function to apply to the value inside the option.
186	///
187	/// # Returns
188	///
189	/// The result of applying `f` to the value if `ma` is `Some`, otherwise `None`.
190	///
191	/// # Examples
192	///
193	/// ```
194	/// use fp_library::classes::semimonad::bind;
195	/// use fp_library::brands::OptionBrand;
196	///
197	/// assert_eq!(bind::<OptionBrand, _, _, _>(Some(5), |x| Some(x * 2)), Some(10));
198	/// assert_eq!(bind::<OptionBrand, _, _, _>(None, |x: i32| Some(x * 2)), None);
199	/// ```
200	fn bind<'a, A: 'a, B: 'a, F>(
201		ma: Apply!(brand: Self, signature: ('a, A: 'a) -> 'a),
202		f: F,
203	) -> Apply!(brand: Self, signature: ('a, B: 'a) -> 'a)
204	where
205		F: Fn(A) -> Apply!(brand: Self, signature: ('a, B: 'a) -> 'a) + 'a,
206	{
207		ma.and_then(f)
208	}
209}
210
211impl Foldable for OptionBrand {
212	/// Folds the option from the right.
213	///
214	/// # Type Signature
215	///
216	/// `forall a b. Foldable Option => ((a, b) -> b, b, Option a) -> b`
217	///
218	/// # Parameters
219	///
220	/// * `f`: The folding function.
221	/// * `init`: The initial value.
222	/// * `fa`: The option to fold.
223	///
224	/// # Returns
225	///
226	/// `f(a, init)` if `fa` is `Some(a)`, otherwise `init`.
227	///
228	/// # Examples
229	///
230	/// ```
231	/// use fp_library::classes::foldable::fold_right;
232	/// use fp_library::brands::OptionBrand;
233	///
234	/// assert_eq!(fold_right::<OptionBrand, _, _, _>(|x: i32, acc| x + acc, 0, Some(5)), 5);
235	/// assert_eq!(fold_right::<OptionBrand, _, _, _>(|x: i32, acc| x + acc, 0, None), 0);
236	/// ```
237	fn fold_right<'a, A: 'a, B: 'a, F>(
238		f: F,
239		init: B,
240		fa: Apply!(brand: Self, signature: ('a, A: 'a) -> 'a),
241	) -> B
242	where
243		F: Fn(A, B) -> B + 'a,
244	{
245		match fa {
246			Some(a) => f(a, init),
247			None => init,
248		}
249	}
250
251	/// Folds the option from the left.
252	///
253	/// # Type Signature
254	///
255	/// `forall a b. Foldable Option => ((b, a) -> b, b, Option a) -> b`
256	///
257	/// # Parameters
258	///
259	/// * `f`: The folding function.
260	/// * `init`: The initial value.
261	/// * `fa`: The option to fold.
262	///
263	/// # Returns
264	///
265	/// `f(init, a)` if `fa` is `Some(a)`, otherwise `init`.
266	///
267	/// # Examples
268	///
269	/// ```
270	/// use fp_library::classes::foldable::fold_left;
271	/// use fp_library::brands::OptionBrand;
272	///
273	/// assert_eq!(fold_left::<OptionBrand, _, _, _>(|acc, x: i32| acc + x, 0, Some(5)), 5);
274	/// ```
275	fn fold_left<'a, A: 'a, B: 'a, F>(
276		f: F,
277		init: B,
278		fa: Apply!(brand: Self, signature: ('a, A: 'a) -> 'a),
279	) -> B
280	where
281		F: Fn(B, A) -> B + 'a,
282	{
283		match fa {
284			Some(a) => f(init, a),
285			None => init,
286		}
287	}
288
289	/// Maps the value to a monoid and returns it, or returns empty.
290	///
291	/// # Type Signature
292	///
293	/// `forall a m. (Foldable Option, Monoid m) => ((a) -> m, Option a) -> m`
294	///
295	/// # Parameters
296	///
297	/// * `f`: The mapping function.
298	/// * `fa`: The option to fold.
299	///
300	/// # Returns
301	///
302	/// `f(a)` if `fa` is `Some(a)`, otherwise `M::empty()`.
303	///
304	/// # Examples
305	///
306	/// ```
307	/// use fp_library::classes::foldable::fold_map;
308	/// use fp_library::brands::OptionBrand;
309	/// use fp_library::types::string; // Import to bring Monoid impl for String into scope
310	///
311	/// assert_eq!(fold_map::<OptionBrand, _, _, _>(|x: i32| x.to_string(), Some(5)), "5".to_string());
312	/// ```
313	fn fold_map<'a, A: 'a, M, F>(
314		f: F,
315		fa: Apply!(brand: Self, signature: ('a, A: 'a) -> 'a),
316	) -> M
317	where
318		M: Monoid + 'a,
319		F: Fn(A) -> M + 'a,
320	{
321		match fa {
322			Some(a) => f(a),
323			None => M::empty(),
324		}
325	}
326}
327
328impl Traversable for OptionBrand {
329	/// Traverses the option with an applicative function.
330	///
331	/// # Type Signature
332	///
333	/// `forall a b f. (Traversable Option, Applicative f) => (a -> f b, Option a) -> f (Option b)`
334	///
335	/// # Parameters
336	///
337	/// * `f`: The function to apply.
338	/// * `ta`: The option to traverse.
339	///
340	/// # Returns
341	///
342	/// The option wrapped in the applicative context.
343	///
344	/// # Examples
345	///
346	/// ```
347	/// use fp_library::classes::traversable::traverse;
348	/// use fp_library::brands::OptionBrand;
349	///
350	/// assert_eq!(traverse::<OptionBrand, OptionBrand, _, _, _>(|x| Some(x * 2), Some(5)), Some(Some(10)));
351	/// ```
352	fn traverse<'a, F: Applicative, A: 'a + Clone, B: 'a + Clone, Func>(
353		f: Func,
354		ta: Apply!(brand: Self, signature: ('a, A: 'a) -> 'a),
355	) -> Apply!(brand: F, signature: ('a, Apply!(brand: Self, signature: ('a, B: 'a) -> 'a): 'a) -> 'a)
356	where
357		Func: Fn(A) -> Apply!(brand: F, signature: ('a, B: 'a) -> 'a) + 'a,
358		Apply!(brand: Self, signature: ('a, B: 'a) -> 'a): Clone,
359	{
360		match ta {
361			Some(a) => F::map(|b| Some(b), f(a)),
362			None => F::pure(None),
363		}
364	}
365
366	/// Sequences an option of applicative.
367	///
368	/// # Type Signature
369	///
370	/// `forall a f. (Traversable Option, Applicative f) => (Option (f a)) -> f (Option a)`
371	///
372	/// # Parameters
373	///
374	/// * `ta`: The option containing the applicative value.
375	///
376	/// # Returns
377	///
378	/// The option wrapped in the applicative context.
379	///
380	/// # Examples
381	///
382	/// ```
383	/// use fp_library::classes::traversable::sequence;
384	/// use fp_library::brands::OptionBrand;
385	///
386	/// assert_eq!(sequence::<OptionBrand, OptionBrand, _>(Some(Some(5))), Some(Some(5)));
387	/// ```
388	fn sequence<'a, F: Applicative, A: 'a + Clone>(
389		ta: Apply!(brand: Self, signature: ('a, Apply!(brand: F, signature: ('a, A: 'a) -> 'a): 'a) -> 'a)
390	) -> Apply!(brand: F, signature: ('a, Apply!(brand: Self, signature: ('a, A: 'a) -> 'a): 'a) -> 'a)
391	where
392		Apply!(brand: F, signature: ('a, A: 'a) -> 'a): Clone,
393		Apply!(brand: Self, signature: ('a, A: 'a) -> 'a): Clone,
394	{
395		match ta {
396			Some(fa) => F::map(|a| Some(a), fa),
397			None => F::pure(None),
398		}
399	}
400}
401
402#[cfg(test)]
403mod tests {
404	use super::*;
405	use crate::{
406		brands::RcFnBrand,
407		classes::{functor::map, pointed::pure, semiapplicative::apply, semimonad::bind},
408		functions::{compose, identity},
409	};
410	use quickcheck_macros::quickcheck;
411
412	// Functor Laws
413
414	/// Tests the identity law for Functor.
415	#[quickcheck]
416	fn functor_identity(x: Option<i32>) -> bool {
417		map::<OptionBrand, _, _, _>(identity, x) == x
418	}
419
420	/// Tests the composition law for Functor.
421	#[quickcheck]
422	fn functor_composition(x: Option<i32>) -> bool {
423		let f = |x: i32| x.wrapping_add(1);
424		let g = |x: i32| x.wrapping_mul(2);
425		map::<OptionBrand, _, _, _>(compose(f, g), x)
426			== map::<OptionBrand, _, _, _>(f, map::<OptionBrand, _, _, _>(g, x))
427	}
428
429	// Applicative Laws
430
431	/// Tests the identity law for Applicative.
432	#[quickcheck]
433	fn applicative_identity(v: Option<i32>) -> bool {
434		apply::<OptionBrand, _, _, RcFnBrand>(
435			pure::<OptionBrand, _>(<RcFnBrand as ClonableFn>::new(identity)),
436			v,
437		) == v
438	}
439
440	/// Tests the homomorphism law for Applicative.
441	#[quickcheck]
442	fn applicative_homomorphism(x: i32) -> bool {
443		let f = |x: i32| x.wrapping_mul(2);
444		apply::<OptionBrand, _, _, RcFnBrand>(
445			pure::<OptionBrand, _>(<RcFnBrand as ClonableFn>::new(f)),
446			pure::<OptionBrand, _>(x),
447		) == pure::<OptionBrand, _>(f(x))
448	}
449
450	/// Tests the composition law for Applicative.
451	#[quickcheck]
452	fn applicative_composition(
453		w: Option<i32>,
454		u_is_some: bool,
455		v_is_some: bool,
456	) -> bool {
457		let v_fn = |x: i32| x.wrapping_mul(2);
458		let u_fn = |x: i32| x.wrapping_add(1);
459
460		let v = if v_is_some {
461			pure::<OptionBrand, _>(<RcFnBrand as ClonableFn>::new(v_fn))
462		} else {
463			None
464		};
465		let u = if u_is_some {
466			pure::<OptionBrand, _>(<RcFnBrand as ClonableFn>::new(u_fn))
467		} else {
468			None
469		};
470
471		// RHS: u <*> (v <*> w)
472		let vw = apply::<OptionBrand, _, _, RcFnBrand>(v.clone(), w.clone());
473		let rhs = apply::<OptionBrand, _, _, RcFnBrand>(u.clone(), vw);
474
475		// LHS: pure(compose) <*> u <*> v <*> w
476		// equivalent to (u . v) <*> w
477		let uv = match (u, v) {
478			(Some(uf), Some(vf)) => {
479				let composed = move |x| uf(vf(x));
480				Some(<RcFnBrand as ClonableFn>::new(composed))
481			}
482			_ => None,
483		};
484
485		let lhs = apply::<OptionBrand, _, _, RcFnBrand>(uv, w);
486
487		lhs == rhs
488	}
489
490	/// Tests the interchange law for Applicative.
491	#[quickcheck]
492	fn applicative_interchange(y: i32) -> bool {
493		// u <*> pure y = pure ($ y) <*> u
494		let f = |x: i32| x.wrapping_mul(2);
495		let u = pure::<OptionBrand, _>(<RcFnBrand as ClonableFn>::new(f));
496
497		let lhs = apply::<OptionBrand, _, _, RcFnBrand>(u.clone(), pure::<OptionBrand, _>(y));
498
499		let rhs_fn = <RcFnBrand as ClonableFn>::new(move |f: std::rc::Rc<dyn Fn(i32) -> i32>| f(y));
500		let rhs = apply::<OptionBrand, _, _, RcFnBrand>(pure::<OptionBrand, _>(rhs_fn), u);
501
502		lhs == rhs
503	}
504
505	// Monad Laws
506
507	/// Tests the left identity law for Monad.
508	#[quickcheck]
509	fn monad_left_identity(a: i32) -> bool {
510		let f = |x: i32| Some(x.wrapping_mul(2));
511		bind::<OptionBrand, _, _, _>(pure::<OptionBrand, _>(a), f) == f(a)
512	}
513
514	/// Tests the right identity law for Monad.
515	#[quickcheck]
516	fn monad_right_identity(m: Option<i32>) -> bool {
517		bind::<OptionBrand, _, _, _>(m, pure::<OptionBrand, _>) == m
518	}
519
520	/// Tests the associativity law for Monad.
521	#[quickcheck]
522	fn monad_associativity(m: Option<i32>) -> bool {
523		let f = |x: i32| Some(x.wrapping_mul(2));
524		let g = |x: i32| Some(x.wrapping_add(1));
525		bind::<OptionBrand, _, _, _>(bind::<OptionBrand, _, _, _>(m, f), g)
526			== bind::<OptionBrand, _, _, _>(m, |x| bind::<OptionBrand, _, _, _>(f(x), g))
527	}
528
529	// Edge Cases
530
531	/// Tests `map` on `None`.
532	#[test]
533	fn map_none() {
534		assert_eq!(map::<OptionBrand, _, _, _>(|x: i32| x + 1, None), None);
535	}
536
537	/// Tests `bind` on `None`.
538	#[test]
539	fn bind_none() {
540		assert_eq!(bind::<OptionBrand, _, _, _>(None, |x: i32| Some(x + 1)), None);
541	}
542
543	/// Tests `bind` returning `None`.
544	#[test]
545	fn bind_returning_none() {
546		assert_eq!(bind::<OptionBrand, _, _, _>(Some(5), |_| None::<i32>), None);
547	}
548
549	/// Tests `fold_right` on `None`.
550	#[test]
551	fn fold_right_none() {
552		assert_eq!(
553			crate::classes::foldable::fold_right::<OptionBrand, _, _, _>(
554				|x: i32, acc| x + acc,
555				0,
556				None
557			),
558			0
559		);
560	}
561
562	/// Tests `fold_left` on `None`.
563	#[test]
564	fn fold_left_none() {
565		assert_eq!(
566			crate::classes::foldable::fold_left::<OptionBrand, _, _, _>(
567				|acc, x: i32| acc + x,
568				0,
569				None
570			),
571			0
572		);
573	}
574
575	/// Tests `traverse` on `None`.
576	#[test]
577	fn traverse_none() {
578		assert_eq!(
579			crate::classes::traversable::traverse::<OptionBrand, OptionBrand, _, _, _>(
580				|x: i32| Some(x + 1),
581				None
582			),
583			Some(None)
584		);
585	}
586
587	/// Tests `traverse` returning `None`.
588	#[test]
589	fn traverse_returning_none() {
590		assert_eq!(
591			crate::classes::traversable::traverse::<OptionBrand, OptionBrand, _, _, _>(
592				|_: i32| None::<i32>,
593				Some(5)
594			),
595			None
596		);
597	}
598}