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::{
8		Apply as TypeclassApply, ApplyFirst, ApplySecond, Bind, Foldable, Functor, Pure,
9	},
10};
11use std::sync::Arc;
12
13impl_brand!(OptionBrand, Option, Kind1, Brand1, (A));
14
15impl Pure for OptionBrand {
16	/// # Examples
17	///
18	/// ```
19	/// use fp_library::{brands::OptionBrand, functions::pure};
20	///
21	/// assert_eq!(
22	///     pure::<OptionBrand, _>(()),
23	///     Some(())
24	/// );
25	fn pure<A>(a: A) -> Apply<Self, (A,)>
26	where
27		Self: Kind<(A,)>,
28	{
29		<Self as Brand<_, _>>::inject(Some(a))
30	}
31}
32
33impl Functor for OptionBrand {
34	/// # Examples
35	///
36	/// ```
37	/// use fp_library::{brands::OptionBrand, functions::{identity, map}};
38	///
39	/// assert_eq!(
40	///     map::<OptionBrand, _, _, _>(identity::<()>)(None),
41	///     None
42	/// );
43	/// assert_eq!(
44	///     map::<OptionBrand, _, _, _>(identity)(Some(())),
45	///     Some(())
46	/// );
47	/// ```
48	fn map<F, A, B>(f: F) -> impl Fn(Apply<Self, (A,)>) -> Apply<Self, (B,)>
49	where
50		Self: Kind<(A,)> + Kind<(B,)>,
51		F: Fn(A) -> B,
52	{
53		move |fa| <Self as Brand<_, _>>::inject(<Self as Brand<_, _>>::project(fa).map(&f))
54	}
55}
56
57impl TypeclassApply for OptionBrand {
58	/// # Examples
59	///
60	/// ```
61	/// use fp_library::{brands::OptionBrand, functions::{apply, identity}};
62	///
63	/// assert_eq!(
64	///     apply::<OptionBrand, fn(()) -> (), _, _>(None)(None),
65	///     None
66	/// );
67	/// assert_eq!(
68	///     apply::<OptionBrand, fn(()) -> (), _, _>(None)(Some(())),
69	///     None
70	/// );
71	/// assert_eq!(
72	///     apply::<OptionBrand, _, _, _>(Some(identity::<()>))(None),
73	///     None
74	/// );
75	/// assert_eq!(
76	///     apply::<OptionBrand, _, _, _>(Some(identity))(Some(())),
77	///     Some(())
78	/// );
79	/// ```
80	fn apply<F, A, B>(ff: Apply<Self, (F,)>) -> impl Fn(Apply<Self, (A,)>) -> Apply<Self, (B,)>
81	where
82		Self: Kind<(F,)> + Kind<(A,)> + Kind<(B,)>,
83		F: Fn(A) -> B,
84		Apply<Self, (F,)>: Clone,
85	{
86		move |fa| match (<Self as Brand<_, _>>::project(ff.to_owned()), &fa) {
87			(Some(f), _) => map::<Self, F, _, _>(f)(fa),
88			_ => <Self as Brand<_, _>>::inject(None::<B>),
89		}
90	}
91}
92
93impl ApplyFirst for OptionBrand {
94	/// # Examples
95	///
96	/// ```
97	/// use fp_library::{brands::OptionBrand, functions::{apply_first, identity}};
98	///
99	/// assert_eq!(
100	///     apply_first::<OptionBrand, bool, bool>(None)(None),
101	///     None
102	/// );
103	/// assert_eq!(
104	///     apply_first::<OptionBrand, bool, _>(None)(Some(false)),
105	///     None
106	/// );
107	/// assert_eq!(
108	///     apply_first::<OptionBrand, _, bool>(Some(true))(None),
109	///     None
110	/// );
111	/// assert_eq!(
112	///     apply_first::<OptionBrand, _, _>(Some(true))(Some(false)),
113	///     Some(true)
114	/// );
115	/// ```
116	fn apply_first<A, B>(fa: Apply<Self, (A,)>) -> impl Fn(Apply<Self, (B,)>) -> Apply<Self, (A,)>
117	where
118		Self: Kind<(A,)> + Kind<(B,)>,
119		Apply<Self, (A,)>: Clone,
120	{
121		move |fb| {
122			<Self as Brand<_, (A,)>>::inject(
123				match (
124					<Self as Brand<_, _>>::project(fa.to_owned()),
125					<Self as Brand<_, (B,)>>::project(fb),
126				) {
127					(Some(a), Some(_)) => Some(a),
128					_ => None,
129				},
130			)
131		}
132	}
133}
134
135impl ApplySecond for OptionBrand {
136	/// # Examples
137	///
138	/// ```
139	/// use fp_library::{brands::OptionBrand, functions::{apply_second, identity}};
140	///
141	/// assert_eq!(
142	///     apply_second::<OptionBrand, bool, bool>(None)(None),
143	///     None
144	/// );
145	/// assert_eq!(
146	///     apply_second::<OptionBrand, bool, _>(None)(Some(false)),
147	///     None
148	/// );
149	/// assert_eq!(
150	///     apply_second::<OptionBrand, _, bool>(Some(true))(None),
151	///     None
152	/// );
153	/// assert_eq!(
154	///     apply_second::<OptionBrand, _, _>(Some(true))(Some(false)),
155	///     Some(false)
156	/// );
157	/// ```
158	fn apply_second<A, B>(_fa: Apply<Self, (A,)>) -> impl Fn(Apply<Self, (B,)>) -> Apply<Self, (B,)>
159	where
160		Self: Kind<(A,)> + Kind<(B,)>,
161		Apply<Self, (A,)>: Clone,
162	{
163		move |fb| {
164			<Self as Brand<_, (B,)>>::inject(
165				match (
166					<Self as Brand<_, (A,)>>::project(_fa.to_owned()),
167					<Self as Brand<_, (B,)>>::project(fb),
168				) {
169					(Some(_), Some(a)) => Some(a),
170					_ => None,
171				},
172			)
173		}
174	}
175}
176
177impl Bind for OptionBrand {
178	/// # Examples
179	///
180	/// ```
181	/// use fp_library::{brands::OptionBrand, functions::{bind, pure}};
182	///
183	/// assert_eq!(
184	///     bind::<OptionBrand, _, _, _>(None)(pure::<OptionBrand, ()>),
185	///     None
186	/// );
187	/// assert_eq!(
188	///     bind::<OptionBrand, _, _, _>(Some(()))(pure::<OptionBrand, _>),
189	///     Some(())
190	/// );
191	/// ```
192	fn bind<F, A, B>(ma: Apply<Self, (A,)>) -> impl Fn(F) -> Apply<Self, (B,)>
193	where
194		Self: Kind<(A,)> + Kind<(B,)> + Sized,
195		F: Fn(A) -> Apply<Self, (B,)>,
196		Apply<Self, (A,)>: Clone,
197	{
198		move |f| {
199			<Self as Brand<_, _>>::inject(
200				<Self as Brand<_, _>>::project(ma.to_owned())
201					.and_then(|a| -> Option<B> { <Self as Brand<_, _>>::project(f(a)) }),
202			)
203		}
204	}
205}
206
207impl Foldable for OptionBrand {
208	/// # Examples
209	///
210	/// ```
211	/// use fp_library::{brands::OptionBrand, functions::fold_right};
212	/// use std::sync::Arc;
213	///
214	/// assert_eq!(
215	///     fold_right::<OptionBrand, _, _>(Arc::new(|a| Arc::new(move |b| a + b)))(1)(Some(1)),
216	///     2
217	/// );
218	/// assert_eq!(
219	///     fold_right::<OptionBrand, i32, _>(Arc::new(|a| Arc::new(move |b| a + b)))(1)(None),
220	///     1
221	/// );
222	/// ```
223	fn fold_right<'a, A, B>(
224		f: crate::aliases::ClonableFn<'a, A, crate::aliases::ClonableFn<'a, B, B>>
225	) -> crate::aliases::ClonableFn<'a, B, crate::aliases::ClonableFn<'a, Apply<Self, (A,)>, B>>
226	where
227		Self: 'a + Kind<(A,)>,
228		A: 'a + Clone,
229		B: 'a + Clone,
230		Apply<Self, (A,)>: 'a,
231	{
232		Arc::new(move |b| {
233			Arc::new({
234				let f = f.clone();
235				move |fa| match (f.clone(), b.to_owned(), <OptionBrand as Brand<_, _>>::project(fa))
236				{
237					(_, b, None) => b,
238					(f, b, Some(a)) => f(a)(b),
239				}
240			})
241		})
242	}
243}