use std::iter::{IntoIterator, Iterator, FlatMap};
use std::collections::{LinkedList, VecDeque};
pub trait Bind: IntoIterator {
fn bind<U, F>(self, f: F) -> FlatMap<Self::IntoIter, U, F>
where
F: Fn(Self::Item) -> U,
U: IntoIterator,
Self: Sized {
self.into_iter().flat_map( f)
}
}
impl<R> Bind for R where R: IntoIterator {}
pub trait Monad: Bind {
fn pure(x: Self::Item) -> Self;
}
impl<T> Monad for Option<T> {
fn pure(x: T) -> Self {
Some(x)
}
}
impl<T,E> Monad for Result<T,E>{
fn pure(x: T) -> Self {
Ok(x)
}
}
impl<T> Monad for Vec<T>{
fn pure(x: T) -> Self {
let mut v = Self::new();
v.push(x);
v
}
}
impl<T> Monad for LinkedList<T>{
fn pure(x: T) -> Self {
let mut v = Self::new();
v.push_front(x);
v
}
}
impl<T> Monad for VecDeque<T>{
fn pure(x: T) -> Self {
let mut v = Self::new();
v.push_front(x);
v
}
}
pub trait MZero: Monad {
fn mzero() -> Self;
}
impl<T> MZero for Option<T> {
fn mzero() -> Self {None}
}
impl<T> MZero for Vec<T> {
fn mzero() -> Self {Self::new()}
}
impl<T> MZero for LinkedList<T> {
fn mzero() -> Self {Self::new()}
}
impl<T> MZero for VecDeque<T> {
fn mzero() -> Self {Self::new()}
}
pub trait MPlus: MZero {
fn mplus(&mut self, _: &mut Self) ;
}
impl<T> MPlus for Vec<T> {
fn mplus(&mut self, other: &mut Self) {
self.append( other);
}
}
impl<T> MPlus for LinkedList<T> {
fn mplus(&mut self, other: &mut Self) {
self.append( other);
}
}
impl<T> MPlus for VecDeque<T> {
fn mplus(&mut self, other: &mut Self) {
self.append( other);
}
}
#[macro_export]
macro_rules! mdo {
(pure $e:expr ) => [Option::pure($e)];
(let $v:ident = $e:expr ; $($rest:tt)*) => [Option::pure($e).bind( move |$v| { mdo!($($rest)*)} )];
(guard $boolean:expr ; $($rest:tt)*) => [(if $boolean {Some(())} else {None}).bind( move |_| { mdo!($($rest)*)} )];
(_ <- $monad:expr ; $($rest:tt)* ) => [($monad).bind( move |_| { mdo!($($rest)*)} )];
(&$v:ident <- $monad:expr ; $($rest:tt)* ) => [($monad).bind( move |&$v| { mdo!($($rest)*)} )];
($v:ident <- pure $e:expr ; $($rest:tt)* ) => [Option::pure($e).bind( move |$v| { mdo!($($rest)*)} )];
($v:ident <- $monad:expr ; $($rest:tt)* ) => [($monad).bind( move |$v| { mdo!($($rest)*)} )];
($monad:expr ) => [$monad];
}
#[cfg(test)]
mod tests {
use crate::monad::{Bind, Monad};
use quickcheck::quickcheck;
quickcheck!{
fn prop_monad_comprehension_vs_iteration( xs: Vec<i32>) -> bool {
let ys = mdo!{
&v <- &xs;
guard v < 4;
pure v * 2
}.collect::<Vec<i32>>();
let zs = (&xs).into_iter().filter(|&v| v < &4).map(|v| v*2).collect::<Vec<i32>>();
ys == zs
}
}
}