Crate comp [] [src]

Pure-macro Do notation and List-comprehension for Option, Result and Iterator.

It provides syntax extensions to easily combind wrapper type (Option, Result and Iterator), which seems like for-comprehension in scala or Do notation in haskell.

Usage

First, add the following to your Cargo.toml:

[dependencies]
comp = "0.1"

Next, add this to your crate root:

#[macro_use]
extern crate comp;

Example

comp-rs delivers three macros : option!, result! and iter!, transforming the arrow(<-) statements into FP binding( flat_map() ).

Iterator

#[macro_use]
extern crate comp;

let iter = iter! {
  let x <- 0..2u8;
  let y <- vec!['a', 'b'];
  (x, y)
};

for x in iter {
  println!("{:?}", x);
}

// Print (0, 'a') (0, 'b') (1, 'a') (1, 'b')

Option

#[macro_use]
extern crate comp;

let option = option! {
  let a <- Some(1);
  let b <- Some(2);
  a + b
};

assert_eq!(option, Some(3));

Result

Unlike Iterator and Option, rust provides Question Mark syntax to combine Results.

Let's see how comp-rs makes it more explicit and expressive.

Native way

use std::fs::File;
use std::io;
use std::io::prelude::*;

// try!() macro must be wrap into a function
fn content() -> io::Result<String> {
    let mut f = try!(File::open("foo.txt"));
    let mut s = String::new();
    try!(f.read_to_string(&mut s));
    Ok(s)
}

Question mark

use std::fs::File;
use std::io;
use std::io::prelude::*;

// '?' mark must be wrap into a function
fn content() -> io::Result<String> {
    let mut f = File::open("foo.txt")?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

result! way

use std::fs::File;
use std::io;
use std::io::prelude::*;

let content: io::Result<String> = result! {
  let mut f <- File::open("foo.txt");
  let mut s = String::new();
  let _ <- f.read_to_string(&mut s);
  s
};

Syntax

All three macros return wrapped type(Option<T>, Result<T> and Iterator<Item=T>), and yield the last expression.

Syntax: (sentence)* ; expression

sentence can be: * let pattern <- expression;: bind expression to pattern.

  • if filter_expression;: filter by condition, and jump over when not satisfied.

  • statement;: let assignment, value assignment, etc.

  • {...}: block and unsafe block.

Syntax Detail

1. Basic arrow(<-) syntax

Rules

Macro                      Expand
                  |
option! {}        |        Some(())
                  |
option! { x; }    |        { x; Some(()) }
                  |
option! { x }     |        Some(x)
                  |

Macro
------------------------------------
Expand

option! { let x <- Some(1); }
------------------------------------
Some(1).and_then(move |x| option!{})

option! { let x <- Some(1); x }
------------------------------------
Some(1).and_then(move |x| option!{ x })

option! { let mut x <- Some(1); x }
------------------------------------
Some(1).and_then(move |mut x| option!{ x })

Example

let option = option! {
  let a <- Some(1);
  let b <- Some(2);
  a + b
};

// code above is expanded roughly into this

let option = {
  Some(1).and_then(move |a| {
    Some(2).and_then(move |b| {
      Some(a + b)
    })
  })
};
let iter = iter! {
  let x <- 0..2;
  let y <- vec!['a', 'b'];
  (x, y)
};

// code above is expanded roughly into this

let iter = {
  (0..2).into_iter().flat_map(move |x| {
    (vec!['a', 'b']).into_iter().flat_map(move |y| {
      ::std::iter::once((x, y))
    })
  })
};

2. Yield

The last expression of the block will be yielded, similar to functions in rust.

let iter = iter! {
  let x <- 0..2;
  let y <- vec!['a', 'b'];

  (x, y)    // <------- Yield
};

The block yields () while the last line is arrow statement or statement with semicolon.

let option: Option<()> = option! {
  let a <- Some(1);
  let b <- Some(2);
};

let option: Option<()> = option! {
  let a <- Some(1);
  let b <- Some(2);
  a + b;
};

3. Pattern

In comp-rs, pattern is supported as it should be.

Tuple

let option = option! {
  let (x, y) <- Some((1, 2));
  (y, x)
};

assert_eq!(option, Some((2, 1)));

Struct

struct Struct { x: usize };

let option = option! {
  let Struct { x } <- Some(Struct { x: 1 });
  x
};

assert_eq!(option, Some(1));

Ignore

let option = option! {
  let _ <- Some(1);
};

4. If-Guard

If-Guard is specific for iter! which translates condition into filter().

It wraps the following code into a block and call filter() on it.

let iter = iter! {
  let x <- 0..4;
  let y <- 2..6;

  if x == y;

  // won't reach here if condition isn't satisfied
  (x, y)
};

let expected = vec![(2, 2), (3, 3)];
assert_eq!(expected, iter.collect::<Vec<_>>());

5. Statement & Block

Statements and blocks are also supported.

// statement
let iter = iter! {
  let start = 5;
  let end;
  end = start * 3;

  // 5, 6, ..., 13, 14
  let x <- start..end;
  x
};
let expected = 5..15;
assert!(iter.eq(expected.into_iter()));
let iter = iter! {
    let mut a <- 0..5;

    // block
    {
        fn double(x: u8) -> u8 { x * 2}
        let tmp = double(a);
        a = tmp;
    };

    // unsafe block
    let count = unsafe {
        static mut CALL_COUNT: u8 = 0;
        CALL_COUNT += 1;
        CALL_COUNT
    };

    (a, count)
};
let expected = vec![(0, 1), (2, 2), (4, 3), (6, 4), (8, 5)];
assert!(iter.eq(expected.into_iter()));

Array

Array in rust behaves differently from other collections. It only iterates its content by reference. So iter! always binds references in arrow(<-) syntax, then you need to deref the bound value. And since one can't move any value out of an array, array should be placed outside the macro to satisfy lifetime.

let array = [0, 1, 2, 3];
let iter = iter! {
    let x <- array;
    let y <- *x..4;
    (*x, y)
};
let expected = vec![(0, 0), (0, 1), (0, 2), (0, 3), (1, 1), (1, 2), (1, 3), (2, 2),
                    (2, 3), (3, 3)];
assert_eq!(expected, iter.collect::<Vec<_>>());

Contribution

All kinds of contribution are welcome.

  • Issue. Feel free to open an issue when you find typos, bugs, or have any question.
  • Pull requests. Better implementation, more tests, more documents and typo fixes are all welcome.

License

Licensed under MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)

Macros

iter

syntax extension specific for Iterator

option

syntax extension specific for Option

result

syntax extension specific for Result