Last active
September 8, 2018 09:04
-
-
Save d-plaindoux/1c1f5875b6bae1951bab3b463c56452a to your computer and use it in GitHub Desktop.
Scalas' for comprehension in Rust
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[macro_export] | |
macro_rules! foreach { | |
($a:ident <- ($e:expr) if ($cond:expr) yield $result:expr) => {{ | |
$e.filter(|$a| $cond).map(|$a| $result) | |
}}; | |
($a:ident <- ($e:expr) yield $result:expr) => {{ | |
$e.map(|$a| $result) | |
}}; | |
($a:ident <- ($e:expr) $($r:tt)+) => {{ | |
$e.flat_map(|$a| foreach!($($r)+)) | |
}} | |
} | |
#[test] | |
fn it_apply_foreach() { | |
let r = foreach!( | |
a <- (1..=2) | |
b <- (1..=a) | |
if (*b < 2) | |
yield b | |
); | |
} | |
// | |
// Borrowing limitation | |
// | |
#[test] | |
fn it_apply_foreach() { | |
let r = foreach!( | |
a <- (1..=2) | |
b <- (1..=5) | |
yield (a,b) | |
// ^ borrowed value does not live long enough | |
); | |
} |
Little improvements during afternoon break (playground):
#[macro_export]
macro_rules! foreach {
($a:ident <- ($e:expr) if ($cond:expr) $($r:tt)+) => {{
foreach!($a <- ($e.filter(move |&$a| $cond)) $($r)+)
}};
($a:ident <- ($e:expr) yield $result:expr) => {{
$e.map(move |$a| $result)
}};
($a:ident <- ($e:expr) $($r:tt)+) => {{
$e.flat_map(move |$a| foreach!($($r)+))
}}
}
#[test]
fn with_a_1to3_and_b_1toa_b_not_2_yield_couple() {
let r: Vec<_> = foreach!(
a <- (1..=3)
b <- (1..=a)
if (b != 2)
yield (a,b)
).collect();
assert_eq!(vec![(1,1),(2,1),(3,1),(3,3)], r);
}
#[test]
fn with_a_1to5_and_a_is_even_and_b_1toa_b_not_2_yield_mult() {
let r: Vec<_> = foreach!(
a <- (1..=5)
if (a % 2 == 0)
b <- (1..=a)
if (b != 2)
yield a*b
).collect();
assert_eq!(vec![2*1,4*1,4*3,4*4], r);
}
Another improvement (kind of variable elision).
#[macro_export]
macro_rules! foreach {
($a:ident <- ($e:expr) if ($cond:expr) $($r:tt)+) => {{
foreach!($a <- ($e.satisfy(move |&$a| $cond)) $($r)+)
}};
($a:ident <- ($e:expr) yield $result:expr) => {{
$e.fmap(move |$a| $result)
}};
($a:ident <- ($e:expr) $($r:tt)+) => {{
$e.bind(move |$a| foreach!($($r)+))
}};
(($e:expr) yield $result:expr) => {{
$e.fmap(move |_| $result)
}};
(($e:expr) $($r:tt)+) => {{
$e.bind(move |_| foreach!($($r)+))
}}
}
Then in my parser combinator library I can write:
#[test]
fn it_parse_any_then_any_macro_seq() {
let r = foreach!(
('\'')
b <- (any()) if (b as char != '\'')
('\'')
yield (b)
);
assert_eq!('b', r.execute(&"'b'".as_bytes(), 0).fold(
|b, _, _| b as char,
|_, _| panic!("Parse error"),
));
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You need to tell Rust you want your lambda to capture its context (move used values):
move
must not be useful for firstflat_map
but it's required for more than 2 variables.