# 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](#roadmap) for what is planned.
```rust
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
| `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` | Lifts a plain value into the block's monad. Must come last. |
| `expression` | A final expression without `;`, already monadic. Must come last. |
A block has to end in a result: either `pure value`, which lifts a plain value,
or a final expression without a semicolon, which is already an `Option`,
`Result` or `Vec` and is returned as it is.
```rust
let lifted: Option<i32> = hdo! {
x <- Some(10i32);
pure x + 1 // an i32, so `pure` wraps it
};
let monadic: Option<i32> = hdo! {
x <- Some(10i32);
x.checked_add(1) // already an Option<i32>, returned as it is
};
```
A last statement that keeps its `;` is still an action, so the block is missing
its result and fails to compile.
## Errors of different types
```rust
let result: Result<i32, Outer> = hdo! {
x <-? inner(); // inner() -> Result<i32, Inner>, Outer: From<Inner>
pure x * 2
};
```
## Lists
```rust
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.
```rust
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;
```
A final expression works there too, `.await` included:
```rust
let user: Result<User, Error> = hdo!(async {
id <-? get_id().await;
fetch_user(id).await
})
.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](https://docs.rs/raskell) 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