Skip to content

Instantly share code, notes, and snippets.

@huonw
Forked from bvssvni/gist:9674632
Created March 21, 2014 03:53
Show Gist options
  • Save huonw/9679206 to your computer and use it in GitHub Desktop.
Save huonw/9679206 to your computer and use it in GitHub Desktop.
//! 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));
}
@bvssvni
Copy link

bvssvni commented Mar 21, 2014

I updated the syntax, see the original gist:

https://gist.github.com/bvssvni/9674632

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment