-
-
Save huonw/9679206 to your computer and use it in GitHub Desktop.
This file contains 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
//! Monad macro in Rust | |
//! | |
//! A typical usage is to avoid nested match expressions. | |
//! | |
//! Supports: | |
//! - Pattern matching | |
//! - Or expressions (A | B => ...) | |
//! - Guarded statements (x if <cond> => ...) | |
#[feature(macro_rules)]; | |
// The monad macro is syntactically similar to a 'match' statement. | |
// Each arm pattern matches against the result of the previous arm. | |
// If any pattern match fails, the value before the block is executed. | |
// | |
// When passing on a value to next arm, use Option. | |
// No colon is allowed after the last arm. | |
// The last arm does not need Option. | |
// | |
// #Example | |
// ``` | |
// use std::path::Path; | |
// use std::io::fs::File; | |
// use std::io::BufferedReader; | |
// use std::vec_ng::Vec; | |
// | |
// let lines: Vec<~str> = monad!( | |
// Vec::new() { | |
// _ => File::open(&Path::new("hello.txt")), | |
// Ok(file) => Some(BufferedReader::new(file)), | |
// Some(mut reader) => Some(reader.lines()), | |
// Some(mut lines) => Some(lines.map(|line| line.unwrap())), | |
// Some(lines) => Some(lines.map(|line| line.trim().into_owned())), | |
// Some(mut lines) => lines.collect() | |
// } | |
// ); | |
// ``` | |
// | |
// The macro is calling itself recursively. | |
macro_rules! monad( | |
// Base case (2 arms). | |
($err: expr { | |
_ => $start: expr, | |
$($a: pat)|+ $(if $c: expr)* => $b: expr | |
}) => { | |
match $start { | |
$($a)|+ $(if $c)* => $b, | |
_ => $err | |
} | |
}; | |
// Case for more than 2 arms. | |
($err: expr { | |
_ => $start: expr, | |
$($a_head: pat)|+ $(if $c_head: expr)* => | |
$b_head: expr $(, $($a: pat)|+ $(if $c: expr)* => $b: expr)+ | |
}) => { | |
match $start { | |
$($a_head)|+ $(if $c_head)* => monad!( $err { | |
_ => $b_head | |
$(, $($a)|+ $(if $c)* => $b)+ | |
} ), | |
_ => $err | |
} | |
} | |
) | |
#[allow(dead_code)] | |
fn main() { | |
use std::path::Path; | |
use std::io::fs::File; | |
use std::io::BufferedReader; | |
use std::vec_ng::Vec; | |
// Read lines from file, create empty vector if something goes wrong. | |
let lines: Vec<~str> = monad!( | |
Vec::new() { | |
_ => File::open(&Path::new("hello.txt")), | |
Ok(file) => Some(BufferedReader::new(file)), | |
Some(mut reader) => Some(reader.lines()), | |
Some(lines) => Some(lines.map(|line| line.unwrap())), | |
Some(lines) => Some(lines.map(|line| line.trim().into_owned())), | |
Some(mut lines) => lines.collect() | |
} | |
); | |
for line in lines.iter() { | |
println!("{}", line.trim()); | |
} | |
} | |
#[test] | |
pub fn test_1() { | |
let x: Option<int> = monad!( | |
None { | |
_ => Some(5), | |
Some(x) => Some(x + 1) | |
} | |
); | |
assert_eq!(x, Some(6)); | |
} | |
#[test] | |
pub fn test_2_pre() { | |
let x: Option<int> = match Some(5) { | |
Some(4) | Some(5) => Some(5), | |
_ => None | |
}; | |
assert_eq!(x, Some(5)); | |
} | |
#[test] | |
pub fn test_2() { | |
let x: Option<int> = monad!( | |
None { | |
_ => Some(5), | |
Some(4) | Some(5) => Some(4) | |
} | |
); | |
assert_eq!(x, Some(4)); | |
} | |
#[test] | |
pub fn test_3() { | |
let x: Option<int> = monad!( | |
None { | |
_ => Some(5), | |
Some(4) | Some(5) => Some(4), | |
Some(4) | Some(5) => Some(6) | |
} | |
); | |
assert_eq!(x, Some(6)); | |
} | |
#[test] | |
pub fn test_4() { | |
let x: Option<int> = monad!( | |
None { | |
_ => Some(5), | |
Some(x) if x == 5 => Some(x+1) | |
} | |
); | |
assert_eq!(x, Some(6)); | |
} | |
#[test] | |
pub fn test_5() { | |
let x: Option<int> = monad!( | |
None { | |
_ => Some(5), | |
Some(x) if x == 5 => Some(x+1), | |
Some(x) if x == 6 => Some(x+1) | |
} | |
); | |
assert_eq!(x, Some(7)); | |
} | |
#[test] | |
pub fn test_6() { | |
let x: Option<int> = monad!( | |
None { | |
_ => Some(5), | |
Some(x) if x == 5 => Some(x+1), | |
Some(6) | Some(7) => Some(7) | |
} | |
); | |
assert_eq!(x, Some(7)); | |
} | |
#[test] | |
pub fn test_7_pre() { | |
let x: Option<int> = match Some(5) { | |
Some(0..10) => Some(6), | |
_ => None, | |
}; | |
assert_eq!(x, Some(6)) | |
} | |
#[test] | |
pub fn test_7() { | |
let x: Option<int> = monad!( | |
None { | |
_ => Some(5), | |
Some(0..10) => Some(6) | |
} | |
); | |
assert_eq!(x, Some(6)); | |
} | |
#[test] | |
pub fn test_8() { | |
let x: Option<int> = monad!( | |
None { | |
_ => 5, | |
4 | 5 => Some(5) | |
} | |
); | |
assert_eq!(x, Some(5)); | |
} | |
#[test] | |
pub fn test_9() { | |
let x: Option<int> = monad!( | |
None { | |
_ => 5, | |
x @ 0..10 => Some(x) | |
} | |
); | |
assert_eq!(x, Some(5)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I updated the syntax, see the original gist:
https://gist.github.com/bvssvni/9674632