lambda-calculus-with-js
Version:
Using JS' anonymous functions to perform lambda calculus
111 lines (76 loc) • 3.57 kB
Markdown
# 用 JS 来搞 Lambda 演算
[](https://github.com/E0SelmY4V/kfc-lambda/actions/workflows/test-all.yml)
JS 可以模拟 Lambda 演算,比如这样:
```js
const bTrue = x => _ => x;
const bFalse = _ => y => y;
const not = a => a(bFalse)(bTrue);
const a = not(bTrue); // 怎么能验证 a 是 _ => y => y ?
const n0 = f => x => x;
const n1 = f => x => f(x);
const n3 = f => x => f(f(f(x)));
const plus = a => b => f => x => a(f)(b(f)(x));
const multi = a => b => a(plus(b))(n0)
const pred = n => f => x => n(p => h => h(p(f)))(_ => x)(n => n0);
const is0 = n => n(_ => bFalse)(bTrue);
const temp = multi(n3) // 怎么能把这个结果转换为 Lambda 表达式?
const yCombinaton = p => (s => p(s(s)))(s => p(s(s)));
const factorial = yCombinaton(
s => n => isZero(n)(
n1
)(
multi(n)(s(pred(n)))
)
);
const n6 = factorial(n3); // JS 是急切求值,怎么防止这段代码爆栈?
```
你可以看出来,这种模拟虽然能用 JS 来对 Lambda 表达式使用 beta 规约,但是会出现各种各样难以使用的问题。
如果你想要解决这些个问题,就请使用《用 JS 来搞 Lambda 演算》库吧!
## 方便定义 Lambda 函数
如果你使用的是 TypeScript ,那你随手一写的 Lambda 函数 `n => n` 会有警告:因为 `n` 是隐式的 `any` 类型,对吗?
但是,本库提供了一个类型 `Lambda` 和一个函数 `gl` 。
只要你声明了一个 Lambda 函数是 `Lambda` ,那不论它多复杂,都不会报错了!
```ts
import { Lambda, gl } from 'lambda-calculus-with-js';
const plus: Lambda = a => b => a(b);
const pred = gl(x => _ => x);
```
这两种方法都可以。
## 直观输出 Lambda 函数
不论你的 Lambda 函数写得有多么精妙,直接通过 `console.log` 输出,总是会得到 `[Function (anonymous)]` 这个恼人的字符串……想要知道它的内部结构需要多么费心费力啊!
但是如果你用了这个库,它提供了一个 `log` 函数和其他设施,你不仅可以看懂一个 Lmabda 表达式的内部,还可以用各种不同的格式看懂!
```ts
import { log, Log, Lambda } from 'lambda-calculus-with-js';
const plus: Lambda = a => b => f => x => a(f)(b(f)(x));
const n2: Lambda = f => x => f(f(x));
const a = plus(n2);
log(a, Log.Js);
// p1 => p2 => p3 => p2(p2(p1(p2)(p3)))
log(a, Log.Std);
// λp1.λp2.λp3.(p2 (p2 ((p1 p2) p3)))
```
## 惰性求值的 Y 组合子
JS 本质上是一个急切求值的语言,所以普通的 Y 组合子不能用,我们只能用 Z 组合子……?
没关系!
只要你使用本库,就可以获得一个叫 `yC` 的惰性求值 Y 组合子!
唯一的注意事项是不要有死循环,也就是说递归必须能在某个时候退出。
否则的话求值的过程是无限长的,就算是惰性求值也没法用 `log` 得知其内部了。
> 顺便一提 `yC` 自己也算死循环,不要直接 `log(yC)` 。
这是如何实现阶乘:
```ts
import { log, yC, Lambda } from 'lambda-calculus-with-js'
const plus: Lambda = a => b => f => x => a(f)(b(f)(x));
const multi: Lambda = a => b => a(plus(b))(n0);
const isZero: Lambda = n => n(_ => _ => y => y)(x => _ => x);
const factorial = yC(
s => n => isZero(n)(
n1
)(
multi(n)(s(pred(n)))
)
);
const n4: Lambda = f => x => f(f(f(f(x))));
const n24 = factorial(n4);
log(n24);
// p1 => p2 => p1(p1(p1(p1(p1(p1(p1(p1(p1(p1(p1(p1(p1(p1(p1(p1(p1(p1(p1(p1(p1(p1(p1(p1(p2))))))))))))))))))))))))
```