fp_library/types/
option.rs

1//! Implementations for [`Option`].
2
3use crate::{
4	functions::map,
5	hkt::{Apply, Brand, Brand1, Kind, Kind1},
6	impl_brand,
7	typeclasses::{Apply as TypeclassApply, ApplyFirst, ApplySecond, Bind, Functor, Pure},
8};
9
10impl_brand!(OptionBrand, Option, Kind1, Brand1, (A));
11
12impl Pure for OptionBrand {
13	/// # Examples
14	///
15	/// ```
16	/// use fp_library::{brands::OptionBrand, functions::pure};
17	///
18	/// assert_eq!(pure::<OptionBrand, _>(()), Some(()));
19	fn pure<A>(a: A) -> Apply<Self, (A,)>
20	where
21		Self: Kind<(A,)>,
22	{
23		<Self as Brand<_, _>>::inject(Some(a))
24	}
25}
26
27impl Functor for OptionBrand {
28	/// # Examples
29	///
30	/// ```
31	/// use fp_library::{brands::OptionBrand, functions::{identity, map}};
32	///
33	/// assert_eq!(map::<OptionBrand, _, _, _>(identity::<()>)(None), None);
34	/// assert_eq!(map::<OptionBrand, _, _, _>(identity)(Some(())), Some(()));
35	/// ```
36	fn map<F, A, B>(f: F) -> impl Fn(Apply<Self, (A,)>) -> Apply<Self, (B,)>
37	where
38		Self: Kind<(A,)> + Kind<(B,)>,
39		F: Fn(A) -> B,
40	{
41		move |fa| <Self as Brand<_, _>>::inject(<Self as Brand<_, _>>::project(fa).map(&f))
42	}
43}
44
45impl TypeclassApply for OptionBrand {
46	/// # Examples
47	///
48	/// ```
49	/// use fp_library::{brands::OptionBrand, functions::{apply, identity}};
50	///
51	/// assert_eq!(apply::<OptionBrand, fn(()) -> (), _, _>(None)(None), None);
52	/// assert_eq!(apply::<OptionBrand, fn(()) -> (), _, _>(None)(Some(())), None);
53	/// assert_eq!(apply::<OptionBrand, _, _, _>(Some(identity::<()>))(None), None);
54	/// assert_eq!(apply::<OptionBrand, _, _, _>(Some(identity))(Some(())), Some(()));
55	/// ```
56	fn apply<F, A, B>(ff: Apply<Self, (F,)>) -> impl Fn(Apply<Self, (A,)>) -> Apply<Self, (B,)>
57	where
58		Self: Kind<(F,)> + Kind<(A,)> + Kind<(B,)>,
59		F: Fn(A) -> B,
60		Apply<Self, (F,)>: Clone,
61	{
62		move |fa| match (<Self as Brand<_, _>>::project(ff.to_owned()), &fa) {
63			(Some(f), _) => map::<Self, F, _, _>(f)(fa),
64			_ => <Self as Brand<_, _>>::inject(None::<B>),
65		}
66	}
67}
68
69impl ApplyFirst for OptionBrand {
70	/// # Examples
71	///
72	/// ```
73	/// use fp_library::{brands::OptionBrand, functions::{apply_first, identity}};
74	///
75	/// assert_eq!(apply_first::<OptionBrand, bool, bool>(None)(None), None);
76	/// assert_eq!(apply_first::<OptionBrand, bool, _>(None)(Some(false)), None);
77	/// assert_eq!(apply_first::<OptionBrand, _, bool>(Some(true))(None), None);
78	/// assert_eq!(apply_first::<OptionBrand, _, _>(Some(true))(Some(false)), Some(true));
79	/// ```
80	fn apply_first<A, B>(fa: Apply<Self, (A,)>) -> impl Fn(Apply<Self, (B,)>) -> Apply<Self, (A,)>
81	where
82		Self: Kind<(A,)> + Kind<(B,)>,
83		Apply<Self, (A,)>: Clone,
84	{
85		move |fb| {
86			<Self as Brand<_, (A,)>>::inject(
87				match (
88					<Self as Brand<_, _>>::project(fa.to_owned()),
89					<Self as Brand<_, (B,)>>::project(fb),
90				) {
91					(Some(a), Some(_)) => Some(a),
92					_ => None,
93				},
94			)
95		}
96	}
97}
98
99impl ApplySecond for OptionBrand {
100	/// # Examples
101	///
102	/// ```
103	/// use fp_library::{brands::OptionBrand, functions::{apply_second, identity}};
104	///
105	/// assert_eq!(apply_second::<OptionBrand, bool, bool>(None)(None), None);
106	/// assert_eq!(apply_second::<OptionBrand, bool, _>(None)(Some(false)), None);
107	/// assert_eq!(apply_second::<OptionBrand, _, bool>(Some(true))(None), None);
108	/// assert_eq!(apply_second::<OptionBrand, _, _>(Some(true))(Some(false)), Some(false));
109	/// ```
110	fn apply_second<A, B>(_fa: Apply<Self, (A,)>) -> impl Fn(Apply<Self, (B,)>) -> Apply<Self, (B,)>
111	where
112		Self: Kind<(A,)> + Kind<(B,)>,
113		Apply<Self, (A,)>: Clone,
114	{
115		move |fb| {
116			<Self as Brand<_, (B,)>>::inject(
117				match (
118					<Self as Brand<_, (A,)>>::project(_fa.to_owned()),
119					<Self as Brand<_, (B,)>>::project(fb),
120				) {
121					(Some(_), Some(a)) => Some(a),
122					_ => None,
123				},
124			)
125		}
126	}
127}
128
129impl Bind for OptionBrand {
130	/// # Examples
131	///
132	/// ```
133	/// use fp_library::{brands::OptionBrand, functions::{bind, pure}};
134	///
135	/// assert_eq!(bind::<OptionBrand, _, _, _>(None)(pure::<OptionBrand, ()>), None);
136	/// assert_eq!(bind::<OptionBrand, _, _, _>(Some(()))(pure::<OptionBrand, _>), Some(()));
137	/// ```
138	fn bind<F, A, B>(ma: Apply<Self, (A,)>) -> impl Fn(F) -> Apply<Self, (B,)>
139	where
140		Self: Kind<(A,)> + Kind<(B,)> + Sized,
141		F: Fn(A) -> Apply<Self, (B,)>,
142		Apply<Self, (A,)>: Clone,
143	{
144		move |f| {
145			<Self as Brand<_, _>>::inject(
146				<Self as Brand<_, _>>::project(ma.to_owned())
147					.and_then(|a| -> Option<B> { <Self as Brand<_, _>>::project(f(a)) }),
148			)
149		}
150	}
151}