Skip to content

Instantly share code, notes, and snippets.

@d-plaindoux
Last active September 8, 2018 09:04
Show Gist options
  • Save d-plaindoux/1c1f5875b6bae1951bab3b463c56452a to your computer and use it in GitHub Desktop.
Save d-plaindoux/1c1f5875b6bae1951bab3b463c56452a to your computer and use it in GitHub Desktop.
Scalas' for comprehension in Rust
#[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
);
}
@d-plaindoux
Copy link
Author

d-plaindoux commented Sep 8, 2018

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