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
101
102
103
104
105
106
//! [Church numerals] and arithmetic operations
//!
//! Church encoding of natural numbers and arithmetic operations.
//!
//! * `zero`
//! * `one`
//! * `succ`
//! * `pred`
//! * `is_zero`
//!
//! [Church numerals]: https://en.wikipedia.org/wiki/Church_encoding#Church_numerals

use std::collections::HashSet;

use church_encoded::boolean::{False, True};
use environment::Binding;
use term::{app, lam, var, Term};

/// Creates a set of bindings for all data types, data structures and operators
/// implemented in this module.
pub fn default_bindings() -> HashSet<Binding> {
    binds! {
        Zero => zero(),
        zero => zero(),
        One => one(),
        one => one(),
        SUCC => succ(),
        succ => succ(),
        PRED => pred(),
        pred => pred(),
        IsZero => is_zero(),
        is_zero => is_zero(),
    }
}

/// Number : 0 : apply f no times to a
///
/// ZERO ≡ λfa.a
pub fn zero() -> Term {
    lam("f", lam("a", var("a")))
}

/// Number : 1 : apply f once to a
///
/// ONE ≡ λfa.fa
pub fn one() -> Term {
    lam("f", lam("a", app(var("f"), var("a"))))
}

macro_rules! church_num {
    ($($typ:ty),*) => {
        $(
            impl From<$typ> for Term {
                fn from(val: $typ) -> Self {
                    lam("f", lam("a", (0..val).fold(var("a"), |acc, _| app(var("f"), acc))))
                }
            }
        )*
    }
}

church_num!(u8, u16, u32, u64, u128, usize);

/// Successor : n + 1
///
/// SUCC ≡ λnfa.f(nfa)
pub fn succ() -> Term {
    lam(
        "n",
        lam(
            "f",
            lam("a", app(var("f"), app![var("n"), var("f"), var("a")])),
        ),
    )
}

/// Predecessor : n - 1
///
/// PRED ≡ λnfa.n (λgh.h (g f)) (λu.a) (λu.u)
pub fn pred() -> Term {
    lam(
        "n",
        lam(
            "f",
            lam(
                "a",
                app![
                    var("n"),
                    lam("g", lam("h", app(var("h"), app(var("g"), var("f"))))),
                    lam("u", var("a")),
                    lam("u", var("u"))
                ],
            ),
        ),
    )
}

/// IsZero : Predicate for numeral zero
///
/// IsZero ≡ λn.n (λa.False) True
pub fn is_zero() -> Term {
    lam("n", app![var("n"), lam("a", False()), True()])
}

#[cfg(test)]
mod tests;