raskell 0.1.0

Haskell-style functional programming for Rust
Documentation

raskell

Haskell-style functional programming for Rust: combinators, type classes and types.

Implemented: hdo!, do notation over Option, Result and Vec, with an async mode. See the roadmap for what is planned.

use raskell::hdo;

let result = hdo! {
    x <- Some(10);
    y <- Some(20);

    let sum = x + y;

    guard sum > 5;

    pure sum * 2
};

assert_eq!(result, Some(60));

hdo! chains binds over Option, Result and Vec.

Statements

Syntax Meaning
pattern <- expression; Bind. Short-circuits on None / Err, iterates over a Vec.
pattern <-? expression; Bind a Result, converting the error through From.
pattern <- expression throw error; Bind a Result whose pattern may fail to match.
pattern: Type <- expression; Bind with the bound value's type spelled out.
guard condition; Yield None (or drop the list element) unless condition holds.
guard condition throw error; Yield Err(error) unless condition holds.
let pattern[: Type] = expression; Plain let, no monad involved.
expression; An action, sequenced like a bind whose value is discarded.
pure expression The block's result. Required, and must come last.

hdo!(async { .. }) accepts the same statements, minus list binds.

Errors of different types

let result: Result<i32, Outer> = hdo! {
    x <-? inner();   // inner() -> Result<i32, Inner>, Outer: From<Inner>

    pure x * 2
};

Lists

let pairs: Vec<(i32, i32)> = hdo! {
    x <- vec![1, 2, 3];
    y <- vec![10, 20];

    guard x + y > 12;

    pure (x, y)
};

assert_eq!(pairs, vec![(1, 20), (2, 20), (3, 10), (3, 20)]);

Async

Wrapping the statements in async { .. } expands the block into an async move block, so .await works anywhere inside it. The result is a plain future: .await it or spawn it.

let result: Result<i32, ApiError> = hdo!(async {
    x <- fetch(10).await;
    y <-? from_store().await;      // ApiError: From<StoreError>

    guard x > 5 throw ApiError::TooSmall;

    pure x + y
})
.await;

Futures are not awaited implicitly; write the .await yourself.

Limitations

  • An async block binds Option and Result only: a list bind cannot short-circuit a future.
  • Lists are bound as Vec; other iterators need .collect() first.

See the crate documentation for the full reference.

Roadmap

Functions and combinators

map, filter, fold, compose, curry, flip, zip_with, maybe, either.

Type classes, Rust-style

Functor, Applicative, Monad, Foldable, Traversable, Semigroup, Monoid, as plain traits that compose with Iterator and the std traits.

Once Monad exists, hdo! should be defined in terms of it instead of the per-type HdoBind impls it dispatches on today.

Types

Either, NonEmpty, later Reader, State, Writer.

License

MIT