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
#![deny(unsafe_code, nonstandard_style)]
#![forbid(rust_2018_idioms)]
#![warn(unreachable_pub, missing_debug_implementations)]
#![allow(rustdoc::bare_urls)]
#![doc = include_str!("../README.md")]
#![cfg_attr(not(feature = "std"), no_std)]

pub use higher_derive::{Bifunctor, Functor};

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;

pub mod contra;
#[doc(inline)]
pub use crate::contra::Contravariant;

pub mod bifunctor;
#[doc(inline)]
pub use crate::bifunctor::Bifunctor;

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 algebras;
pub mod effect;
pub mod rings;

#[cfg(feature = "io")]
pub mod io;

/// 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 {
    (yield $result:expr) => {
        $crate::Pure::pure($result)
    };

    ($result:expr) => {
        $result
    };

    ($binding:ident <= $comp:expr; $($tail:tt)*) => {
        $crate::Bind::bind($comp, move |$binding| run!($($tail)*))
    };

    ($comp:expr; $($tail:tt)*) => {
        $crate::Bind::bind($comp, move |_| run!($($tail)*))
    }
}

#[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)
        );
    }
}