Macro map_for::map_for
[−]
[src]
macro_rules! map_for { (@process ($($move:tt)*) ($($n:ident),+) <- $e:expr; => $e0:expr) => { ... }; (@process ($($move:tt)*) ($($n:ident),+) <- $e:expr; if $g:expr; $($tail:tt)+) => { ... }; (@process ($($move:tt)*) ($($n:ident),+) <- $e:expr; ($($n0:ident),+) <- $e0:expr; $($tail:tt)+) => { ... }; (@process ($($move:tt)*) ($($n:ident),+) <- $e:expr; ($($n0:ident),+) = $e0:expr; $($tail:tt)+) => { ... }; (@process ($($move:tt)*) ($($n:ident),+) <- $e:expr; $n0:ident <- $e0:expr; $($tail:tt)+) => { ... }; (@process ($($move:tt)*) ($($n:ident),+) <- $e:expr; $n0:ident = $e0:expr; $($tail:tt)+) => { ... }; (@process ($($move:tt)*) $n:ident <- $e:expr; => $e0:expr) => { ... }; (@process ($($move:tt)*) $n:ident <- $e:expr; if $g:expr; $($tail:tt)+) => { ... }; (@process ($($move:tt)*) $n:ident <- $e:expr; ($($n0:ident),+) <- $e0:expr; $($tail:tt)+) => { ... }; (@process ($($move:tt)*) $n:ident <- $e:expr; ($($n0:ident),+) = $e0:expr; $($tail:tt)+) => { ... }; (@process ($($move:tt)*) $n:ident <- $e:expr; $n0:ident <- $e0:expr; $($tail:tt)+) => { ... }; (@process ($($move:tt)*) $n:ident <- $e:expr; $n0:ident = $e0:expr; $($tail:tt)+) => { ... }; (move; $($tail:tt)+) => { ... }; ($($tail:tt)+) => { ... }; }
Scala-like for comprehension similar to those described in https://stackoverflow.com/questions/3754089/scala-for-comprehension#3754568 The main difference is that since rust does not have partial functions, we do not support general patterns in the left-hand sides, but only single identifiers and list of identifiers (that will match a tuple).
The macro will work for any type that implements the map
,
flat_map
and filter
functions.
Examples
Basic usage
Mappings are defined using the <-
operator and the final value
is declared with the =>
operator:
let l = map_for!{ x <- 0..4; y <- 0..x; => y // y will take the following values depending on x: // x == 0 -> empty // x == 1 -> 0 // x == 2 -> 0, 1 // x == 3 -> 0, 1, 2 }.collect::<Vec<_>>(); assert_eq!(l, vec![ 0, 0, 1, 0, 1, 2 ]);
Moving or borrowing
By default, the generated closures will borrow their environment.
If you'd prefer the environment to be moved inside the closures,
you can specify move;
as the first statement (the semicolon is
here to make it clear that this will be applied globally to all
the closures generated by the macro).
let l = map_for!{ move; // Only for illustration, not strictly necessary here x <- 0..4; => x }.collect::<Vec<_>>(); assert_eq!(l, vec![ 0, 1, 2, 3 ]);
Regular binding
Regular bindings (i.e. direct bindings that don't require a call
to map
or flat_map
) can be inserted between mappings by using
the =
operator:
let l = map_for!{ move; x <- 0..4; y = 2*x; // This is directly translated into `let y = 2*x;` // without going through a mapping function. z <- 0..1; => y+z }.collect::<Vec<_>>(); assert_eq!(l, vec![ 0, 2, 4, 6 ]);
Tuple deconstruction
A limited form of pattern-matching is available to deconstruct single-level tuples:
use map_for::FlatMap; // Required to use an `Option` let e = map_for!{ (a, b) <- Some ((1, 2)); (c, d) = (3, 4); => a+b+c+d }; assert_eq!(e, Some (10));
Filtering
Mappings can be filtered using an if
statement:
let l = map_for!{ x <- 0..10; if (x%2) == 0; => x }.collect::<Vec<_>>(); assert_eq!(l, vec![ 0, 2, 4, 6, 8 ]);
Custom types
Except for filtering, map_for
will work with any type that
defines map
and flat_map
either directly or because it
implements a trait that defines those fuctions. Moreover filtering
will work with any type that also defines a filter
function:
#[derive (Debug, PartialEq)] enum MyMonad { Empty, Value (i32), } impl MyMonad { fn flat_map<F> (self, f: F) -> Self where F: FnOnce (i32) -> Self { use MyMonad::*; match self { Empty => Empty, Value (x) => f (x), } } fn map<F> (self, f: F) -> Self where F: FnOnce (i32) -> i32 { use MyMonad::*; match self { Empty => Empty, Value (x) => Value (f (x)), } } fn filter<P> (self, p: P) -> Self where P: FnOnce (i32) -> bool { // Only required to enable the `if` statement in `map_for` use MyMonad::*; match self { Value (x) if p (x) => self, _ => Empty, } } } use MyMonad::*; let c = map_for!{ a <- Value (1); if (a == 1); b <- Value (2); => a+b }; assert_eq!(c, Value (3));