fp_library/types/option.rs
1//! Implementations for [`Option`].
2//!
3//! This module provides implementations of functional programming traits for the standard library [`Option`] type.
4
5use crate::{
6 Apply,
7 brands::OptionBrand,
8 classes::{
9 applicative::Applicative, apply_first::ApplyFirst, apply_second::ApplySecond,
10 clonable_fn::ClonableFn, foldable::Foldable, functor::Functor, lift::Lift, monoid::Monoid,
11 par_foldable::ParFoldable, pointed::Pointed, semiapplicative::Semiapplicative,
12 semimonad::Semimonad, send_clonable_fn::SendClonableFn, traversable::Traversable,
13 },
14 impl_kind,
15 kinds::*,
16};
17
18impl_kind! {
19 for OptionBrand {
20 type Of<'a, A: 'a>: 'a = Option<A>;
21 }
22}
23
24impl Functor for OptionBrand {
25 /// Maps a function over the value in the option.
26 ///
27 /// This method applies a function to the value inside the option, producing a new option with the transformed value. If the option is `None`, it returns `None`.
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::Functor;
46 /// use fp_library::brands::OptionBrand;
47 ///
48 /// let x = Some(5);
49 /// let y = OptionBrand::map(|i| i * 2, x);
50 /// assert_eq!(y, Some(10));
51 ///
52 /// // Using the free function
53 /// use fp_library::classes::functor::map;
54 /// assert_eq!(map::<OptionBrand, _, _, _>(|x: i32| x * 2, Some(5)), Some(10));
55 /// assert_eq!(map::<OptionBrand, _, _, _>(|x: i32| x * 2, None), None);
56 /// ```
57 fn map<'a, F, A: 'a, B: 'a>(
58 f: F,
59 fa: Apply!(
60 brand: Self,
61 signature: ('a, A: 'a) -> 'a,
62 ),
63 ) -> Apply!(
64 brand: Self,
65 signature: ('a, B: 'a) -> 'a,
66 )
67 where
68 F: Fn(A) -> B + 'a,
69 {
70 fa.map(f)
71 }
72}
73
74impl Lift for OptionBrand {
75 /// Lifts a binary function into the option context.
76 ///
77 /// This method lifts a binary function to operate on values within the option context.
78 ///
79 /// ### Type Signature
80 ///
81 /// `forall a b c. Lift Option => ((a, b) -> c, Option a, Option b) -> Option c`
82 ///
83 /// ### Parameters
84 ///
85 /// * `f`: The binary function to apply.
86 /// * `fa`: The first option.
87 /// * `fb`: The second option.
88 ///
89 /// ### Returns
90 ///
91 /// `Some(f(a, b))` if both options are `Some`, otherwise `None`.
92 ///
93 /// ### Examples
94 ///
95 /// ```
96 /// use fp_library::classes::lift::Lift;
97 /// use fp_library::brands::OptionBrand;
98 ///
99 /// let x = Some(1);
100 /// let y = Some(2);
101 /// let z = OptionBrand::lift2(|a, b| a + b, x, y);
102 /// assert_eq!(z, Some(3));
103 ///
104 /// // Using the free function
105 /// use fp_library::classes::lift::lift2;
106 /// assert_eq!(lift2::<OptionBrand, _, _, _, _>(|x: i32, y: i32| x + y, Some(1), Some(2)), Some(3));
107 /// assert_eq!(lift2::<OptionBrand, _, _, _, _>(|x: i32, y: i32| x + y, Some(1), None), None);
108 /// ```
109 fn lift2<'a, F, A, B, C>(
110 f: F,
111 fa: Apply!(brand: Self, signature: ('a, A: 'a) -> 'a),
112 fb: Apply!(brand: Self, signature: ('a, B: 'a) -> 'a),
113 ) -> Apply!(brand: Self, signature: ('a, C: 'a) -> 'a)
114 where
115 F: Fn(A, B) -> C + 'a,
116 A: 'a,
117 B: 'a,
118 C: 'a,
119 {
120 fa.zip(fb).map(|(a, b)| f(a, b))
121 }
122}
123
124impl Pointed for OptionBrand {
125 /// Wraps a value in an option.
126 ///
127 /// This method wraps a value in an option context.
128 ///
129 /// ### Type Signature
130 ///
131 /// `forall a. Pointed Option => a -> Option a`
132 ///
133 /// ### Parameters
134 ///
135 /// * `a`: The value to wrap.
136 ///
137 /// ### Returns
138 ///
139 /// `Some(a)`.
140 ///
141 /// ### Examples
142 ///
143 /// ```
144 /// use fp_library::classes::pointed::Pointed;
145 /// use fp_library::brands::OptionBrand;
146 ///
147 /// let x = OptionBrand::pure(5);
148 /// assert_eq!(x, Some(5));
149 ///
150 /// // Using the free function
151 /// use fp_library::classes::pointed::pure;
152 /// assert_eq!(pure::<OptionBrand, _>(5), Some(5));
153 /// ```
154 fn pure<'a, A: 'a>(a: A) -> Apply!(brand: Self, signature: ('a, A: 'a) -> 'a) {
155 Some(a)
156 }
157}
158
159impl ApplyFirst for OptionBrand {}
160impl ApplySecond for OptionBrand {}
161
162impl Semiapplicative for OptionBrand {
163 /// Applies a wrapped function to a wrapped value.
164 ///
165 /// This method applies a function wrapped in an option to a value wrapped in an option.
166 ///
167 /// ### Type Signature
168 ///
169 /// `forall a b. Semiapplicative Option => (Option (a -> b), Option a) -> Option b`
170 ///
171 /// ### Parameters
172 ///
173 /// * `ff`: The option containing the function.
174 /// * `fa`: The option containing the value.
175 ///
176 /// ### Returns
177 ///
178 /// `Some(f(a))` if both are `Some`, otherwise `None`.
179 ///
180 /// ### Examples
181 ///
182 /// ```
183 /// use fp_library::classes::semiapplicative::Semiapplicative;
184 /// use fp_library::classes::clonable_fn::ClonableFn;
185 /// use fp_library::brands::{OptionBrand};
186 /// use fp_library::brands::RcFnBrand;
187 /// use std::rc::Rc;
188 ///
189 /// let f = Some(<RcFnBrand as ClonableFn>::new(|x: i32| x * 2));
190 /// let x = Some(5);
191 /// let y = OptionBrand::apply::<RcFnBrand, i32, i32>(f, x);
192 /// assert_eq!(y, Some(10));
193 ///
194 /// // Using the free function
195 /// use fp_library::classes::semiapplicative::apply;
196 /// let f = Some(<RcFnBrand as ClonableFn>::new(|x: i32| x * 2));
197 /// assert_eq!(apply::<RcFnBrand, OptionBrand, _, _>(f, Some(5)), Some(10));
198 /// ```
199 fn apply<'a, FnBrand: 'a + ClonableFn, A: 'a + Clone, B: 'a>(
200 ff: Apply!(brand: Self, signature: ('a, Apply!(brand: FnBrand, kind: ClonableFn, lifetimes: ('a), types: (A, B)): 'a) -> 'a),
201 fa: Apply!(brand: Self, signature: ('a, A: 'a) -> 'a),
202 ) -> Apply!(brand: Self, signature: ('a, B: 'a) -> 'a) {
203 match (ff, fa) {
204 (Some(f), Some(a)) => Some(f(a)),
205 _ => None,
206 }
207 }
208}
209
210impl Semimonad for OptionBrand {
211 /// Chains option computations.
212 ///
213 /// This method chains two option computations, where the second computation depends on the result of the first.
214 ///
215 /// ### Type Signature
216 ///
217 /// `forall a b. Semimonad Option => (Option a, a -> Option b) -> Option b`
218 ///
219 /// ### Parameters
220 ///
221 /// * `ma`: The first option.
222 /// * `f`: The function to apply to the value inside the option.
223 ///
224 /// ### Returns
225 ///
226 /// The result of applying `f` to the value if `ma` is `Some`, otherwise `None`.
227 ///
228 /// ### Examples
229 ///
230 /// ```
231 /// use fp_library::classes::semimonad::Semimonad;
232 /// use fp_library::brands::OptionBrand;
233 ///
234 /// let x = Some(5);
235 /// let y = OptionBrand::bind(x, |i| Some(i * 2));
236 /// assert_eq!(y, Some(10));
237 ///
238 /// // Using the free function
239 /// use fp_library::classes::semimonad::bind;
240 /// assert_eq!(bind::<OptionBrand, _, _, _>(Some(5), |x| Some(x * 2)), Some(10));
241 /// assert_eq!(bind::<OptionBrand, _, _, _>(None, |x: i32| Some(x * 2)), None);
242 /// ```
243 fn bind<'a, F, A: 'a, B: 'a>(
244 ma: Apply!(brand: Self, signature: ('a, A: 'a) -> 'a),
245 f: F,
246 ) -> Apply!(brand: Self, signature: ('a, B: 'a) -> 'a)
247 where
248 F: Fn(A) -> Apply!(brand: Self, signature: ('a, B: 'a) -> 'a) + 'a,
249 {
250 ma.and_then(f)
251 }
252}
253
254impl Foldable for OptionBrand {
255 /// Folds the option from the right.
256 ///
257 /// This method performs a right-associative fold of the option. If the option is `Some(a)`, it applies the function to `a` and the initial value. If `None`, it returns the initial value.
258 ///
259 /// ### Type Signature
260 ///
261 /// `forall a b. Foldable Option => ((a, b) -> b, b, Option a) -> b`
262 ///
263 /// ### Parameters
264 ///
265 /// * `func`: The folding function.
266 /// * `initial`: The initial value.
267 /// * `fa`: The option to fold.
268 ///
269 /// ### Returns
270 ///
271 /// `func(a, initial)` if `fa` is `Some(a)`, otherwise `initial`.
272 ///
273 /// ### Examples
274 ///
275 /// ```
276 /// use fp_library::classes::foldable::Foldable;
277 /// use fp_library::brands::OptionBrand;
278 /// use fp_library::brands::RcFnBrand;
279 ///
280 /// let x = Some(5);
281 /// let y = OptionBrand::fold_right::<RcFnBrand, _, _, _>(|a, b| a + b, 10, x);
282 /// assert_eq!(y, 15);
283 ///
284 /// // Using the free function
285 /// use fp_library::classes::foldable::fold_right;
286 /// assert_eq!(fold_right::<RcFnBrand, OptionBrand, _, _, _>(|x: i32, acc| x + acc, 0, Some(5)), 5);
287 /// assert_eq!(fold_right::<RcFnBrand, OptionBrand, _, _, _>(|x: i32, acc| x + acc, 0, None), 0);
288 /// ```
289 fn fold_right<'a, FnBrand, Func, A: 'a, B: 'a>(
290 func: Func,
291 initial: B,
292 fa: Apply!(brand: Self, signature: ('a, A: 'a) -> 'a),
293 ) -> B
294 where
295 Func: Fn(A, B) -> B + 'a,
296 FnBrand: ClonableFn + 'a,
297 {
298 match fa {
299 Some(a) => func(a, initial),
300 None => initial,
301 }
302 }
303
304 /// Folds the option from the left.
305 ///
306 /// This method performs a left-associative fold of the option. If the option is `Some(a)`, it applies the function to the initial value and `a`. If `None`, it returns the initial value.
307 ///
308 /// ### Type Signature
309 ///
310 /// `forall a b. Foldable Option => ((b, a) -> b, b, Option a) -> b`
311 ///
312 /// ### Parameters
313 ///
314 /// * `func`: The function to apply to the accumulator and each element.
315 /// * `initial`: The initial value of the accumulator.
316 /// * `fa`: The option to fold.
317 ///
318 /// ### Returns
319 ///
320 /// `f(initial, a)` if `fa` is `Some(a)`, otherwise `initial`.
321 ///
322 /// ### Examples
323 ///
324 /// ```
325 /// use fp_library::classes::foldable::Foldable;
326 /// use fp_library::brands::OptionBrand;
327 /// use fp_library::brands::RcFnBrand;
328 ///
329 /// let x = Some(5);
330 /// let y = OptionBrand::fold_left::<RcFnBrand, _, _, _>(|b, a| b + a, 10, x);
331 /// assert_eq!(y, 15);
332 ///
333 /// // Using the free function
334 /// use fp_library::classes::foldable::fold_left;
335 /// assert_eq!(fold_left::<RcFnBrand, OptionBrand, _, _, _>(|acc, x: i32| acc + x, 0, Some(5)), 5);
336 /// ```
337 fn fold_left<'a, FnBrand, Func, A: 'a, B: 'a>(
338 func: Func,
339 initial: B,
340 fa: Apply!(brand: Self, signature: ('a, A: 'a) -> 'a),
341 ) -> B
342 where
343 Func: Fn(B, A) -> B + 'a,
344 FnBrand: ClonableFn + 'a,
345 {
346 match fa {
347 Some(a) => func(initial, a),
348 None => initial,
349 }
350 }
351
352 /// Maps the value to a monoid and returns it, or returns empty.
353 ///
354 /// This method maps the element of the option to a monoid. If the option is `None`, it returns the monoid's identity element.
355 ///
356 /// ### Type Signature
357 ///
358 /// `forall a m. (Foldable Option, Monoid m) => ((a) -> m, Option a) -> m`
359 ///
360 /// ### Parameters
361 ///
362 /// * `func`: The mapping function.
363 /// * `fa`: The option to fold.
364 ///
365 /// ### Returns
366 ///
367 /// `func(a)` if `fa` is `Some(a)`, otherwise `M::empty()`.
368 ///
369 /// ### Examples
370 ///
371 /// ```
372 /// use fp_library::classes::foldable::Foldable;
373 /// use fp_library::brands::OptionBrand;
374 /// use fp_library::types::string; // Import to bring Monoid impl for String into scope
375 /// use fp_library::brands::RcFnBrand;
376 ///
377 /// let x = Some(5);
378 /// let y = OptionBrand::fold_map::<RcFnBrand, _, _, _>(|a: i32| a.to_string(), x);
379 /// assert_eq!(y, "5".to_string());
380 ///
381 /// // Using the free function
382 /// use fp_library::classes::foldable::fold_map;
383 /// assert_eq!(fold_map::<RcFnBrand, OptionBrand, _, _, _>(|x: i32| x.to_string(), Some(5)), "5".to_string());
384 /// ```
385 fn fold_map<'a, FnBrand, Func, A: 'a, M>(
386 func: Func,
387 fa: Apply!(brand: Self, signature: ('a, A: 'a) -> 'a),
388 ) -> M
389 where
390 M: Monoid + 'a,
391 Func: Fn(A) -> M + 'a,
392 FnBrand: ClonableFn + 'a,
393 {
394 match fa {
395 Some(a) => func(a),
396 None => M::empty(),
397 }
398 }
399}
400
401impl Traversable for OptionBrand {
402 /// Traverses the option with an applicative function.
403 ///
404 /// This method maps the element of the option to a computation, evaluates it, and wraps the result in the applicative context. If `None`, it returns `pure(None)`.
405 ///
406 /// ### Type Signature
407 ///
408 /// `forall a b f. (Traversable Option, Applicative f) => (a -> f b, Option a) -> f (Option b)`
409 ///
410 /// ### Parameters
411 ///
412 /// * `func`: The function to apply to each element, returning a value in an applicative context.
413 /// * `ta`: The option to traverse.
414 ///
415 /// ### Returns
416 ///
417 /// The option wrapped in the applicative context.
418 ///
419 /// ### Examples
420 ///
421 /// ```
422 /// use fp_library::classes::traversable::Traversable;
423 /// use fp_library::brands::OptionBrand;
424 ///
425 /// let x = Some(5);
426 /// let y = OptionBrand::traverse::<OptionBrand, _, _, _>(|a| Some(a * 2), x);
427 /// assert_eq!(y, Some(Some(10)));
428 ///
429 /// // Using the free function
430 /// use fp_library::classes::traversable::traverse;
431 /// assert_eq!(traverse::<OptionBrand, OptionBrand, _, _, _>(|x| Some(x * 2), Some(5)), Some(Some(10)));
432 /// ```
433 fn traverse<'a, F: Applicative, Func, A: 'a + Clone, B: 'a + Clone>(
434 func: Func,
435 ta: Apply!(brand: Self, signature: ('a, A: 'a) -> 'a),
436 ) -> Apply!(brand: F, signature: ('a, Apply!(brand: Self, signature: ('a, B: 'a) -> 'a): 'a) -> 'a)
437 where
438 Func: Fn(A) -> Apply!(brand: F, signature: ('a, B: 'a) -> 'a) + 'a,
439 Apply!(brand: Self, signature: ('a, B: 'a) -> 'a): Clone,
440 {
441 match ta {
442 Some(a) => F::map(|b| Some(b), func(a)),
443 None => F::pure(None),
444 }
445 }
446 /// Sequences an option of applicative.
447 ///
448 /// This method evaluates the computation inside the option and wraps the result in the applicative context. If `None`, it returns `pure(None)`.
449 ///
450 /// ### Type Signature
451 ///
452 /// `forall a f. (Traversable Option, Applicative f) => (Option (f a)) -> f (Option a)`
453 ///
454 /// ### Parameters
455 ///
456 /// * `ta`: The option containing the applicative value.
457 ///
458 /// # Returns
459 ///
460 /// The option wrapped in the applicative context.
461 ///
462 /// ### Examples
463 ///
464 /// ```
465 /// use fp_library::classes::traversable::Traversable;
466 /// use fp_library::brands::OptionBrand;
467 ///
468 /// let x = Some(Some(5));
469 /// let y = OptionBrand::sequence::<OptionBrand, _>(x);
470 /// assert_eq!(y, Some(Some(5)));
471 ///
472 /// // Using the free function
473 /// use fp_library::classes::traversable::sequence;
474 /// assert_eq!(sequence::<OptionBrand, OptionBrand, _>(Some(Some(5))), Some(Some(5)));
475 /// ```
476 fn sequence<'a, F: Applicative, A: 'a + Clone>(
477 ta: Apply!(brand: Self, signature: ('a, Apply!(brand: F, signature: ('a, A: 'a) -> 'a): 'a) -> 'a)
478 ) -> Apply!(brand: F, signature: ('a, Apply!(brand: Self, signature: ('a, A: 'a) -> 'a): 'a) -> 'a)
479 where
480 Apply!(brand: F, signature: ('a, A: 'a) -> 'a): Clone,
481 Apply!(brand: Self, signature: ('a, A: 'a) -> 'a): Clone,
482 {
483 match ta {
484 Some(fa) => F::map(|a| Some(a), fa),
485 None => F::pure(None),
486 }
487 }
488}
489
490impl<FnBrand: SendClonableFn> ParFoldable<FnBrand> for OptionBrand {
491 /// Maps the value to a monoid and returns it, or returns empty, in parallel.
492 ///
493 /// This method maps the element of the option to a monoid. Since `Option` contains at most one element, no actual parallelism occurs, but the interface is satisfied.
494 ///
495 /// ### Type Signature
496 ///
497 /// `forall a m. (ParFoldable Option, Monoid m, Send m, Sync m) => (f a m, Option a) -> m`
498 ///
499 /// ### Parameters
500 ///
501 /// * `func`: The mapping function.
502 /// * `fa`: The option to fold.
503 ///
504 /// ### Returns
505 ///
506 /// The combined monoid value.
507 ///
508 /// ### Examples
509 ///
510 /// ```
511 /// use fp_library::classes::par_foldable::ParFoldable;
512 /// use fp_library::brands::{OptionBrand, ArcFnBrand};
513 /// use fp_library::classes::send_clonable_fn::SendClonableFn;
514 /// use fp_library::classes::send_clonable_fn::new_send;
515 ///
516 /// let x = Some(1);
517 /// let f = new_send::<ArcFnBrand, _, _>(|x: i32| x.to_string());
518 /// let y = <OptionBrand as ParFoldable<ArcFnBrand>>::par_fold_map(f, x);
519 /// assert_eq!(y, "1".to_string());
520 ///
521 /// // Using the free function
522 /// use fp_library::classes::par_foldable::par_fold_map;
523 /// let x = Some(1);
524 /// let f = new_send::<ArcFnBrand, _, _>(|x: i32| x.to_string());
525 /// assert_eq!(par_fold_map::<ArcFnBrand, OptionBrand, _, _>(f, x), "1".to_string());
526 /// ```
527 fn par_fold_map<'a, A, M>(
528 func: Apply!(brand: FnBrand, kind: SendClonableFn, output: SendOf, lifetimes: ('a), types: (A, M)),
529 fa: Apply!(brand: Self, signature: ('a, A: 'a) -> 'a),
530 ) -> M
531 where
532 A: 'a + Clone + Send + Sync,
533 M: Monoid + Send + Sync + 'a,
534 {
535 match fa {
536 Some(a) => func(a),
537 None => M::empty(),
538 }
539 }
540}
541
542#[cfg(test)]
543mod tests {
544 use super::*;
545 use crate::{
546 brands::{ArcFnBrand, RcFnBrand},
547 classes::{
548 functor::map,
549 par_foldable::{par_fold_map, par_fold_right},
550 pointed::pure,
551 semiapplicative::apply,
552 semimonad::bind,
553 send_clonable_fn::new_send,
554 },
555 functions::{compose, identity},
556 };
557 use quickcheck_macros::quickcheck;
558
559 // Functor Laws
560
561 /// Tests the identity law for Functor.
562 #[quickcheck]
563 fn functor_identity(x: Option<i32>) -> bool {
564 map::<OptionBrand, _, _, _>(identity, x) == x
565 }
566
567 /// Tests the composition law for Functor.
568 #[quickcheck]
569 fn functor_composition(x: Option<i32>) -> bool {
570 let f = |x: i32| x.wrapping_add(1);
571 let g = |x: i32| x.wrapping_mul(2);
572 map::<OptionBrand, _, _, _>(compose(f, g), x)
573 == map::<OptionBrand, _, _, _>(f, map::<OptionBrand, _, _, _>(g, x))
574 }
575
576 // Applicative Laws
577
578 /// Tests the identity law for Applicative.
579 #[quickcheck]
580 fn applicative_identity(v: Option<i32>) -> bool {
581 apply::<RcFnBrand, OptionBrand, _, _>(
582 pure::<OptionBrand, _>(<RcFnBrand as ClonableFn>::new(identity)),
583 v,
584 ) == v
585 }
586
587 /// Tests the homomorphism law for Applicative.
588 #[quickcheck]
589 fn applicative_homomorphism(x: i32) -> bool {
590 let f = |x: i32| x.wrapping_mul(2);
591 apply::<RcFnBrand, OptionBrand, _, _>(
592 pure::<OptionBrand, _>(<RcFnBrand as ClonableFn>::new(f)),
593 pure::<OptionBrand, _>(x),
594 ) == pure::<OptionBrand, _>(f(x))
595 }
596
597 /// Tests the composition law for Applicative.
598 #[quickcheck]
599 fn applicative_composition(
600 w: Option<i32>,
601 u_is_some: bool,
602 v_is_some: bool,
603 ) -> bool {
604 let v_fn = |x: i32| x.wrapping_mul(2);
605 let u_fn = |x: i32| x.wrapping_add(1);
606
607 let v = if v_is_some {
608 pure::<OptionBrand, _>(<RcFnBrand as ClonableFn>::new(v_fn))
609 } else {
610 None
611 };
612 let u = if u_is_some {
613 pure::<OptionBrand, _>(<RcFnBrand as ClonableFn>::new(u_fn))
614 } else {
615 None
616 };
617
618 // RHS: u <*> (v <*> w)
619 let vw = apply::<RcFnBrand, OptionBrand, _, _>(v.clone(), w.clone());
620 let rhs = apply::<RcFnBrand, OptionBrand, _, _>(u.clone(), vw);
621
622 // LHS: pure(compose) <*> u <*> v <*> w
623 // equivalent to (u . v) <*> w
624 let uv = match (u, v) {
625 (Some(uf), Some(vf)) => {
626 let composed = move |x| uf(vf(x));
627 Some(<RcFnBrand as ClonableFn>::new(composed))
628 }
629 _ => None,
630 };
631
632 let lhs = apply::<RcFnBrand, OptionBrand, _, _>(uv, w);
633
634 lhs == rhs
635 }
636
637 /// Tests the interchange law for Applicative.
638 #[quickcheck]
639 fn applicative_interchange(y: i32) -> bool {
640 // u <*> pure y = pure ($ y) <*> u
641 let f = |x: i32| x.wrapping_mul(2);
642 let u = pure::<OptionBrand, _>(<RcFnBrand as ClonableFn>::new(f));
643
644 let lhs = apply::<RcFnBrand, OptionBrand, _, _>(u.clone(), pure::<OptionBrand, _>(y));
645
646 let rhs_fn = <RcFnBrand as ClonableFn>::new(move |f: std::rc::Rc<dyn Fn(i32) -> i32>| f(y));
647 let rhs = apply::<RcFnBrand, OptionBrand, _, _>(pure::<OptionBrand, _>(rhs_fn), u);
648
649 lhs == rhs
650 }
651
652 // Monad Laws
653
654 /// Tests the left identity law for Monad.
655 #[quickcheck]
656 fn monad_left_identity(a: i32) -> bool {
657 let f = |x: i32| Some(x.wrapping_mul(2));
658 bind::<OptionBrand, _, _, _>(pure::<OptionBrand, _>(a), f) == f(a)
659 }
660
661 /// Tests the right identity law for Monad.
662 #[quickcheck]
663 fn monad_right_identity(m: Option<i32>) -> bool {
664 bind::<OptionBrand, _, _, _>(m, pure::<OptionBrand, _>) == m
665 }
666
667 /// Tests the associativity law for Monad.
668 #[quickcheck]
669 fn monad_associativity(m: Option<i32>) -> bool {
670 let f = |x: i32| Some(x.wrapping_mul(2));
671 let g = |x: i32| Some(x.wrapping_add(1));
672 bind::<OptionBrand, _, _, _>(bind::<OptionBrand, _, _, _>(m, f), g)
673 == bind::<OptionBrand, _, _, _>(m, |x| bind::<OptionBrand, _, _, _>(f(x), g))
674 }
675
676 // Edge Cases
677
678 /// Tests `map` on `None`.
679 #[test]
680 fn map_none() {
681 assert_eq!(map::<OptionBrand, _, _, _>(|x: i32| x + 1, None), None);
682 }
683
684 /// Tests `bind` on `None`.
685 #[test]
686 fn bind_none() {
687 assert_eq!(bind::<OptionBrand, _, _, _>(None, |x: i32| Some(x + 1)), None);
688 }
689
690 /// Tests `bind` returning `None`.
691 #[test]
692 fn bind_returning_none() {
693 assert_eq!(bind::<OptionBrand, _, _, _>(Some(5), |_| None::<i32>), None);
694 }
695
696 /// Tests `fold_right` on `None`.
697 #[test]
698 fn fold_right_none() {
699 assert_eq!(
700 crate::classes::foldable::fold_right::<RcFnBrand, OptionBrand, _, _, _>(
701 |x: i32, acc| x + acc,
702 0,
703 None
704 ),
705 0
706 );
707 }
708
709 /// Tests `fold_left` on `None`.
710 #[test]
711 fn fold_left_none() {
712 assert_eq!(
713 crate::classes::foldable::fold_left::<RcFnBrand, OptionBrand, _, _, _>(
714 |acc, x: i32| acc + x,
715 0,
716 None
717 ),
718 0
719 );
720 }
721
722 /// Tests `traverse` on `None`.
723 #[test]
724 fn traverse_none() {
725 assert_eq!(
726 crate::classes::traversable::traverse::<OptionBrand, OptionBrand, _, _, _>(
727 |x: i32| Some(x + 1),
728 None
729 ),
730 Some(None)
731 );
732 }
733
734 /// Tests `traverse` returning `None`.
735 #[test]
736 fn traverse_returning_none() {
737 assert_eq!(
738 crate::classes::traversable::traverse::<OptionBrand, OptionBrand, _, _, _>(
739 |_: i32| None::<i32>,
740 Some(5)
741 ),
742 None
743 );
744 }
745
746 // ParFoldable Tests
747
748 /// Tests `par_fold_map` on `None`.
749 #[test]
750 fn par_fold_map_none() {
751 let x: Option<i32> = None;
752 let f = new_send::<ArcFnBrand, _, _>(|x: i32| x.to_string());
753 assert_eq!(par_fold_map::<ArcFnBrand, OptionBrand, _, _>(f, x), "".to_string());
754 }
755
756 /// Tests `par_fold_map` on `Some`.
757 #[test]
758 fn par_fold_map_some() {
759 let x = Some(5);
760 let f = new_send::<ArcFnBrand, _, _>(|x: i32| x.to_string());
761 assert_eq!(par_fold_map::<ArcFnBrand, OptionBrand, _, _>(f, x), "5".to_string());
762 }
763
764 /// Tests `par_fold_right` on `Some`.
765 #[test]
766 fn par_fold_right_some() {
767 let x = Some(5);
768 let f = new_send::<ArcFnBrand, _, _>(|(a, b): (i32, i32)| a + b);
769 assert_eq!(par_fold_right::<ArcFnBrand, OptionBrand, _, _>(f, 10, x), 15);
770 }
771}