operational/lib.rs
1//! The operational library makes it easy to implement monads with tricky control flow.
2//!
3//! This is very useful for: writing web applications in a sequential style,
4//! programming games with a uniform interface for human and AI players and easy replay,
5//! implementing fast parser monads, designing monadic DSLs, etc.
6//!
7//! A thorough introduction to the ideas behind this library is given in
8//! ["The Operational Monad Tutorial"](http://apfelmus.nfshost.com/articles/operational-monad.html),
9//! published in [Issue 15 of the Monad.Reader](http://themonadreader.wordpress.com/2010/01/26/issue-15/).
10
11use std::fmt;
12
13/// Contains the `Instr` trait, and a few useful utilities for working with instructions.
14pub mod instr;
15use instr::Instr;
16
17mod kleisli;
18pub use kleisli::Kleisli;
19
20mod seq;
21pub use seq::*;
22
23/// Represents a program, i.e. a sequence of instructions.
24///
25/// - The _instructions_ are given by the type `I`.
26/// - `A` is the return type of the program.
27pub enum Program<'a, I: Instr, A> {
28 /// The case `Pure(a)` means that the program contains no instructions and just returns the result `a`.
29 Pure(Box<A>),
30 /// The case `Then(instr, k)` means that the first instruction is `instr` and the remaining program is given by the kleisli arrow `k`.
31 Then(Box<I>, Kleisli<'a, I, I::Return, A>)
32}
33
34impl<'a, I: 'a + Instr, A> Program<'a, I, A> {
35
36 fn and_then_boxed<B, F>(self, js: F) -> Program<'a, I, B>
37 where F: 'a + Fn(Box<A>) -> Program<'a, I, B> {
38 match self {
39 Program::Pure(a) => js(a),
40 Program::Then(i, is) => Program::Then(i, kleisli::append_boxed(is, js))
41 }
42 }
43
44 /// Appends a continuation to a program. Which means,
45 /// given a function from `A` to `Program<I, B>`,
46 /// passes the return value of the program to the function,
47 /// and returns the resulting program.
48 ///
49 /// Equivalent to the monadic `>>=` operator.
50 pub fn and_then<B, F>(self, js: F) -> Program<'a, I, B>
51 where F: 'a + Fn(A) -> Program<'a, I, B> {
52 match self {
53 Program::Pure(a) => js(*a),
54 Program::Then(i, is) => Program::Then(i, is.append(js))
55 }
56 }
57
58 /// Modifies the return value of the program.
59 /// Seen differently, it lifts a function from
60 /// `A` to `B` into a function from `Program<I, A>`
61 /// to `Program<I, B>`.
62 ///
63 /// Equivalent to the monadic `liftM`.
64 pub fn map<B, F>(self, f: F) -> Program<'a, I, B>
65 where F: 'a + Fn(A) -> B {
66 self.and_then(move |a| point(f(a)))
67 }
68
69}
70
71impl<'a, I: 'a + Instr, A: PartialEq> PartialEq for Program<'a, I, A> {
72 fn eq(&self, other: &Program<'a, I, A>) -> bool {
73 match (self, other) {
74 (&Program::Pure(ref a), &Program::Pure(ref b)) => a == b,
75 _ => false
76 }
77 }
78}
79
80impl<'a, I: 'a + Instr, A: fmt::Debug> fmt::Debug for Program<'a, I, A> {
81 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
82 match self {
83 &Program::Pure(ref a) => write!(f, "Pure({:?})", a),
84 &Program::Then(_, _) => write!(f, "Then(..)")
85 }
86 }
87}
88
89/// Using a value, constructs the empty program,
90/// i.e. a program that directly returns that value.
91///
92/// Equivalent to the monadic `return`.
93pub fn point<'a, I: 'a + Instr, A>(a: A) -> Program<'a, I, A> {
94 Program::Pure(Box::new(a))
95}