-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlambda-calc.rkt
executable file
·351 lines (288 loc) · 10.6 KB
/
lambda-calc.rkt
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
#lang lazy
(require rackunit)
; This is so we can actually write tests that we can see right now
(define (check-expect actual expected)
(check-equal? (! actual) (! expected)))
; factorial : Nat -> Nat
(define (factorial n)
(if (zero? n)
1
(* n (factorial (sub1 n)))))
(check-expect (factorial 0) 1)
(check-expect (factorial 5) 120)
; This is fine and all, but this uses a lot of things that are not lambda!
; How can we limit our toolbelt to see lambda's power?
; Well, what things are not lambda?
; - define function
; - if
; - zero? (predicates/booleans)
; - 1 (Natural numbers)
; - *, sub1 (arithmetic)
; So define should be easy, right?
#;(define factorial
(λ (n)
(if (zero? n)
1
(* n (factorial (sub1 n))))))
; oh no! what does factorial mean now in the recursive call? It's not defined.
; so define is ok as long as we don't have recursion.
; This means we need to do recursion with lambda.
; UPDATED:
; Well, what things are not lambda?
; - define function
; - if
; - zero? (predicates/booleans)
; - 1 (natural numbers)
; - *, sub1 (arithmetic)
; - recursion
; λNat -------------------------------------------
; What do natural numbers represent? One interpretation could
; be "the amount of a thing", or "the times a certain
; action occurs". We're going to represent numbers kind
; of like the latter.
; A λNat is a [[X -> X] -> [X -> X]]
; and represents the times a function is applied onto an input
; the "template" for using an λnat is like
#;(define (λnat-temp λnat)
((λnat ...) ...))
; a sort of "template" for creating a λnat would be like
#;(define λnat
(λ (f)
(λ (x) ...)))
; λone : λNat
; applies a function to an input once
(define λone
(λ (f)
(λ (x) (f x))))
; λtwo : λNat
; applies a function twice to an input
(define λtwo
(λ (f)
(λ (x) (f (f x)))))
; what would zero be?
; λzero : λNat
; applies a function no times at all to an input
(define λzero
(λ (f)
(λ (x) x)))
; These functions encapsulate the ~essence~ of natural numbers!!
; But we don't just trust ourselves in computer science, we write checks!
; Let's write some translation functions back into the world of numerals
; so we can interpret the lambda expressions that we are writing
; λnat->nat : λNat -> Nat
(define (λnat->nat λnum)
((λnum add1) 0))
(check-expect (λnat->nat λzero) 0)
(check-expect (λnat->nat λone) 1)
(check-expect (λnat->nat λtwo) 2)
; It's getting kind of tedious to write out all these numbers
; λadd1 : λNat -> λNat
; produces an λNat that applies a function one more time than the supplied λnat
(define λadd1
(λ (n)
(λ (f)
(λ (x) (f ((n f) x))))))
(check-expect (λnat->nat (λadd1 λtwo)) 3)
(check-expect (λnat->nat (λadd1 λzero)) 1)
; Now we can define
(define λthree (λadd1 λtwo))
; λadd : λNat λNat -> λNat
; adds the two λnats together
(define λ+
(λ (n1 n2)
(λ (f)
(λ (x) ((n2 f) ((n1 f) x))))))
(check-expect (λnat->nat (λ+ λzero λzero)) 0)
(check-expect (λnat->nat (λ+ λthree λtwo)) 5)
; Now we're cooking. We really want to get * and sub1 though
; λ* : λNat λNat -> λNat
; multiplies the two λnats together
(define λ*
(λ (n1 n2)
(λ (f)
(λ (x) ((n1 (n2 f)) x)))))
(check-expect (λnat->nat (λ* λzero λthree)) 0)
(check-expect (λnat->nat (λ* λthree λtwo)) 6)
; OK but how in the world do we do sub1?
; Well... this is a little harder, since we don't really have a concept of negation.
; We'll come back to this once we've built up a little more tooling in our language
; Let's do booleans! Remember, our goal is to be able to ask the question zero?
; What are booleans? Like numbers, we'll ask how they're used. With either true
; or false, we use booleans to make decisions! The basic part of a language that
; deals with booleans are `if` expressions. Let's look at how an if works
(check-expect (if true 1 2) 1) ; true here is associated with "choose the first option"
(check-expect (if false 1 2) 2) ; false here is associated with "choose the second option"
; so let's define booleans as "choosers".
; A λBoolean is a (A) [A A -> A]
; where it picks one of the options
; a way to sanity check ourselves would be
(define (λboolean->boolean b) (b #true #false))
; λtrue : λBoolean
; pick the first option
(define λtrue (λ (x y) x))
(check-expect (λtrue 1 2) 1)
; λfalse : λBoolean
; pick the second option
(define λfalse (λ (x y) y))
(check-expect (λfalse 1 2) 2)
; λif : (X) λBoolean X X -> X
(define λif
(λ (bool true-case false-case)
(bool true-case false-case)))
(check-expect (λif λtrue 1 2) 1)
(check-expect (λif λfalse 1 2) 2)
; λzero? : λNat -> λBoolean
; is the given λNat zero?
(define λzero?
(λ (n) ((n (λ (_) λfalse)) λtrue)))
(check-expect (λboolean->boolean (λzero? λzero)) #true)
(check-expect (λboolean->boolean (λzero? λone)) #false)
(check-expect (λboolean->boolean (λzero? λtwo)) #false)
; we're almost there to implementing sub1!
; one last thing we need is pairs (and as a result we get lists!!)
; what is a pair? What do you do with it... call first or second on it.
; When given some selector, give the first or second thing
; A λPair is a [X X -> [[X X -> X] -> X]]
(define λmake-pair
(λ (x y) (λ (chooser) (chooser x y))))
(define λfirst (λ (pair) (pair λtrue)))
(define λsecond (λ (pair) (pair λfalse)))
(check-expect (λfirst (λmake-pair 1 2)) 1)
(check-expect (λsecond (λmake-pair 1 2)) 2)
(check-expect (λfirst (λsecond (λmake-pair 1 (λmake-pair 2 3)))) 2)
; λinc-cons : λPair -> λPair
; copy the first into the second and increment the first
(define λinc-cons
(λ (pair)
(λmake-pair (λadd1 (λfirst pair)) (λfirst pair))))
; sub1 : λnat -> λnat
; produce an λnat that performs given function one less time than the original
(define λsub1
(λ (n)
(λsecond ((n λinc-cons) (λmake-pair λzero λzero)))))
(check-expect (λnat->nat (λsub1 λzero)) 0)
(check-expect (λnat->nat (λsub1 λone)) 0)
(check-expect (λnat->nat (λsub1 λthree)) 2)
; We've nearly got it!!
; Let's check in on how we could rewrite factorial
; λfactorial : λNat -> λNat
(define (λfactorial-with-named-recursion n)
(λif (λzero? n)
λone
(λ* n (λfactorial-with-named-recursion (λsub1 n)))))
(check-expect (λnat->nat (λfactorial-with-named-recursion λzero)) 1)
(check-expect (λnat->nat (λfactorial-with-named-recursion λthree)) 6)
; We were able to replace everything except
; for the function define construct that allow
; But earlier we figured out that we can't just do
#;(define λfactorial
(λ (n)
(λif (λzero? n)
λone
(λ* n (λfactorial (λsub1 n))))))
; because we're trying to use λfactorial inside
; its own definition and we can't do that with normal
; defines!!
; So do we give up? Never! Let's figure out how to implement
; recursion -- without names.
; Oof ok so it's about to get weird.
; What happens when we do recursion? You can imagine copying and pasting
; the function body of the recursive call into our original function.
; And we keep copying and pasting the recursive calls until we get to our base case.
; So we're kind of passing on a blob of code that is the function body.
; We just can't refer to the same name.
; So what if we called a different function instead? In lambdas, we access to variables
; as inputs to the lambda. What if we just took the function we call to do recursion
; as an input?
#;(define λfactorial
(... (λ (recur)
(λ (n)
(λif (λzero? n)
λone
(λ* n (recur (λsub1 n))))))))
; So this is technically allowed, but how the hell does this work? What needs to
; happen? Well, recur should figure out somehow to take the n-1 and pass it back
; into the λ (n), and we need to make sure that λfactorial still matches its
; original signature.
#;((λ (f) (f f)) (λ (f) (f f)))
; We just created an infinite loop! Without "recursion"!
; This was a big step, but we don't want infinite recursion, we want
; to only recur a certain number of times.
(define Y
(λ (g)
((λ (f) (f f))
(λ (f) (g (f f))))))
#;(define λfactorial
(Y
(λ (recur)
(λ (n)
(λif (λzero? n)
λone
(λ* n (recur (λsub1 n))))))))
(define λfactorial
((λ (g)
((λ (f) (f f))
(λ (f) (g (f f)))))
(λ (fct)
(λ (n)
((λ (bool t-case f-case)
(bool t-case f-case))
((λ (rep) ((rep (λ (x) (λ (x y) y))) (λ (x y) x))) n)
(λ (f) (λ (x) (f x)))
((λ (rep1 rep2)
(λ (f)
(λ (x) ((rep1 (rep2 f)) x))))
n
(fct ((λ (rep)
(λ (f)
(λ (x)
((((λ (pair) (pair (λ (x y) y)))
((rep (λ (p)
((λ (a b)
(λ (selector) (selector a b)))
((λ (rep)
(λ (f)
(λ (x) (f ((rep f) x)))))
((λ (pair) (pair (λ (x y) x))) p))
((λ (pair) (pair (λ (x y) x))) p))))
((λ (a b)
(λ (selector) (selector a b)))
(λ (f) (λ (x) x))
(λ (f) (λ (x) x)))))
f)
x))))
n))))))))
(λnat->nat
(((λ (g)
((λ (f) (f f))
(λ (f) (g (f f)))))
(λ (fct)
(λ (n)
((λ (bool t-case f-case)
(bool t-case f-case))
((λ (rep) ((rep (λ (x) (λ (x y) y))) (λ (x y) x))) n)
(λ (f) (λ (x) (f x)))
((λ (rep1 rep2)
(λ (f)
(λ (x) ((rep1 (rep2 f)) x))))
n
(fct ((λ (rep)
(λ (f)
(λ (x)
((((λ (pair) (pair (λ (x y) y)))
((rep (λ (p)
((λ (a b)
(λ (selector) (selector a b)))
((λ (rep)
(λ (f)
(λ (x) (f ((rep f) x)))))
((λ (pair) (pair (λ (x y) x))) p))
((λ (pair) (pair (λ (x y) x))) p))))
((λ (a b)
(λ (selector) (selector a b)))
(λ (f) (λ (x) x))
(λ (f) (λ (x) x)))))
f)
x))))
n)))))))
(λ (f) (λ (x) (f (f (f (f (f x)))))))))