├── .gitignore ├── .editorconfig ├── .travis.yml ├── .github └── dependabot.yml ├── package.json ├── README.md └── lambda.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | indent_style = space 3 | indent_size = 2 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | script: npm run test:ci 2 | language: node_js 3 | node_js: 4 | - "7" 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "19:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lambda", 3 | "version": "1.0.0", 4 | "description": "", 5 | "dependencies": {}, 6 | "devDependencies": { 7 | "chokidar-cli": "3.0.0" 8 | }, 9 | "scripts": { 10 | "test": "node --stack-size=65500 lambda", 11 | "test:ci": "npm test | (! grep '✗')", 12 | "test:watch": "chokidar 'lambda.js' -d 0 -c 'clear;npm test'" 13 | }, 14 | "author": "", 15 | "license": "ISC" 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

λ

2 | 3 | ## Fun with Lambda Calculus in JS 4 | 5 | [![Build Status](https://travis-ci.org/gtramontina/lambda.svg?branch=master)](https://travis-ci.org/gtramontina/lambda) 6 | 7 | ### Exploring: 8 | 9 | ```shell 10 | $ node lambda.js 11 | ``` 12 |

13 | 14 | ### Fiddling with it (`npm install` first): 15 | 16 | ```shell 17 | $ npm test 18 | ``` 19 | 20 | --- 21 | 22 | #### Notes: 23 | 24 | 1. These are functions _all the way_, so expect your stack to grow real quick! :smile: You can increase it if you want with node's parameter `--stack-size=`, as you can see in the test npm script. 25 | 26 | 2. I cheated a bit when using named lambda abstractions (for the sake of readability) 27 | 28 | #### References: 29 | 30 | * [Lambda Calculi, by Shane Steinert-Threlkeld](http://www.iep.utm.edu/lambda-calculi/) 31 | * [Lambda Calculus - Wikipedia](https://en.wikipedia.org/wiki/Lambda_calculus) 32 | * [Church Encoding - Wikipedia](https://en.wikipedia.org/wiki/Church_encoding) 33 | * [Programming with Nothing](https://codon.com/programming-with-nothing), by [@tomstuart](https://github.com/tomstuart) 34 | * [Fun with the Lambda Calculus](https://www.youtube.com/watch?v=QPqoFCHpLF4), by [@coreyhaines](https://github.com/coreyhaines) 35 | * [Types and Programming Languages (chapter 5, "The Untyped Lambda-Calculus") ](https://www.cis.upenn.edu/~bcpierce/tapl/), by [Benjamin C. Pierce](https://en.wikipedia.org/wiki/Benjamin_C._Pierce) 36 | 37 | --- 38 | 39 | I hope you enjoy reading it as much as I enjoyed doing it! :heart: Perhaps you could add more examples…? :smile: 40 | -------------------------------------------------------------------------------- /lambda.js: -------------------------------------------------------------------------------- 1 | // Arithmetics ----------------------------------------------------------------- 2 | 3 | IDENTITY = x => x 4 | SUCCESSOR = n => f => x => f(n(f)(x)) 5 | PREDECESSOR = n => f => x => n(g => h => h(g(f)))(_ => x)(u => u) 6 | ADDITION = m => n => n(SUCCESSOR)(m) 7 | SUBTRACTION = m => n => n(PREDECESSOR)(m) 8 | MULTIPLICATION = m => n => f => m(n(f)) 9 | POWER = x => y => y(x) 10 | ABS_DIFFERENCE = x => y => ADDITION(SUBTRACTION(x)(y))(SUBTRACTION(y)(x)) 11 | 12 | // Logic ----------------------------------------------------------------------- 13 | 14 | TRUE = t => f => t 15 | FALSE = t => f => f 16 | AND = p => q => p(q)(p) 17 | OR = p => q => p(p)(q) 18 | XOR = p => q => p(NOT(q))(q) 19 | NOT = c => c(FALSE)(TRUE) 20 | IF = c => t => f => c(t)(f) 21 | 22 | // Comparison ------------------------------------------------------------------ 23 | 24 | IS_ZERO = n => n(_ => FALSE)(TRUE) 25 | IS_LESS_THAN = m => n => NOT(IS_LESS_THAN_EQUAL(n)(m)) 26 | IS_LESS_THAN_EQUAL = m => n => IS_ZERO(SUBTRACTION(m)(n)) 27 | IS_EQUAL = m => n => AND(IS_LESS_THAN_EQUAL(m)(n))(IS_LESS_THAN_EQUAL(n)(m)) 28 | IS_NOT_EQUAL = m => n => OR(NOT(IS_LESS_THAN_EQUAL(m)(n)))(NOT(IS_LESS_THAN_EQUAL(n)(m))) 29 | IS_GREATER_THAN_EQUAL = m => n => IS_LESS_THAN_EQUAL(n)(m) 30 | IS_GREATER_THAN = m => n => NOT(IS_LESS_THAN_EQUAL(m)(n)) 31 | IS_NULL = p => p(x => y => FALSE) 32 | NIL = x => TRUE 33 | 34 | // Combinators ----------------------------------------------------------------- 35 | 36 | Y = f => (x => f(y => (x(x))(y)))(x => f(y => (x(x))(y))) 37 | 38 | // Lists ----------------------------------------------------------------------- 39 | 40 | CONS = x => y => f => f(x)(y) 41 | CAR = p => p(TRUE) 42 | CDR = p => p(FALSE) 43 | 44 | RANGE = m => n => Y(f => m => IF(IS_EQUAL(m)(n)) 45 | (_ => CONS(m)(NIL)) 46 | (_ => CONS(m)(f(SUCCESSOR(m)))) 47 | ())(m) 48 | 49 | MAP = x => g => Y(f => x => IF(IS_NULL(x)) 50 | (_ => x) 51 | (_ => CONS(g(CAR(x)))(f(CDR(x)))) 52 | ())(x) 53 | 54 | // Test "Framework" ------------------------------------------------------------ 55 | 56 | ASSERT = truth => IF(truth) 57 | (description => `[\x1b[32m✓\x1b[0m] ${description}`) 58 | (description => `[\x1b[31m✗\x1b[0m] ${description}`) 59 | 60 | REFUTE = truth => ASSERT(NOT(truth)) 61 | 62 | TEST = description => assertion => console.log(assertion(description)) 63 | 64 | // Church Numerals ------------------------------------------------------------- 65 | 66 | $zero = f => IDENTITY 67 | $one = SUCCESSOR($zero) 68 | $two = SUCCESSOR($one) 69 | $three = SUCCESSOR($two) 70 | $four = MULTIPLICATION($two)($two) 71 | $five = SUCCESSOR($four) 72 | $eight = MULTIPLICATION($two)($four) 73 | $nine = SUCCESSOR($eight) 74 | $ten = MULTIPLICATION($two)($five) 75 | 76 | // Tests ----------------------------------------------------------------------- 77 | 78 | TEST('TRUE') 79 | (ASSERT(TRUE)) 80 | 81 | TEST('FALSE') 82 | (REFUTE(FALSE)) 83 | 84 | TEST('AND') 85 | (ASSERT(AND(TRUE)(TRUE))) 86 | 87 | TEST('OR')(ASSERT(AND 88 | (AND(OR(TRUE)(FALSE))(OR(FALSE)(TRUE))) 89 | (NOT(OR(FALSE)(FALSE))))) 90 | 91 | TEST('XOR')(ASSERT(AND 92 | (AND(XOR(TRUE)(FALSE))(XOR(FALSE)(TRUE))) 93 | (NOT(XOR(TRUE)(TRUE))))) 94 | 95 | TEST('NOT') 96 | (REFUTE(NOT(TRUE))) 97 | 98 | TEST('IF')(ASSERT(AND 99 | (IF(TRUE)(TRUE)(FALSE)) 100 | (NOT(IF(FALSE)(TRUE)(FALSE))))) 101 | 102 | TEST('IDENTITY') 103 | (ASSERT(IS_EQUAL(IDENTITY)(x => x))) 104 | 105 | TEST('SUCCESSOR') 106 | (ASSERT(IS_EQUAL(SUCCESSOR($zero))($one))) 107 | 108 | TEST('PREDECESSOR') 109 | (ASSERT(IS_EQUAL($zero)(PREDECESSOR($one)))) 110 | 111 | TEST('ADDITION') 112 | (ASSERT(IS_EQUAL(SUCCESSOR($one))(ADDITION($one)($one)))) 113 | 114 | TEST('SUBTRACTION') 115 | (ASSERT(IS_EQUAL($zero)(SUBTRACTION($one)($one)))) 116 | 117 | TEST('MULTIPLICATION') 118 | (ASSERT(IS_EQUAL($four)(MULTIPLICATION($two)($two)))) 119 | 120 | TEST('POWER')(ASSERT(AND 121 | (IS_EQUAL($nine)(POWER($three)($two))) 122 | (IS_EQUAL($eight)(POWER($two)($three))))) 123 | 124 | TEST('ABS_DIFFERENCE')(ASSERT(AND 125 | (IS_EQUAL($one)(ABS_DIFFERENCE($three)($two))) 126 | (IS_EQUAL($one)(ABS_DIFFERENCE($two)($three))))) 127 | 128 | TEST('IS_ZERO') 129 | (ASSERT(IS_ZERO($zero))) 130 | 131 | TEST('IS_LESS_THAN') 132 | (ASSERT(IS_LESS_THAN($zero)($one))) 133 | 134 | TEST('IS_LESS_THAN_EQUAL')(ASSERT(AND 135 | (IS_LESS_THAN_EQUAL($one)($one)) 136 | (IS_LESS_THAN_EQUAL($zero)($one)))) 137 | 138 | TEST('IS_EQUAL')(ASSERT(AND 139 | (IS_EQUAL($zero)($zero)) 140 | (IS_EQUAL($one)($one)))) 141 | 142 | TEST('IS_NOT_EQUAL') 143 | (ASSERT(IS_NOT_EQUAL($zero)($one))) 144 | 145 | TEST('IS_GREATER_THAN_EQUAL')(ASSERT(AND 146 | (IS_GREATER_THAN_EQUAL($one)($one)) 147 | (IS_GREATER_THAN_EQUAL($one)($zero)))) 148 | 149 | TEST('IS_GREATER_THAN') 150 | (ASSERT(IS_GREATER_THAN($one)($zero))) 151 | 152 | TEST('IS_NULL') 153 | (ASSERT(IS_NULL(NIL))) 154 | 155 | TEST('CAR')(ASSERT(AND 156 | (IS_EQUAL(CAR(CONS($five)($one)))($five)) 157 | (IS_EQUAL(CAR(CONS($two)(CONS($one)($three))))($two)))) 158 | 159 | TEST('CDR')(ASSERT(AND 160 | (IS_EQUAL(CDR(CONS($five)($one)))($one)) 161 | (IS_EQUAL(CAR(CDR(CONS($two)(CONS($one)($three)))))($one)))) 162 | 163 | TEST('CONS')(ASSERT(AND 164 | (IS_EQUAL(CDR(CDR(CONS($two)(CONS($one)($three)))))($three)) 165 | (IS_EQUAL(CAR(CDR(CONS($five)(CONS($two)(CONS($one)($three))))))($two)))) 166 | 167 | TEST('RANGE')(ASSERT(AND( 168 | AND 169 | (IS_EQUAL(CAR(RANGE($three)($five)))($three)) 170 | (IS_EQUAL(CAR(CDR(RANGE($three)($five))))($four)))( 171 | AND 172 | (IS_EQUAL(CAR(CDR(CDR(RANGE($three)($five)))))($five)) 173 | (IS_NULL(CDR(CDR(CDR(RANGE($three)($five))))))))) 174 | 175 | TEST('MAP')(ASSERT(AND( 176 | AND 177 | (IS_EQUAL 178 | (CAR(MAP(RANGE($three)($five))(v => POWER(v)($two)))) 179 | (POWER($three)($two))) 180 | (IS_EQUAL 181 | (CAR(CDR(MAP(RANGE($three)($five))(v => POWER(v)($two))))) 182 | (POWER($four)($two))))( 183 | AND 184 | (IS_EQUAL 185 | (CAR(CDR(CDR(MAP(RANGE($three)($five))(v => POWER(v)($two)))))) 186 | (POWER($five)($two))) 187 | (IS_NULL(CDR(CDR(CDR(MAP(RANGE($three)($five))(v => POWER(v)($two)))))))))) 188 | 189 | // Examples -------------------------------------------------------------------- 190 | 191 | console.log('\n--- Examples ---\n') 192 | 193 | FACTORIAL = Y(f => n => IF(IS_ZERO(n)) 194 | (_ => SUCCESSOR(n)) 195 | (_ => MULTIPLICATION(n)(f(PREDECESSOR(n)))) 196 | ()) 197 | 198 | FIBONACCI = Y(f => n => IF(IS_LESS_THAN_EQUAL(n)(SUCCESSOR(f => IDENTITY))) 199 | (_ => n) 200 | (_ => ADDITION 201 | (f(PREDECESSOR(n))) 202 | (f(PREDECESSOR(PREDECESSOR(n))))) 203 | ()) 204 | 205 | TEST('FACTORIAL: 5! = 120')(ASSERT(IS_EQUAL 206 | (FACTORIAL($five)) 207 | (ADDITION(MULTIPLICATION($ten)($ten))(ADDITION($ten)($ten))))) 208 | 209 | TEST('FIBONACCI: 10 = 55')(ASSERT(IS_EQUAL 210 | (FIBONACCI($ten)) 211 | (ADDITION(MULTIPLICATION($five)($ten))($five)))) 212 | --------------------------------------------------------------------------------