Lambda Calculus Implementation in JavaScript

Table of Contents

Introduction

This org-mode file contains a Lambda Calculus implementation in JavaScript. Each section includes self-contained code blocks that can be run independently in a JavaScript environment.

Lambda Expressions

// Step 1: Understand Lambda Expressions
const identity = x => x;

console.log(identity(5));
// Output: 5

Church Booleans

// Step 2: Implement Church Booleans
const lcTrue = x => y => x;
const lcFalse = x => y => y;
const lcIf = condition => then => else_ => condition(then)(else_);

console.log(lcIf(lcTrue)("yes")("no"));
// Output: yes

Basic Combinators

// Step 3: Implement Basic Combinators
const I = x => x;
const K = x => y => x;
const KI = x => y => y;

console.log(I(5));
console.log(K(3)(4));
console.log(KI(3)(4));
// Output:
// 5
// 3
// 4

Church Numerals

// Step 4: Create Church Numerals
const lcZero = f => x => x;
const lcSucc = n => f => x => f(n(f)(x));

const churchToInt = n => n(x => x + 1)(0);

const lcOne = lcSucc(lcZero);
const lcTwo = lcSucc(lcOne);

console.log(churchToInt(lcZero));
console.log(churchToInt(lcOne));
console.log(churchToInt(lcTwo));
// Output:
// 0
// 1
// 2

Basic Arithmetic

// Step 5: Implement Basic Arithmetic
const lcZero = f => x => x;
const lcSucc = n => f => x => f(n(f)(x));

const churchToInt = n => n(x => x + 1)(0);

const lcAdd = m => n => f => x => m(f)(n(f)(x));
const lcMult = m => n => f => m(n(f));

const lcOne = lcSucc(lcZero);
const lcTwo = lcSucc(lcOne);
const lcThree = lcSucc(lcTwo);

console.log(churchToInt(lcAdd(lcOne)(lcTwo)));
console.log(churchToInt(lcMult(lcTwo)(lcThree)));
// Output:
// 3
// 6

Pairs

// Step 6: Implement Pairs
const lcPair = x => y => f => f(x)(y);
const lcFirst = p => p(x => y => x);
const lcSecond = p => p(x => y => y);

const myPair = lcPair(3)(4);
console.log(lcFirst(myPair));
console.log(lcSecond(myPair));
// Output:
// 3
// 4

Lists

// Step 7: Build Lists Using Pairs
const lcTrue = x => y => x;
const lcFalse = x => y => y;
const lcPair = x => y => f => f(x)(y);
const lcFirst = p => p(x => y => x);
const lcSecond = p => p(x => y => y);

const lcNil = x => lcTrue;
const lcCons = lcPair;
const lcIsNil = l => l(h => t => lcFalse);
const lcHead = lcFirst;
const lcTail = lcSecond;

// Helper function to convert JavaScript array to Church-encoded list
const listToChurch = arr => {
  if (arr.length === 0) return lcNil;
  return lcCons(arr[0])(listToChurch(arr.slice(1)));
};

// Helper function to convert Church-encoded list to JavaScript array
const churchToList = l => {
  const result = [];
  let current = l;
  while (lcIsNil(current)(false)(true)) {
    result.push(lcHead(current));
    current = lcTail(current);
  }
  return result;
};

const myList = listToChurch([1, 2, 3]);
console.log(churchToList(myList));
console.log(lcHead(myList));
console.log(churchToList(lcTail(myList)));
console.log(lcIsNil(myList)(true)(false));
console.log(lcIsNil(lcNil)(true)(false));
// Output:
// [1, 2, 3]
// 1
// [2, 3]
// false
// true

Comparison Operations

// Step 8: Implement Comparison Operations
const lcZero = f => x => x;
const lcSucc = n => f => x => f(n(f)(x));
const lcTrue = x => y => x;
const lcFalse = x => y => y;

const lcIsZero = n => n(x => lcFalse)(lcTrue);
const lcLeq = m => n => lcIsZero(lcSub(m)(n));
const lcEq = m => n => lcAnd(lcLeq(m)(n))(lcLeq(n)(m));

const lcAnd = x => y => x(y)(lcFalse);
const lcSub = m => n => n(n => f => x => n(g => h => h(g(f)))(y => x)(y => y))(m);

const lcOne = lcSucc(lcZero);
const lcTwo = lcSucc(lcOne);

console.log(lcIsZero(lcZero)(true)(false));
console.log(lcIsZero(lcOne)(true)(false));
console.log(lcLeq(lcOne)(lcTwo)(true)(false));
console.log(lcEq(lcOne)(lcOne)(true)(false));
console.log(lcEq(lcOne)(lcTwo)(true)(false));
// Output:
// true
// false
// true
// true
// false

Higher-Order List Operations

// Step 9: Implement Higher-Order List Operations
const lcZero = f => x => x;
const lcSucc = n => f => x => f(n(f)(x));
const lcTrue = x => y => x;
const lcFalse = x => y => y;
const lcPair = x => y => f => f(x)(y);
const lcFirst = p => p(x => y => x);
const lcSecond = p => p(x => y => y);
const lcNil = x => lcTrue;
const lcCons = lcPair;
const lcIsNil = l => l(h => t => lcFalse);
const lcHead = lcFirst;
const lcTail = lcSecond;

const lcIf = condition => then => else_ => condition(then)(else_);

const lcMap = f => l => lcIf(lcIsNil(l))
  (lcNil)
  (() => lcCons(f(lcHead(l)))(lcMap(f)(lcTail(l))))();

const lcFilter = p => l => lcIf(lcIsNil(l))
  (lcNil)
  (() => lcIf(p(lcHead(l)))
    (() => lcCons(lcHead(l))(lcFilter(p)(lcTail(l))))
    (() => lcFilter(p)(lcTail(l))))();

const lcFold = f => acc => l => lcIf(lcIsNil(l))
  (acc)
  (() => lcFold(f)(f(acc)(lcHead(l)))(lcTail(l)))();

const listToChurch = arr => {
  if (arr.length === 0) return lcNil;
  return lcCons(arr[0])(listToChurch(arr.slice(1)));
};

const churchToList = l => {
  const result = [];
  let current = l;
  while (lcIsNil(current)(false)(true)) {
    result.push(lcHead(current));
    current = lcTail(current);
  }
  return result;
};

const myList = listToChurch([1, 2, 3]);

const doubledList = lcMap(x => x * 2)(myList);
console.log("Doubled list:", churchToList(doubledList));

const filteredList = lcFilter(x => x > 1)(myList);
console.log("Filtered list:", churchToList(filteredList));

const sumList = lcFold(x => y => x + y)(0)(myList);
console.log("Sum of list:", sumList);
// Output:
// Doubled list: [2, 4, 6]
// Filtered list: [2, 3]
// Sum of list: 6

Recursion and Fixed-Point Combinators

// Step 10: Explore Recursion and Fixed-Point Combinators
const lcZero = f => x => x;
const lcSucc = n => f => x => f(n(f)(x));
const lcTrue = x => y => x;
const lcFalse = x => y => y;
const lcIf = condition => then => else_ => condition(then)(else_);

const lcIsZero = n => n(x => lcFalse)(lcTrue);
const lcSub = m => n => n(n => f => x => n(g => h => h(g(f)))(y => x)(y => y))(m);
const lcMult = m => n => f => m(n(f));

const churchToInt = n => n(x => x + 1)(0);

const lcOne = lcSucc(lcZero);

const Y = f => (x => f(y => x(x)(y)))(x => f(y => x(x)(y)));

const factorial = Y(f => n => lcIf(lcIsZero(n))
  (lcOne)
  (() => lcMult(n)(f(lcSub(n)(lcOne))))());

const lcFive = lcSucc(lcSucc(lcSucc(lcSucc(lcOne))));
console.log("Factorial of 5:", churchToInt(factorial(lcFive)));
// Output:
// Factorial of 5: 120

Conclusion

This org-mode file provides a comprehensive implementation of Lambda Calculus in JavaScript, including Church encodings for booleans, numerals, and lists, as well as higher-order functions and recursion using the Y combinator. Each section can be run independently in a JavaScript environment (such as a browser console) to explore different aspects of Lambda Calculus.

Author: Claude 3.5 Sonnet (Anthropic, PBC)

jwalsh@nexus

Last Updated: 2025-07-30 13:45:27

build: 2025-12-23 09:12 | sha: e32f33e