230
社区成员




作业信息
Implement the substitution function N[v/x] : subst (N: lambda, x: string, v: value) : lambda
Think about how substitution works on arbitrary terms, i.e. N[M/x] where M could contain free variables.
Implement Church numberals and arithmetic functions using lambda calculus
作业实现
任务1&2
type rec lambda =
| Var(string)
| Fn(string, lambda)
| App(lambda, lambda)
// copy from lec2-code/src/Lambda.res
let print_lambda = l => {
let print_paren = (b, s) => {
if b {
"(" ++ s ++ ")"
} else {
s
}
}
let rec go = (l, p) => {
switch l {
| Var(x) => x
| Fn(x, a) => print_paren(p > 0, "fun " ++ x ++ " -> " ++ go(a, 0))
| App(a, b) => print_paren(p > 1, go(a, 1) ++ " " ++ go(b, 2))
}
}
go(l, 0)
}
let rec acquireFreeVar = va => {
switch va {
| Var(x) => list{x}
| App(m, n) => Belt.List.concat(acquireFreeVar(m), acquireFreeVar(n))
| Fn(x, body) => Belt.List.keep(acquireFreeVar(body), y => y != x)
}
}
let rec genNewName = (oldName, freeVar) => {
if Belt.List.has(freeVar, oldName ++ "a", String.equal) {
genNewName(oldName ++ "a", freeVar)
} else {
oldName ++ "a"
}
}
// substitution function a[v/x]
let rec subst = (x, v, a, freeVar) => {
switch a {
| Var(y) =>
if x == y {
v
} else {
a
}
| App(f, arg) => App(subst(x, v, f, freeVar), subst(x, v, arg, freeVar))
| Fn(y, body) =>
if x == y {
a
// 不相等说明要把 body 里是 x 的给替换掉,如果 freeVar 里有值等于 y,要把 y rename 了
} else if Belt.List.has(freeVar, y, String.equal) {
let newName = genNewName(y, freeVar)
let newBody = subst(y, Var(newName), body, list{newName})
Fn(newName, subst(x, v, newBody, freeVar))
} else {
Fn(y, subst(x, v, body, freeVar))
}
}
}
let rec eval = (t: lambda) => {
switch t {
// 最后消出来是个 Var 是不允许的
| Var(_) => assert false
| Fn(_, _) => t
| App(f, arg) => {
let Fn(x, body) = eval(f)
let va = eval(arg)
let freeVar = acquireFreeVar(va)
eval(subst(x, va, body, freeVar))
}
}
}
let test1 = Fn("x", Var("x"))
let test2 = App(Fn("x", Var("x")), Fn("x", Var("x")))
let test3 = App(
Fn("y", Fn("x", App(Var("x"), Var("y")))),
Fn("y", Fn("x", App(Var("x"), Var("y")))),
)
let test4 = App(
App(Fn("y", Fn("x", App(Var("x"), Var("y")))), Fn("y", Fn("x", App(Var("x"), Var("y"))))),
Fn("z", Var("z")),
)
let test5 = App(Fn("u", App(Var("u"), Fn("y", App(Var("y"), Var("z"))))), Fn("z", Var("z")))
Js.log(print_lambda(test1))
Js.log(print_lambda(eval(test1)))
Js.log(print_lambda(test2))
Js.log(print_lambda(eval(test2)))
Js.log(print_lambda(eval(Fn("z", App(Fn("x", Var("x")), Var("y"))))))
Js.log(print_lambda(test3))
Js.log(print_lambda(eval(test3)))
Js.log(print_lambda(test4))
Js.log(print_lambda(eval(test4)))
Js.log(print_lambda(test5))
Js.log(print_lambda(eval(test5)))
任务 3
// Homework: implement the arithmetic functions for peano numbers
let rec peano_add = (n: nat, m: nat): nat => {
switch n {
| Z => m
// hint: use peano_succ
| S(n') => peano_add(n', S(m))
}
}
let rec peano_mul = (n: nat, m: nat): nat => {
switch n {
| Z => Z
// hint: use peano_succ
| S(n') => peano_add(m, peano_mul(n', m))
}
}
// Homework: implement the arithmetic functions for church numbers
let church_add = (n: cnum<_>, m: cnum<_>): cnum<_> => {
(s, z) => n(s, m(s, z))
}
let church_mul = (n: cnum<_>, m: cnum<_>): cnum<_> => {
(s, z) => n((z) => m(s, z), z)
}
slides 中提到我们可以用 beta-reduction 一直对 lambda 式子进行化简,但目前的 interpreter 应该不会针对 Fn(_, body) 的场景下的 body 内部继续进行 substitution,是不是这个 eval 只是针对一次 App 的结果求值,如果要完成最终的化简要怎么做呢