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