#![no_std]
#![feature(fn_traits, unboxed_closures)]
#[cfg(test)]
#[macro_use]
extern crate std;
#[macro_export]
macro_rules! effect_map {
( $e:expr ) => {
move || $e
};
( $b:block ) => {
move || $b
};
}
pub enum ResolveFn<T> {
Const(T),
}
impl<T, Args> FnOnce<Args> for ResolveFn<T> {
type Output = T;
#[inline(always)]
extern "rust-call" fn call_once(self, _: Args) -> Self::Output {
use ResolveFn::Const;
match self {
Const(v) => v,
}
}
}
impl<T> From<T> for ResolveFn<T> {
fn from(v: T) -> Self {
use ResolveFn::Const;
Const(v)
}
}
pub trait EffectMonad<A>: Sized {
fn bind<B, Eb, F>(self, f: F) -> BoundEffect<Self, F>
where Eb: FnOnce() -> B,
F: FnOnce(A) -> Eb;
#[inline(always)]
fn bind_ignore_contents<B, Eb>(self, eb: Eb) -> BoundEffect<Self, ResolveFn<Eb>>
where Eb: FnOnce() -> B,
{
self.bind(eb.into())
}
}
impl<T, A> EffectMonad<A> for T
where T: FnOnce() -> A,
{
#[inline(always)]
fn bind<B, Eb, F>(self, f: F) -> BoundEffect<Self, F>
where Eb: FnOnce() -> B,
F: FnOnce(A) -> Eb,
{
bind_effects(self, f)
}
}
pub struct BoundEffect<Ea, F> {
ea: Ea,
f: F,
}
impl<A, B, Ea, Eb, F> FnOnce<()> for BoundEffect<Ea, F>
where Ea: FnOnce() -> A,
Eb: FnOnce() -> B,
F: FnOnce(A) -> Eb,
{
type Output = B;
extern "rust-call" fn call_once(self, _: ()) -> Self::Output {
let a_result = (self.ea)();
(self.f)(a_result)()
}
}
fn bind_effects<A, B, Ea, Eb, F>(first: Ea, f: F) -> BoundEffect<Ea, F>
where Ea: FnOnce() -> A,
Eb: FnOnce() -> B,
F: FnOnce(A) -> Eb,
{
BoundEffect {
ea: first,
f: f,
}
}
#[cfg(test)]
mod public_test {
use super::*;
#[test]
fn effect_monad_bind_performs() {
let mut x: isize = 0;
let px = &mut x as *mut isize;
(|| unsafe {
*px += 2;
}).bind_ignore_contents(|| unsafe {
*px -= 1;
})();
assert_eq!(x, 1);
}
#[test]
fn effect_monad_bind_performs_sequentially() {
let mut x: isize = 3;
let px = &mut x as *mut isize;
(|| unsafe {
*px *= 2;
}).bind_ignore_contents(|| unsafe {
*px -= 1;
})();
assert_eq!(x, 5);
}
#[test]
fn effect_monad_bind_binds() {
let mut x: isize = 0;
let px = &mut x as *mut isize;
(|| unsafe {
*px *= 2;
42
}).bind(|a: isize| {
move || unsafe {
*px = a
}
})();
assert_eq!(x, 42);
}
#[test]
fn println_can_be_mapped_as_effect() {
effect_map!(println!("hello")).bind_ignore_contents(effect_map!(println!("goodbye")))();
}
#[test]
fn effect_map_performs_effect() {
let mut x: isize = 0;
{
let px = &mut x;
effect_map!(*px += 1)();
}
assert_eq!(x, 1);
}
#[test]
fn effect_can_implicitly_borrow() {
let mut x = 1;
{
(|| {
x += 5;
})();
}
assert_eq!(x, 6);
}
#[test]
fn effect_map_compiles_block() {
let mut x: isize = 0;
{
let px = &mut x;
effect_map!({
*px = 42;
})()
}
assert_eq!(x, 42);
}
#[test]
fn effect_monad_bind_safely_chains_state() {
let mut x: isize = 0;
{
let px = &mut x;
(effect_map!({
*px = 6;
px
})).bind(|px| effect_map!(*px += 1))();
}
assert_eq!(x, 7);
}
}
#[test]
fn bind_effect_binds() {
let mut x: isize = 0;
let px = &mut x as *mut isize;
bind_effects(|| unsafe {
*px *= 2;
42
}, |a: isize| {
move || unsafe {
*px = a
}
})();
assert_eq!(x, 42);
}