raskell 0.1.1

Haskell-style functional programming for Rust
Documentation
//! Haskell-style functional programming for Rust.
//!
//! [`hdo!`] turns a sequence of binds into a chain of monadic operations over
//! [`Option`], [`Result`] and [`Vec`]. The README lists the combinators, type
//! classes and types that are 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));
//! ```
//!
//! # 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` | Lifts a plain value into the block's monad. Must come last. |
//! | `expression` | A final expression without `;`, already monadic. Must come last. |
//!
//! # The block's result
//!
//! A block ends either in `pure value`, which lifts a plain value into the
//! block's monad, or in a final expression without a semicolon, which is
//! already an [`Option`], [`Result`] or [`Vec`] and is returned as it is:
//!
//! ```
//! use raskell::hdo;
//!
//! let lifted: Option<i32> = hdo! {
//!     x <- Some(10);
//!
//!     pure x + 1
//! };
//!
//! let monadic: Option<i32> = hdo! {
//!     x <- Some(10i32);
//!
//!     x.checked_add(1)
//! };
//!
//! assert_eq!(lifted, monadic);
//! ```
//!
//! The `;` is what tells the two apart: a last statement that keeps it is an
//! action, which leaves the block without a result and fails to compile.
//!
//! # Errors of different types
//!
//! Plain `<-` keeps the error type as it is. Use `<-?` to convert it through
//! [`From`], the way `?` does:
//!
//! ```
//! use raskell::hdo;
//!
//! #[derive(Debug, PartialEq)]
//! struct Inner;
//!
//! #[derive(Debug, PartialEq)]
//! struct Outer;
//!
//! impl From<Inner> for Outer {
//!     fn from(_: Inner) -> Self {
//!         Outer
//!     }
//! }
//!
//! fn inner() -> Result<i32, Inner> {
//!     Err(Inner)
//! }
//!
//! let result: Result<i32, Outer> = hdo! {
//!     x <-? inner();
//!
//!     pure x * 2
//! };
//!
//! assert_eq!(result, Err(Outer));
//! ```
//!
//! # Refutable patterns
//!
//! A refutable pattern filters an `Option` block:
//!
//! ```
//! use raskell::hdo;
//!
//! let result = hdo! {
//!     Some(x) <- Some(Some(10));
//!
//!     pure x * 2
//! };
//!
//! assert_eq!(result, Some(20));
//! ```
//!
//! In a `Result` block there is no obvious error to produce, so `throw` is
//! required:
//!
//! ```
//! use raskell::hdo;
//!
//! #[derive(Debug, PartialEq)]
//! enum Error {
//!     Missing,
//! }
//!
//! let result: Result<i32, Error> = hdo! {
//!     Some(x) <- Ok(None::<i32>) throw Error::Missing;
//!
//!     pure x * 2
//! };
//!
//! assert_eq!(result, Err(Error::Missing));
//! ```
//!
//! # Lists
//!
//! A `Vec` bind is a list comprehension, and `guard` filters it:
//!
//! ```
//! use raskell::hdo;
//!
//! 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)]);
//! ```
//!
//! # Type annotations
//!
//! Rust infers the bound value's type from the expression. When the expression
//! carries no type (`Err(..)` says nothing about the `Ok` side), annotate the
//! pattern:
//!
//! ```
//! use raskell::hdo;
//!
//! #[derive(Debug, PartialEq)]
//! enum Error {
//!     Failed,
//!     Missing,
//! }
//!
//! let result: Result<i32, Error> = hdo! {
//!     Some(x): Option<i32> <- Err(Error::Failed) throw Error::Missing;
//!
//!     pure x * 2
//! };
//!
//! assert_eq!(result, Err(Error::Failed));
//! ```
//!
//! # Actions
//!
//! A bare statement expression is sequenced, not discarded, so anything
//! monadic in statement position short-circuits the block:
//!
//! ```
//! use raskell::hdo;
//!
//! #[derive(Debug, PartialEq)]
//! struct Error;
//!
//! fn check() -> Result<(), Error> {
//!     Err(Error)
//! }
//!
//! let result: Result<i32, Error> = hdo! {
//!     check();
//!
//!     pure 1
//! };
//!
//! assert_eq!(result, Err(Error));
//! ```
//!
//! Bind it to `_` with a plain `let` to opt out:
//!
//! ```
//! # use raskell::hdo;
//! let mut empty: Vec<i32> = Vec::new();
//!
//! let result = hdo! {
//!     x <- Some(10);
//!
//!     let _ = empty.pop();
//!
//!     pure x
//! };
//!
//! assert_eq!(result, Some(10));
//! ```
//!
//! # Async
//!
//! Wrapping the statements in `async { .. }` expands the block into an
//! `async move` block,
//! so `.await` is allowed anywhere inside it: in bind expressions, `guard`
//! conditions and `pure` alike. The result is a plain future: `.await` it, or
//! spawn it.
//!
//! ```
//! use raskell::hdo;
//!
//! #[derive(Debug, PartialEq)]
//! enum ApiError {
//!     Store,
//!     TooSmall,
//! }
//!
//! #[derive(Debug, PartialEq)]
//! struct StoreError;
//!
//! impl From<StoreError> for ApiError {
//!     fn from(_: StoreError) -> Self {
//!         ApiError::Store
//!     }
//! }
//!
//! async fn fetch(value: i32) -> Result<i32, ApiError> {
//!     Ok(value)
//! }
//!
//! async fn from_store() -> Result<i32, StoreError> {
//!     Err(StoreError)
//! }
//!
//! let block = hdo!(async {
//!     x <- fetch(10).await;
//!     y <-? from_store().await;
//!
//!     guard x > 5 throw ApiError::TooSmall;
//!
//!     pure x + y
//! });
//!
//! let result: Result<i32, ApiError> = pollster::block_on(block);
//!
//! assert_eq!(result, Err(ApiError::Store));
//! ```
//!
//! A final expression ends an `async` block the same way, `.await` included:
//!
//! ```
//! use raskell::hdo;
//!
//! async fn fetch(value: i32) -> Option<i32> {
//!     Some(value)
//! }
//!
//! let block = hdo!(async {
//!     x <- fetch(10).await;
//!
//!     fetch(x * 2).await
//! });
//!
//! assert_eq!(pollster::block_on(block), Some(20));
//! ```
//!
//! Futures are not awaited implicitly: write the `.await` yourself. The block's
//! type has to be known, either from an annotation on the awaited value or from
//! the enclosing function's signature.
//!
//! # 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.

#![warn(missing_docs)]

// Lets macro expansion use `::raskell` paths inside this crate as well.
extern crate self as raskell;

#[doc(hidden)]
pub mod __private;

/// The common imports.
pub mod prelude {
    pub use crate::hdo;
}

pub use raskell_macros::hdo;

#[cfg(test)]
mod tests {
    //! `hdo!` must also work inside the crate that defines it.

    use crate::hdo;

    #[test]
    fn hdo_works_inside_the_support_crate() {
        let result = hdo! {
            x <- Some(10);

            guard x > 5;

            pure x * 2
        };

        assert_eq!(result, Some(20));
    }
}