Skip to main content

karpal_core/
macros.rs

1/// Monadic do-notation sugar.
2///
3/// Desugars sequential `x = expr` bindings into nested `Chain::chain` calls.
4///
5/// # Example
6/// ```ignore
7/// do_! { OptionF;
8///     x = Some(1);
9///     y = Some(x + 1);
10///     OptionF::pure(x + y)
11/// }
12/// ```
13#[macro_export]
14macro_rules! do_ {
15    // Terminal: just an expression (no more bindings)
16    ($F:ty; $e:expr) => {
17        $e
18    };
19    // Binding: x = expr; rest...
20    ($F:ty; $x:ident = $e:expr; $($rest:tt)+) => {
21        <$F as $crate::chain::Chain>::chain($e, |$x| {
22            $crate::do_!($F; $($rest)+)
23        })
24    };
25}
26
27/// Applicative do-notation sugar.
28///
29/// Collects independent bindings and combines them with `ap`/`fmap`.
30/// Supports 1–4 bindings followed by `yield expr`.
31///
32/// # Example
33/// ```ignore
34/// ado_! { OptionF;
35///     x = Some(1);
36///     y = Some(2);
37///     yield x + y
38/// }
39/// ```
40#[macro_export]
41macro_rules! ado_ {
42    // 1 binding
43    ($F:ty; $x:ident = $e:expr; yield $body:expr) => {
44        <$F as $crate::functor::Functor>::fmap($e, |$x| $body)
45    };
46    // 2 bindings
47    ($F:ty; $x1:ident = $e1:expr; $x2:ident = $e2:expr; yield $body:expr) => {
48        <$F as $crate::apply::Apply>::ap(
49            <$F as $crate::functor::Functor>::fmap($e1, |$x1| move |$x2| $body),
50            $e2,
51        )
52    };
53    // 3 bindings
54    ($F:ty;
55        $x1:ident = $e1:expr;
56        $x2:ident = $e2:expr;
57        $x3:ident = $e3:expr;
58        yield $body:expr
59    ) => {
60        <$F as $crate::apply::Apply>::ap(
61            <$F as $crate::apply::Apply>::ap(
62                <$F as $crate::functor::Functor>::fmap($e1, |$x1| move |$x2| move |$x3| $body),
63                $e2,
64            ),
65            $e3,
66        )
67    };
68    // 4 bindings
69    ($F:ty;
70        $x1:ident = $e1:expr;
71        $x2:ident = $e2:expr;
72        $x3:ident = $e3:expr;
73        $x4:ident = $e4:expr;
74        yield $body:expr
75    ) => {
76        <$F as $crate::apply::Apply>::ap(
77            <$F as $crate::apply::Apply>::ap(
78                <$F as $crate::apply::Apply>::ap(
79                    <$F as $crate::functor::Functor>::fmap($e1, |$x1| {
80                        move |$x2| move |$x3| move |$x4| $body
81                    }),
82                    $e2,
83                ),
84                $e3,
85            ),
86            $e4,
87        )
88    };
89}
90
91#[cfg(test)]
92mod tests {
93    use crate::applicative::Applicative;
94    use crate::hkt::OptionF;
95    #[cfg(any(feature = "std", feature = "alloc"))]
96    use crate::hkt::VecF;
97
98    #[test]
99    fn do_option_some() {
100        let result = do_! { OptionF;
101            x = Some(1);
102            y = Some(x + 1);
103            OptionF::pure(x + y)
104        };
105        assert_eq!(result, Some(3));
106    }
107
108    #[test]
109    fn do_option_none() {
110        let result: Option<i32> = do_! { OptionF;
111            x = Some(1);
112            _y = None::<i32>;
113            OptionF::pure(x)
114        };
115        assert_eq!(result, None);
116    }
117
118    #[test]
119    fn do_option_single() {
120        let result = do_! { OptionF;
121            Some(42)
122        };
123        assert_eq!(result, Some(42));
124    }
125
126    #[test]
127    fn do_vec() {
128        let result = do_! { VecF;
129            x = vec![1, 2];
130            y = vec![10, 20];
131            VecF::pure(x + y)
132        };
133        assert_eq!(result, vec![11, 21, 12, 22]);
134    }
135
136    #[test]
137    fn ado_option_1() {
138        let result = ado_! { OptionF;
139            x = Some(5);
140            yield x * 2
141        };
142        assert_eq!(result, Some(10));
143    }
144
145    #[test]
146    fn ado_option_2() {
147        let result = ado_! { OptionF;
148            x = Some(1);
149            y = Some(2);
150            yield x + y
151        };
152        assert_eq!(result, Some(3));
153    }
154
155    #[test]
156    fn ado_option_2_none() {
157        let result = ado_! { OptionF;
158            x = Some(1);
159            y = None::<i32>;
160            yield x + y
161        };
162        assert_eq!(result, None);
163    }
164
165    #[test]
166    fn ado_option_3() {
167        let result = ado_! { OptionF;
168            x = Some(1);
169            y = Some(2);
170            z = Some(3);
171            yield x + y + z
172        };
173        assert_eq!(result, Some(6));
174    }
175
176    #[test]
177    fn ado_option_4() {
178        let result = ado_! { OptionF;
179            a = Some(1);
180            b = Some(2);
181            c = Some(3);
182            d = Some(4);
183            yield a + b + c + d
184        };
185        assert_eq!(result, Some(10));
186    }
187
188    #[test]
189    fn ado_vec_2() {
190        let result = ado_! { VecF;
191            x = vec![1, 2];
192            y = vec![10, 20];
193            yield x + y
194        };
195        assert_eq!(result, vec![11, 21, 12, 22]);
196    }
197}