1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
//! The operational library makes it easy to implement monads with tricky control flow.
//!
//! This is very useful for: writing web applications in a sequential style,
//! programming games with a uniform interface for human and AI players and easy replay,
//! implementing fast parser monads, designing monadic DSLs, etc.
//!
//! A thorough introduction to the ideas behind this library is given in
//! ["The Operational Monad Tutorial"](http://apfelmus.nfshost.com/articles/operational-monad.html),
//! published in [Issue 15 of the Monad.Reader](http://themonadreader.wordpress.com/2010/01/26/issue-15/).

use std::fmt;

/// Contains the `Instr` trait, and a few useful utilities for working with instructions.
pub mod instr;
use instr::Instr;

mod kleisli;
pub use kleisli::Kleisli;

mod seq;
pub use seq::*;

/// Represents a program, i.e. a sequence of instructions.
///
/// - The _instructions_ are given by the type `I`.
/// - `A` is the return type of the program.
pub enum Program<'a, I: Instr, A> {
    /// The case `Pure(a)` means that the program contains no instructions and just returns the result `a`.
    Pure(Box<A>),
    /// The case `Then(instr, k)` means that the first instruction is `instr` and the remaining program is given by the kleisli arrow `k`.
    Then(Box<I>, Kleisli<'a, I, I::Return, A>)
}

impl<'a, I: 'a + Instr, A> Program<'a, I, A> {

    fn and_then_boxed<B, F>(self, js: F) -> Program<'a, I, B>
        where F: 'a + Fn(Box<A>) -> Program<'a, I, B> {
        match self {
            Program::Pure(a) => js(a),
            Program::Then(i, is) => Program::Then(i, kleisli::append_boxed(is, js))
        }
    }

    /// Appends a continuation to a program. Which means,
    /// given a function from `A` to `Program<I, B>`,
    /// passes the return value of the program to the function,
    /// and returns the resulting program.
    ///
    /// Equivalent to the monadic `>>=` operator.
    pub fn and_then<B, F>(self, js: F) -> Program<'a, I, B>
        where F: 'a + Fn(A) -> Program<'a, I, B> {
        match self {
            Program::Pure(a) => js(*a),
            Program::Then(i, is) => Program::Then(i, is.append(js))
        }
    }

    /// Modifies the return value of the program.
    /// Seen differently, it lifts a function from
    /// `A` to `B` into a function from `Program<I, A>`
    /// to `Program<I, B>`.
    ///
    /// Equivalent to the monadic `liftM`.
    pub fn map<B, F>(self, f: F) -> Program<'a, I, B>
        where F: 'a + Fn(A) -> B {
        self.and_then(move |a| point(f(a)))
    }

}

impl<'a, I: 'a + Instr, A: PartialEq> PartialEq for Program<'a, I, A> {
    fn eq(&self, other: &Program<'a, I, A>) -> bool {
        match (self, other) {
            (&Program::Pure(ref a), &Program::Pure(ref b)) => a == b,
            _ => false
        }
    }
}

impl<'a, I: 'a + Instr, A: fmt::Debug> fmt::Debug for Program<'a, I, A> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Program::Pure(ref a) => write!(f, "Pure({:?})", a),
            &Program::Then(_, _) => write!(f, "Then(..)")
        }
    }
}

/// Using a value, constructs the empty program,
/// i.e. a program that directly returns that value.
///
/// Equivalent to the monadic `return`.
pub fn point<'a, I: 'a + Instr, A>(a: A) -> Program<'a, I, A> {
    Program::Pure(Box::new(a))
}

/// Lift an instruction into a program.
pub fn lift<'a, I: 'a + Instr>(i: I) -> Program<'a, I, I::Return> {
    Program::Then(Box::new(i), Kleisli::new())
}