do_notation/
lib.rs

1//! # `do-notation`, the monadic `do` notation brought to Rust.
2//!
3//! This crate provides the `m!` macro, which provides the Haskell monadic syntactic sugar `do`.
4//!
5//! > Note: it is not possible to use the `do!` syntax as `do` is a reserved keyword in Rust.
6//!
7//! The syntax is very similar to what you find in Haskell:
8//!
9//! - You use the `m!` macro; in Haskell, you use the `do` keyword.
10//! - The `<-` syntactic sugar binds its left hand side to the monadic right hand side
11//!   by _entering_ the right side via a closure.
12//! - Like almost any statement in Rust, you must end your statement with a semicolon (`;`).
13//! - The last line must be absent of `;` or contains the `return` keyword.
14//! - You can use `return` nowhere but on the last line.
15//! - A line containing a single expression with a semicolon is a valid statement and has the same effect as `_ <- expr`.
16//! - `let` bindings are allowed in the form `let <pattern> = <expr>;` and have the regular Rust meaning.
17//!
18//! ## How do I make my monad works with `m!`?
19//!
20//! Because monads are higher-kinded types, it is not possible to define the monadic do-notation in a fully type-system
21//! elegant way. However, this crate is based on the rebindable concept in Haskell (i.e. you can change what the `>>=`
22//! operator’s types are), so `m!` has one type-system requirement and one syntactic requirement.
23//!
24//! First, you have to implement one trait: [`Lift`], which allows to _lift_ a value `A` into a _monadic structure of
25//! `A`_. For instance, lifting a `A` into the `Option` monad yields an `Option<A>`.
26//!
27//! Then, you have to provide an `and_then` method, which is akin to Haskell’s `>>=` operator. The choice of using
28//! `and_then` and not a proper name like `flat_map` or `bind` is due to the current state of the standard-library —
29//! monads like `Option` and `Result<_, E>` don’t have `flat_map` defined on them but have `and_then`. The type signature
30//! is not enforced, but:
31//!
32//! - `and_then` must be a binary function taking a type `A`, a closure `A -> Monad<B>` and returns `Monad<B>`, where
33//!   `Monad` is the monad you are adding `and_then` for. For instance, if you are implementing it for `Option`,
34//!   `and_then` takes an `A`, a closure `A -> Option<B>` and returns an `Option<B>`.
35//! - `and_then` must move its first argument, which has to be `self`. The type of `Self` is not enforced.
36//! - `and_then`’s closure must take `A` with a `FnOnce` closure.
37//!
38//! ## Meaning of the `<-` operator
39//!
40//! The `<-` syntactic sugar is not strictly speaking an operator: it’s not valid vanilla Rust. Instead, it’s a trick
41//! defined in the `m!` allowing to use both [`Lift::lift`] and `and_then`. When you look at code inside a do-notation
42//! block, every monadic statements (separated with `;` in this crate) can be imagined as a new level of nesting inside
43//! a closure — the one passed to `and_then`, indeed.
44//!
45//! ## First example: fallible code
46//!
47//! One of the first monadic application that people learn is the _fallible_ effect — `Maybe` in Haskell.
48//! In `Rust`, it’s `Option`. `Option` is an interesting monad as it allows you to fail early.
49//!
50//! ```rust
51//! use do_notation::m;
52//!
53//! let r = m! {
54//!   x <- Some("Hello, world!");
55//!   y <- Some(3);
56//!   Some(x.len() * y)
57//! };
58//!
59//! assert_eq!(r, Some(39));
60//! ```
61//!
62//! The `binding <- expr` syntax unwraps the right part and binds it to `binding`, making it available to
63//! next calls — remember, nested closures. The final line re-enters the structure (here, `Option`) explicitly.
64//!
65//! Note that it is possible to re-enter the structure without having to specify how / knowing the structure
66//! (with `Option`, you re-enter with `Some`). You can use the `return` keyword, that will automatically lift the
67//! value into the right structure:
68//!
69//! ```rust
70//! use do_notation::m;
71//!
72//! let r = m! {
73//!   x <- Some(1);
74//!   y <- Some(2);
75//!   z <- Some(3);
76//!   return [x, y, z];
77//! };
78//!
79//! assert_eq!(r, Some([1, 2, 3]));
80//! ```
81
82#[macro_export]
83macro_rules! m {
84  // return
85  (return $r:expr ;) => {
86    $crate::Lift::lift($r)
87  };
88
89  // let-binding
90  (let $p:pat = $e:expr ; $($r:tt)*) => {{
91    let $p = $e;
92    m!($($r)*)
93  }};
94
95  // const-bind
96  (_ <- $x:expr ; $($r:tt)*) => {
97    $x.and_then(move |_| { m!($($r)*) })
98  };
99
100  // bind
101  ($binding:ident <- $x:expr ; $($r:tt)*) => {
102    $x.and_then(move |$binding| { m!($($r)*) })
103  };
104
105  // const-bind
106  ($e:expr ; $($a:tt)*) => {
107    $e.and_then(move |_| m!($($a)*))
108  };
109
110  // pure
111  ($a:expr) => {
112    $a
113  }
114}
115
116/// Lift a value inside a monad.
117pub trait Lift<A> {
118  /// Lift a value into a default structure.
119  fn lift(a: A) -> Self;
120}
121
122impl<A> Lift<A> for Option<A> {
123  fn lift(a: A) -> Self {
124    Some(a)
125  }
126}
127
128impl<A, E> Lift<A> for Result<A, E> {
129  fn lift(a: A) -> Self {
130    Ok(a)
131  }
132}
133
134#[cfg(test)]
135mod tests {
136  use super::*;
137
138  #[test]
139  fn option() {
140    let r: Option<i32> = m! {
141      v <- Some(3);
142      Some(v)
143    };
144
145    assert_eq!(r, Some(3));
146
147    let r: Option<i32> = m! {
148      v <- r;
149      x <- Some(10);
150      Some(v * x)
151    };
152
153    assert_eq!(r, Some(30));
154
155    let n: Option<i32> = None;
156    let r: Option<i32> = m! {
157      v <- Some(314);
158      x <- n;
159      Some(v * x)
160    };
161
162    assert_eq!(r, None);
163
164    let r = m! {
165      _ <- Some("a");
166      b <- Some("b");
167      _ <- Option::<&str>::None;
168      Some(b)
169    };
170
171    assert_eq!(r, None);
172
173    let r = m! {
174      _ <- Some("a");
175      return "b";
176    };
177
178    assert_eq!(r, Some("b"));
179  }
180
181  #[test]
182  fn result() {
183    let r: Result<i32, &str> = m! {
184      v <- Ok(3);
185      Ok(v)
186    };
187
188    assert_eq!(r, Ok(3));
189
190    let r: Result<i32, &str> = m! {
191      v <- r;
192      x <- Ok(10);
193      Ok(v * x)
194    };
195
196    assert_eq!(r, Ok(30));
197
198    let n: Result<i32, &str> = Err("error");
199    let r: Result<i32, &str> = m! {
200      v <- Ok(314);
201      x <- n;
202      Ok(v * x)
203    };
204
205    assert_eq!(r, Err("error"));
206
207    let r = m! {
208      _ <- Result::<&str, &str>::Ok("a");
209      b <- Ok("b");
210      _ <- Result::<&str, &str>::Err("nope");
211      Ok(b)
212    };
213
214    assert_eq!(r, Err("nope"));
215
216    fn guard<E>(cond: bool, err: E) -> Result<(), E> {
217      if cond {
218        Ok(())
219      } else {
220        Err(err)
221      }
222    }
223
224    let r = m! {
225      x <- Ok(true);
226      _ <- guard(1 == 2, "meh");
227      Ok(x)
228    };
229
230    assert_eq!(r, Err("meh"));
231  }
232
233  #[test]
234  fn instruction_counter() {
235    struct IC<A> {
236      count: usize,
237      value: A,
238    }
239
240    impl<A> IC<A> {
241      fn new(value: A) -> Self {
242        IC { count: 1, value }
243      }
244
245      fn value(&self) -> &A {
246        &self.value
247      }
248
249      fn count(&self) -> usize {
250        self.count
251      }
252
253      fn and_then<B>(self, f: impl FnOnce(A) -> IC<B>) -> IC<B> {
254        let r = f(self.value);
255
256        IC {
257          count: self.count + r.count,
258          value: r.value,
259        }
260      }
261    }
262
263    impl<A> Lift<A> for IC<A> {
264      fn lift(value: A) -> Self {
265        Self::new(value)
266      }
267    }
268
269    let ic = m! {
270      a <- IC::new(10);
271      b <- IC::new(2);
272      IC::new(a + b)
273    };
274
275    assert_eq!(ic.value(), &12);
276    assert_eq!(ic.count(), 3);
277
278    let ic = m! {
279      _ <- IC::new("a");
280      let x = 2;
281
282      // test statements
283      let y = if 1 == 1 { 3 } else { 0 };
284
285      _ <- IC::new("b");
286      _ <- IC::new("c");
287
288      return [1, x, y];
289    };
290
291    assert_eq!(ic.value(), &[1, 2, 3]);
292    assert_eq!(ic.count(), 4);
293  }
294}