Skip to content

Commit c872521

Browse files
committed
awkward
1 parent 31fafcc commit c872521

File tree

4 files changed

+72
-25
lines changed

4 files changed

+72
-25
lines changed

lambda/church/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@
77
It is amazing that in Go it is allowed to declare strange
88
recursive types, which one is `Term`:
99
```go
10+
package church
11+
12+
// Term is an abstraction of a function
13+
// that can be applied any number of
14+
// times to itself. It is the base
15+
// concept in lambda calculus. There
16+
// will be aliases for this type for
17+
// better readability.
1018
type Term func(Term) Term
1119
```
1220
It means that we can implement pure lambda calculus. 'purity'
@@ -35,3 +43,4 @@ If you want to explore this funny code, I have an order for you:
3543
5. `compare.go` — they will save us
3644
6. `dirty.go` — disgusting hacks
3745
7. `numeral.go` — the main boss, division and mod
46+
8. `klop.go` — are you sure?

lambda/church/klop.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package church
2+
3+
import "github.com/nikmy/algo/tools/slices"
4+
5+
func k(a Term) Term {
6+
return func(b Term) Term { return func(c Term) Term { return func(d Term) Term { return func(e Term) Term { return func(f Term) Term { return func(g Term) Term {
7+
return func(h Term) Term { return func(i Term) Term { return func(j Term) Term { return func(k Term) Term { return func(l Term) Term { return func(m Term) Term {
8+
return func(n Term) Term { return func(o Term) Term { return func(p Term) Term { return func(q Term) Term { return func(s Term) Term { return func(t Term) Term {
9+
return func(u Term) Term { return func(v Term) Term { return func(w Term) Term { return func(x Term) Term { return func(y Term) Term { return func(z Term) Term {
10+
return func(r Term) Term {
11+
return r(compose(
12+
compose(t, h, i, s),
13+
compose(i, s),
14+
compose(a),
15+
compose(f, i, x, e, d),
16+
compose(p, o, i, n, t),
17+
compose(c, o, m, b, i, n, a, t, o, r),
18+
))
19+
}
20+
}}}}}}
21+
}}}}}}
22+
}}}}}}
23+
}}}}}}
24+
}
25+
26+
func compose(ts ...Term) Term {
27+
return func(x Term) Term {
28+
for _, t := range ts {
29+
x = t(x)
30+
}
31+
return x
32+
}
33+
}
34+
35+
func yKlop(g Term) Term {
36+
return compose(slices.Repeat[Term](k, 26)...)(g)
37+
}

lambda/church/numeral.go

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ func Mul(m Numeral) Term {
3535
}
3636

3737
/*
38-
Kleene's trick:
38+
Kleene's trick:
3939
40-
Let's define incStep(N) as Nth iteration of { Pair _ n |-> Pair n (Inc n) },
41-
so it maps (Pair `0` `0`) to (Pair (`N-1` f) (`N` f)), and the left side
42-
of N(incStep)(Pair 0 0) is "decremented" N
40+
Let's define incStep(N) as Nth iteration of { Pair _ n |-> Pair n (Inc n) },
41+
so it maps (Pair `0` `0`) to (Pair `N-1` `N`), and the left side
42+
of N(incStep)(Pair `0` `0`) is "decremented" N
4343
*/
4444
func incStep(p Term) Term {
4545
return Pair(Right(p))(Inc(Right(p)))
@@ -79,37 +79,25 @@ func Sub(m Numeral) Term {
7979
8080
So, if a solution exists, Div is a fixed point of genDiv
8181
combinator. To find a fixed point, we can use powerful
82-
tool Y-combinator:
82+
tool Y-combinator (see y_comb.go):
8383
84-
YComb == (\x.\y.y(xxy))(\x.\y.y(xxy))
84+
yComb == (\x.\y.y(xxy))(\x.\y.y(xxy))
8585
8686
For any combinator F:
8787
88-
YComb F = (\x.\y.y(xxy)) (\x.\y.y(xxy)) F
89-
= F((\x.\y.y(xxy)) (\x.\y.y(xxy)) F)
90-
= F(YComb F)
88+
yComb F = (\x.\y.y(xxy)) (\x.\y.y(xxy)) F
89+
= F((\x.\y.y(xxy)) (\x.\y.y(xxy)) F)
90+
= F(yComb F)
9191
9292
(if you don't understand the first transition, just
9393
substitute (\x.\y.y(xxy)) as x and F as y into the first
9494
closure).
9595
9696
So, now we have a formula for Div:
9797
98-
Div == YComb (\G . Less(m, n) Zero Inc(G (Sub m n) n))
98+
Div == yComb (\G . Less(m, n) Zero Inc(G (Sub m n) n))
9999
*/
100100

101-
// yCombPart == (\x.\y.y(xxy))
102-
func yCombPart(x Term) Term {
103-
return func(y Term) Term {
104-
return y(x(x)(y))
105-
}
106-
}
107-
108-
// YComb == (\x.\y.y(xxy))(\x.\y.y(xxy))
109-
func YComb(g Term) Term {
110-
return (yCombPart)(yCombPart)(g)
111-
}
112-
113101
// genDiv G == Less(m, n) Zero Inc(G (Sub m n) n)
114102
func genDiv(g Term) Term {
115103
return func(m Numeral) Term {
@@ -119,9 +107,9 @@ func genDiv(g Term) Term {
119107
}
120108
}
121109

122-
// div == YComb genDiv
110+
// div == yComb genDiv
123111
func div(m Numeral) Term {
124-
return YComb(genDiv)(m)
112+
return yComb(genDiv)(m)
125113
}
126114

127115
var _ = div // unfortunately, it does not actually work :(
@@ -133,7 +121,7 @@ var _ = div // unfortunately, it does not actually work :(
133121
// overflow callstack (because go runtime
134122
// evaluates all arguments before apply).
135123
//
136-
// Test for this function does not check YComb
124+
// Test for this function does not check yComb
137125
// trick, but it checks recursive formula.
138126
func Div(m Numeral) Term {
139127
return func(n Term) Numeral {

lambda/church/y_comb.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package church
2+
3+
// yCombPart == (\x.\y.y(xxy))
4+
func yCombPart(x Term) Term {
5+
return func(y Term) Term {
6+
return y(x(x)(y))
7+
}
8+
}
9+
10+
// yComb == (\x.\y.y(xxy))(\x.\y.y(xxy))
11+
func yComb(g Term) Term {
12+
return (yCombPart)(yCombPart)(g)
13+
}

0 commit comments

Comments
 (0)