macro_rules! bind { (return $e:expr, if $cond:expr;) => { ... }; (return $e:expr;) => { ... }; (let $x:ident : $t:ty = $e:expr; $($rest:tt)+) => { ... }; (let $p:pat = $e:expr; $($rest:tt)+) => { ... }; (for $p:pat in $e:expr , if $cond:expr ; $($rest:tt)+) => { ... }; (for $p:pat in $e:expr; $($rest:tt)+) => { ... }; ($s:stmt; $($rest:tt)+) => { ... }; ($e:expr) => { ... }; }
Expand description
Bind macro. Allows for a more natural syntax for monadic composition.
It is similar to the do
notation in Haskell or the for
notation in Scala.
Usage
use rust2fun::prelude::*;
let actual = bind! {
for x in Some(1);
for y in Some(2);
x + y
};
assert_eq!(Some(3), actual);
let actual = bind! {
for x in Some(1);
for y in None::<i32>;
x + y
};
assert_eq!(None, actual);
The syntax supports pattern matching, can bind variables and contain statements.
use rust2fun::prelude::*;
let actual = bind! {
for (_, a) in Some((1, 2));
let b = 3;
std::println!("a = {}, b = {}", a, b);
a + b
};
assert_eq!(Some(5), actual);
Guards can be implemented using an if statement within a bind discarding the result.
use rust2fun::prelude::*;
let actual = bind! {
for x in Some(1);
for _guard in if x > 0 { Some(()) } else { None };
x
};
assert_eq!(Some(1), actual);
… or using the special if
syntax for monoids.
use rust2fun::prelude::*;
let actual = bind! {
for x in Some(1);
for _guard in Some(()), if x > 0;
x
};
assert_eq!(Some(1), actual);
The last example can be rewritten with the return syntax as follows:
use rust2fun::prelude::*;
let actual = bind! {
for x in Some(1);
return Some(x), if x > 0;
};
assert_eq!(Some(1), actual);
The return keyword is used to return the result of the last bind.
use rust2fun::prelude::*;
let actual = bind! {
for x in Some(1);
for y in Some(2);
return Some(x + y);
};
assert_eq!(Some(3), actual);
Examples
use rust2fun::prelude::*;
fn get_opening_prices() -> Vec<(AssetId, i32)> {
vec![(1, 225), (2, 310), (3, 128), (4, 99), (5, 200), (6, 0)]
}
fn get_closing_prices() -> Vec<(AssetId, i32)> {
vec![(5, 210), (3, 130), (2, 308), (4, 100), (1, 220)]
}
fn get_asset_name(id: AssetId) -> Option<String> {
match id {
1 => Some("AAPL".to_string()),
2 => Some("MSFT".to_string()),
3 => Some("GOOG".to_string()),
4 => Some("AMZN".to_string()),
_ => None,
}
}
let profits = bind! {
for (id_open, opening_price) in get_opening_prices();
for (id_close, closing_price) in get_closing_prices();
let diff = closing_price - opening_price;
for name in get_asset_name(id_open).into_iter().collect::<Vec<_>>(),
if id_open == id_close && diff > 0;
(name, diff)
};
assert_eq!(vec![("GOOG".to_string(), 2), ("AMZN".to_string(), 1)], profits);