fp_library/types/
option.rs

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