├── .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 | [](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 |
--------------------------------------------------------------------------------