-
Notifications
You must be signed in to change notification settings - Fork 8
/
lib.rs
270 lines (235 loc) · 6.5 KB
/
lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
#![deny(unsafe_code, nonstandard_style)]
#![forbid(rust_2018_idioms)]
#![warn(unreachable_pub, missing_debug_implementations)]
#![allow(rustdoc::bare_urls)]
#![doc = include_str!("../README.md")]
pub use higher_derive::{Bifunctor, BifunctorRef, Functor, FunctorRef};
pub mod semigroup;
#[doc(inline)]
pub use crate::semigroup::Semigroup;
pub mod monoid;
#[doc(inline)]
pub use crate::monoid::Monoid;
pub mod functor;
#[doc(inline)]
pub use crate::functor::{Functor, FunctorRef};
pub mod contra;
#[doc(inline)]
pub use crate::contra::Contravariant;
pub mod bifunctor;
#[doc(inline)]
pub use crate::bifunctor::{Bifunctor, BifunctorRef};
pub mod profunctor;
#[doc(inline)]
pub use crate::profunctor::Profunctor;
pub mod pure;
#[doc(inline)]
pub use crate::pure::Pure;
pub mod apply;
#[doc(inline)]
pub use crate::apply::Apply;
pub mod bind;
#[doc(inline)]
pub use crate::bind::Bind;
pub mod applicative;
#[doc(inline)]
pub use crate::applicative::Applicative;
pub mod monad;
#[doc(inline)]
pub use crate::monad::Monad;
pub mod foldable;
#[doc(inline)]
pub use crate::foldable::Foldable;
pub mod traversable;
#[doc(inline)]
pub use crate::traversable::Traversable;
pub mod error;
#[doc(inline)]
pub use crate::error::{ApplicativeError, MonadError};
pub mod alt;
#[doc(inline)]
pub use crate::alt::Alt;
pub mod plus;
#[doc(inline)]
pub use crate::plus::Plus;
pub mod alternative;
#[doc(inline)]
pub use crate::alternative::Alternative;
pub mod monadplus;
#[doc(inline)]
pub use crate::monadplus::MonadPlus;
pub mod algebras;
pub mod rings;
/// Monadic do notation.
///
/// This macro provides some syntactic sugar to make monads easier to read and
/// write.
///
/// It takes a series of expressions evaluating to monads, separated by
/// semicolons, and chains them together using [`bind`](Bind::bind).
///
/// The last expression may be preceded by the `yield` keyword, which will
/// automatically wrap the result up into a monad using [`pure`](Pure::pure).
/// Otherwise it must be a plain expression returning a monad.
///
/// Expressions before the last may be binding expressions, binding the result
/// of the monadic computation to a named variable available to the subsequent
/// expressions. If they don't have a binding, the result of the computation is
/// discarded. A binding expression looks like this:
///
/// ```text
/// variable <= expression;
/// ```
///
/// # Examples
///
/// The simplest example of monadic do notation is using the [`Option`](Option)
/// type. It will run through the list of expressions as long as they keep
/// evaluating to [`Some`](Option::Some), but if an expression should return
/// [`None`](Option::None), it will discard the subsequent computations and
/// immediately return [`None`](Option::None).
///
/// We'll illustrate with integer division using
/// [`usize::checked_div`](usize::checked_div), which returns an
/// `Option<usize>`:
///
/// ```
/// # use higher::run;
/// # assert_eq!(
/// run! {
/// x <= 16usize.checked_div(2);
/// // x = 16 / 2 = 8
/// y <= x.checked_div(2);
/// // y = x / 2 = 8 / 2 = 4
/// z <= y.checked_div(2);
/// // z = y / 2 = 4 / 2 = 2
/// yield x + y + z
/// // returns Some(x + y + z) = Some(8 + 4 + 2) = Some(14)
/// }
/// # , Some(14));
/// ```
///
/// And for a failing example, when we divide by zero:
///
/// ```
/// # use higher::run;
/// # assert_eq!(
/// run! {
/// x <= 16usize.checked_div(2);
/// // x = 16 / 2 = 8
/// y <= x.checked_div(0);
/// // Division by zero returns None, the remaining expressions are ignored
/// z <= y.checked_div(2);
/// yield x + y + z
/// // returns None
/// }
/// # , None);
/// ```
#[macro_export]
macro_rules! run {
($binding:ident <= <$coerce:ident> $comp:expr; $($tail:tt)*) => {
$crate::Bind::bind::<$coerce, _>($comp, move |$binding| run!($($tail)*))
};
($binding:ident <= $comp:expr; $($tail:tt)*) => {
$crate::Bind::bind($comp, move |$binding| run!($($tail)*))
};
(<$coerce:ident> $comp:expr; $($tail:tt)*) => {
$crate::Bind::bind::<$coerce, _>($comp, move |_| run!($($tail)*))
};
($comp:expr; $($tail:tt)*) => {
$crate::Bind::bind($comp, move |_| run!($($tail)*))
};
(yield $result:expr) => {
$crate::Pure::pure($result)
};
($result:expr) => {
$result
};
}
/// Construct a function that ignores its argument and returns the same value
/// every time you call it.
///
/// You may know this function as `const` in certain other languages.
///
/// ```
/// # use higher::repeat;
/// let f = repeat(31337);
/// assert_eq!(f("Joe"), 31337);
/// assert_eq!(f("Mike"), 31337);
/// assert_eq!(f("Robert"), 31337);
/// assert_eq!(f("Bjarne"), 31337);
/// ```
pub fn repeat<A: Clone, B>(value: A) -> impl Fn(B) -> A {
move |_| value.clone()
}
#[cfg(test)]
mod test {
#[test]
fn do_notation() {
// The standard list monad test.
assert_eq!(
run! {
x <= vec![1, 2];
y <= vec![x, x + 1];
yield (x, y)
},
vec![(1, 1), (1, 2), (2, 2), (2, 3)]
);
// Option with yield.
assert_eq!(
run! {
x <= 25u32.checked_div(2);
y <= x.checked_div(3);
z <= 9u32.checked_div(y);
yield x + y + z
},
Some(18)
);
// Option which fails.
assert_eq!(
run! {
x <= 5u32.checked_div(2);
y <= x.checked_div(8);
z <= 9u32.checked_div(y);
yield x + y + z
},
None
);
// Option with manual wrap.
assert_eq!(
run! {
x <= 25u32.checked_div(2);
y <= x.checked_div(3);
z <= 9u32.checked_div(y);
Some(x + y + z)
},
Some(18)
);
// Option without binding.
assert_eq!(
run! {
2u32.checked_div(2);
2u32.checked_div(1)
},
Some(2)
);
// Option without binding which fails.
assert_eq!(
run! {
2u32.checked_div(0);
2u32.checked_div(1)
},
None
);
// Options with different types.
assert_eq!(
run! {
s <= Some("64");
x <= s.parse::<u32>().ok();
y <= x.checked_div(2);
yield y
},
Some(32)
);
}
}