lambda_calculus

A simple, zero-dependency implementation of pure lambda calculus in Safe Rust


Keywords
mathematics, functional, combinators, lambda, calculus, beta-reduction, church-encoding, combinatory-logic, debruijn, embedded-iterators-encoding, lambda-calculus, lambda-expressions, lambda-functions, lambda-interpreter, parigot-encoding, reduction-strategies, rust, rust-library, scott-encoding, ski-combinators, stump-fu-encoding
License
CC0-1.0

Documentation

lambda_calculus

license current version docs.rs actively maintained

lambda_calculus is a simple, zero-dependency implementation of pure lambda calculus in Safe Rust.

Features

  • a parser for lambda expressions, both in classic and De Bruijn index notation
  • 7 尾-reduction strategies
  • a set of standard terms (combinators)
  • lambda-encoded boolean, pair, tuple, option and result data types
  • single-pair-encoded list
  • Church-, Scott- and Parigot-encoded numerals and lists
  • Stump-Fu (embedded iterators)- and binary-encoded numerals
  • signed numbers

Installation

Include the library by adding the following to your Cargo.toml:

[dependencies]
lambda_calculus = "3"

Compilation features:

  • backslash_lambda: changes the display of lambdas from to \
  • encoding: builds the data encoding modules; default feature

Example feature setup in Cargo.toml:

[dependencies.lambda_calculus]
version = "3"
default-features = false # do not build the data encoding modules
features = ["backslash_lambda"] # use a backslash lambda

Examples

Comparing classic and De Bruijn index notation

code:

use lambda_calculus::data::num::church::{succ, pred};

fn main() {
    println!("SUCC := {0} = {0:?}", succ());
    println!("PRED := {0} = {0:?}", pred());
}

stdout:

SUCC := 位a.位b.位c.b (a b c) = 位位位2(321)
PRED := 位a.位b.位c.a (位d.位e.e (d b)) (位d.c) (位d.d) = 位位位3(位位1(24))(位2)(位1)

Parsing lambda expressions

code:

use lambda_calculus::*;

fn main() {
    assert_eq!(
        parse(&"位a.位b.位c.b (a b c)", Classic),
        parse(&"位位位2(321)", DeBruijn)
    );
}

Showing 尾-reduction steps

code:

use lambda_calculus::*;
use lambda_calculus::data::num::church::pred;

fn main() {
    let mut expr = app!(pred(), 1.into_church());

    println!("{} order 尾-reduction steps for PRED 1 are:", NOR);

    println!("{}", expr);
    while expr.reduce(NOR, 1) != 0 {
        println!("{}", expr);
    }
}

stdout:

normal order 尾-reduction steps for PRED 1 are:
(位a.位b.位c.a (位d.位e.e (d b)) (位d.c) (位d.d)) (位a.位b.a b)
位a.位b.(位c.位d.c d) (位c.位d.d (c a)) (位c.b) (位c.c)
位a.位b.(位c.(位d.位e.e (d a)) c) (位c.b) (位c.c)
位a.位b.(位c.位d.d (c a)) (位c.b) (位c.c)
位a.位b.(位c.c ((位d.b) a)) (位c.c)
位a.位b.(位c.c) ((位c.b) a)
位a.位b.(位c.b) a
位a.位b.b

Comparing the number of steps for different reduction strategies

code:

use lambda_calculus::*;
use lambda_calculus::data::num::church::fac;

fn main() {
    let expr = app(fac(), 3.into_church());

    println!("comparing normalizing orders' reduction step count for FAC 3:");
    for &order in [NOR, APP, HNO, HAP].iter() {
        println!("{}: {}", order, expr.clone().reduce(order, 0));
    }
}

stdout:

comparing normalizing orders' reduction step count for FAC 3:
normal: 46
applicative: 39
hybrid normal: 46
hybrid applicative: 39

Comparing different numeral encodings

code:

use lambda_calculus::*;

fn main() {
    println!("comparing different encodings of number 3 (De Bruijn indices):");
    println!("  Church encoding: {:?}", 3.into_church());
    println!("   Scott encoding: {:?}", 3.into_scott());
    println!(" Parigot encoding: {:?}", 3.into_parigot());
    println!("Stump-Fu encoding: {:?}", 3.into_stumpfu());
    println!("  binary encoding: {:?}", 3.into_binary());
}

stdout:

comparing different encodings of number 3 (De Bruijn indices):
  Church encoding: 位位2(2(21))
   Scott encoding: 位位1(位位1(位位1(位位2)))
 Parigot encoding: 位位2(位位2(位位2(位位1)1)(2(位位1)1))(2(位位2(位位1)1)(2(位位1)1))
Stump-Fu encoding: 位位2(位位2(2(21)))(位位2(位位2(21))(位位2(位位21)(位位1)))
  binary encoding: 位位位1(13)