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
//! 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))
}