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}