fp_library/types/
option.rs

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