Monads, Functors, & More in Stable Rust
Haskell-style monads with macro-free Rust syntax.
Types work with zero annotations
A fancy example (that compiles and runs as a test in gallery.rs):
assert_eq!;
Desugared do-notation:
assert_eq!;
The logic of Haskell lists with the speed of Rust vectors:
use *;
let li = list!;
assert_eq!;
N-fold bind without type annotations:
// from the wonderful Haskell docs: https://en.wikibooks.org/wiki/Haskell/Understanding_monads/List
assert_eq!;
assert_eq!;
And even the notoriously tricky join-in-terms-of-bind with no type annotations necessary:
let li = list!; // List<List<u8>>
let joined = li.join; // --> List<u8>!
assert_eq!;
Syntactic sugar
- Rust requires
>>=to be self-modifying, so we use>>instead.return(keyword) becomesconsumeand Haskell's>>becomes&. - Function-application order is standardized across the board to match
>>=instead of<$>: it'sx.fmap(f)to meanf <$> x.- I think this makes it easier to read as a chain of computations, but I'm not dead-set on it, and the type system would work either way.
- For functors, you can use
fmap(f, x)orx.fmap(f), or you can pipe it:x % f % g % .... Applicativeuses*instead of<*>(or the explicit methodtie).Alternativeuses|instead of<|>(or the explicit methodeither).- Monoids use
+instead of<>.
Use
Just write a monad! { ... and you get all its superclasses (Functor, Applicative, Alternative, ...) for free, plus property-based tests of the monad laws:
use *;
monad!
// And these just work:
// Monad
assert_eq;
assert_eq;
assert_eq;
// Functor
assert_eq!;
assert_eq!;
Rust-specific monads
Catch panics without worrying about the details:
assert_eq!;
assert_eq!;
Sharp edges
Right now, you can use >> for bind only when you have a concrete instance of Monad like Maybe but not a general <M: Monad<A>>.
The latter still works but requires an explicit call to m.bind(f) (or, if you don't use the trait, Monad::<A>::bind(m, f)).
This should be fixed with the Rust's non-lifetime binder feature when it rolls out.
"Cannot find type ... in this scope" in a doctest
Doctests try to guess where to place a fn main { ... } if you don't provide one, and sometimes it reads an rsmonad macro as something that should be in a main block.
Try adding an explicit fn main () { ... } around the statements you want to run.
If you don't want fn main() { ... } to show up in documentation but can't fix this error, comment it out:
/// monad! { ... }
/// # fn main {
/// a();
/// b();
/// c();
/// # }
#![no_std]
Disable default features:
# Cargo.toml
[]
= { = "*", = false }
Note that this will also disable List,though this is probably what you want: we can't know its length at compile time (that's the point of its bind implementation), so it requires a heap.
An alloc feature is in the works for #![no_std] extern crate alloc; crates, but it's not finalized yet.