naan/
monad.rs

1use crate::prelude::*;
2
3/// [`Monad`], but specialized to know at compile-time
4/// that the function contained in `AMB` will only be called one time.
5pub trait MonadOnce<M, A>: Monad<M, A>
6  where Self: Applicative<M, A>,
7        M: HKT1<T<A> = Self>
8{
9  /// See [`MonadOnce`]
10  fn bind1<B, AMB>(self, f: AMB) -> M::T<B>
11    where AMB: F1Once<A, Ret = M::T<B>>;
12
13  /// [`MonadOnce::bind1`] that [`Discard`]s the output of the function.
14  ///
15  /// For [`Result`], this allows you to easily perform side effects
16  /// that may fail without dropping the data in the Result.
17  ///
18  /// ```
19  /// use naan::prelude::*;
20  ///
21  /// fn log(s: &str) {
22  ///   println!("{s}")
23  /// }
24  ///
25  /// let name: Result<String, String> = Ok("hello".into());
26  /// name.discard(|n: &String| {
27  ///       log(n.as_str());
28  ///       Ok(())
29  ///     });
30  /// ```
31  fn discard<AMB, B>(self, f: AMB) -> M::T<A>
32    where Self: Sized,
33          B: Discard,
34          AMB: for<'a> F1Once<&'a A, Ret = M::T<B>>,
35          M::T<B>: MonadOnce<M, B>
36  {
37    self.bind1::<A, _>(|a| f.call1(&a).bind1::<A, _>(|_| M::T::<A>::pure(a)))
38  }
39
40  /// [`MonadOnce::discard`] with mutable access to the data
41  ///
42  /// ```
43  /// use naan::prelude::*;
44  ///
45  /// fn log(s: &str) {
46  ///   println!("{s}")
47  /// }
48  ///
49  /// let name: Result<String, String> = Ok("hello, ".into());
50  /// name.discard_mut(|hi: &mut String| {
51  ///       log(hi.as_str());
52  ///       *hi = format!("{hi}, world!");
53  ///       Ok(())
54  ///     })
55  ///     .discard(|n: &String| {
56  ///       log(n.as_str());
57  ///       Ok(())
58  ///     });
59  /// ```
60  fn discard_mut<AMB, B>(self, f: AMB) -> M::T<A>
61    where Self: Sized,
62          B: Discard,
63          AMB: for<'a> F1Once<&'a mut A, Ret = M::T<B>>,
64          M::T<B>: MonadOnce<M, B>
65  {
66    self.bind1::<A, _>(|mut a| f.call1(&mut a).bind1::<A, _>(|_| M::T::<A>::pure(a)))
67  }
68}
69
70/// `Monad` generalizes the concept of a *sequence of computations*,
71/// using the return value of one to determine the next.
72///
73/// For [`Result`](Result::and_then) and [`Option`](Option::and_then), `bind` is an alias for their
74/// `and_then` inherent methods.
75///
76/// ```no_run
77/// use std::fs::{File, OpenOptions};
78/// use std::io::{Read, Write};
79///
80/// use naan::prelude::*;
81///
82/// OpenOptions::new().append(true)
83///                   .open("./README.txt")
84///                   .bind(|mut file: File| {
85///                     write!(file, "\n## New Heading\n\nHello from example!").fmap1(|_| file)
86///                   })
87///                   .bind(|mut file: File| {
88///                     let mut s = String::new();
89///                     file.read_to_string(&mut s).fmap1(|_| s)
90///                   });
91/// ```
92pub trait Monad<M, A>
93  where Self: Applicative<M, A>,
94        M: HKT1<T<A> = Self>
95{
96  /// See [`Monad`]
97  fn bind<B, AMB>(self, f: AMB) -> M::T<B>
98    where AMB: F1<A, Ret = M::T<B>>;
99
100  /// Flatten a nested `Monad`
101  /// ```
102  /// use naan::prelude::*;
103  ///
104  /// assert_eq!(Some(Some("hello")).flatten(), Some("hello"));
105  /// ```
106  fn flatten<AA>(self) -> M::T<AA>
107    where Self: Sized,
108          M: HKT1<T<AA> = A> + HKT1<T<<M as HKT1>::T<AA>> = Self>
109  {
110    self.bind::<AA, _>(|s| s)
111  }
112}
113
114/// [`Monad`] but with looser type constraints,
115/// allowing for blanket [`Monad`] implementations
116/// on types [`Equiv`]alent to `M<A>`
117pub trait MonadSurrogate<M, A>
118  where Self: Equiv<To = M::T<A>> + ApplicativeSurrogate<M, A>,
119        M: HKT1
120{
121  /// Type yielded by `bind_` that isn't _exactly_ `M::T<B>`, but
122  /// is conceptually [`Equiv`]alent.
123  ///
124  /// The output type may use both type parameters, or only one.
125  ///
126  /// The reason we allow the output to be parameterized by `AMB` (the function from `A -> Self<B>`)
127  /// is so that the returning type can store **the function** and defer transformation.
128  ///
129  /// This allows implementing lazy monads with no heap dependency (ex. [`IO`])
130  type BindOutput<B, AMB>;
131
132  /// Use a function from `A -> M<B>` to transform something
133  /// akin to `M<A>` to something akin to `M<B>`.
134  fn bind_<B, AMB>(self, f: AMB) -> Self::BindOutput<B, AMB>
135    where AMB: F1<A, Ret = M::T<B>>,
136          Self::BindOutput<B, AMB>: Equiv<To = M::T<B>>;
137}