230
社区成员




Complete the de Bruijn index based interpreter in natural semantics
Apply the de Bruijn index for extended lambda calculus (+ Let)
实现德布朗指数在自然语义下的解释器。
可能是翻译问题,德布朗指数(de Bruijn index)的index理解成索引可能更自然一些。参考
实现解释器,需要对Lambda表达式进行分类讨论:
对于Var(i)
,仍然解释为Var(i)
;
对于Fn(b)
,递归地解释b
;
对于App(f, arg)
,首先解释f
,如果得到一个函数,那么再解释arg
,并将f
应用到arg
上。在应用β规约的时候,应当注意f
和arg
中自由变量的存在情况:
假设f
具有λ.M
的形式,arg
具有N
的形式:
M
和N
中都不存在自由变量:直接使用N
代换M
中的index 0。即M[N/0]
;M
中存在自由变量y
,N
中不存在自由变量:注意在对f
应用β规约之后,对于y
而言,其外部的λ层数减少了1
,所以需要对代换后的结果中自由变量的索引 shift -1。即shift(-1,M[N/0])
N
中存在自由变量:在对f
应用β规约时,需要强调操作的顺序。需要先将N
放到M
中进行代换,再将λ层数减少1。如果调换顺序,那么在将λ层数减少1后,M
中的自由变量索引 shift -1,可能和M
中最外层的受控变量索引混淆,导致无法将N
正确的代换到shift(-1,M)
中。在保证上述顺序的情况下,N
在M
中进行代换时,其自由变量外部的λ层数增加了1,在代换结束后,代换结果中全部自由变量外部的λ层数都减少了1。所以有shift(-1,M[shift(1,N)/0])
。对于上述几种情况,综合来看,由于shift操作对受控变量无影响,上述几种情况可以总结为shift(-1,M[shift(1,N)/0])
。
应用β规约后,递归地解释规约的结果。
对于App(f, arg)
,如果解释f
得到的不是函数,那么保留解释f
的结果,递归地解释arg
。
代码如下:
module Debru = {
type rec lambda =
| Var(int)
| Fn(lambda)
| App(lambda, lambda)
let rec toString = (t: lambda): string => switch t {
| Var(i) => Js.Int.toString(i)
| Fn(b) => "(λ." ++ toString(b) ++")"
| App(m, n) => "(" ++ toString(m) ++" " ++ toString(n) ++ ")"
}
let print_lambda = l => {
let print_paren = (b, s) => {
if b { "(" ++ s ++ ")" } else { s }
}
let rec go = (l, p) => {
switch l {
| Var(x) => Js.Int.toString(x)
| Fn(a) => print_paren(p>0, "fun -> " ++ go(a, 0))
| App(a, b) => print_paren(p>1, go(a, 1) ++ " " ++ go(b, 2))
}
}
go(l, 0)
}
// Var(j) becomes Var(i+j) if j >= d
let rec shift_aux = (i, d, u) => {
switch u {
| Var(j) => { if j >= d { Var(i+j) } else { u } }
| Fn(b) => Fn(shift_aux(i, d+1, b))
| App(a, b) =>
App(shift_aux(i, d, a), shift_aux(i, d, b))
}
}
let shift = (i, u) => shift_aux(i, 0, u)
// t[u/i]: use u to replace Var(i) in term t
let rec subst = (t, i, u) => {
switch t {
| Var(j) => if j == i { u } else { t }
| Fn(b) => Fn(subst(b, i+1, shift(1, u)))
| App(a, b) => App(subst(a, i, u), subst(b, i, u))
}
}
// Homework: implement the complete interpreter
let eval = (t: lambda) => {
let rec evalHelper = (t:lambda) => {
switch t {
| Var(_) => t
| Fn(b) => Fn(evalHelper(b))
| App(f, arg) => {
switch evalHelper(f) {
| Fn(b) => {
let va = evalHelper(arg)
let va_shifted = shift(1,va)
let b_substed = subst(b, 0, va_shifted)
let b_substed_deshifted = shift(-1, b_substed)
evalHelper(b_substed_deshifted)
}
| k => App(k, evalHelper(arg))
}
}
}
}
evalHelper(t)
}
}
下面是测试代码:
module DebruTest = {
open! Debru
let id = Fn(Var(0))
let trueB = Fn(Fn(Var(1)))
let falseB = Fn(Fn(Var(0)))
let zero = Fn(Fn(Var(0)))
let one = Fn(Fn(App(Var(1),Var(0))))
let succ = Fn(Fn(Fn(App(Var(1),App(App(Var(2),Var(1)),Var(0))))))
let pair = Fn(Fn(Fn(App(App(Var(0),Var(2)),Var(1)))))
let fst = Fn(App(Var(0),trueB))
let snd = Fn(App(Var(0),falseB))
let pred =
Fn(App(
fst,App(
App(
Var(0),
Fn(App(
App(pair,App(snd,Var(0))),
App(succ,App(snd,Var(0)))))),
App(App(pair,zero),zero))))
let toChurchNumB = (n : int) => {
let rec helper = (n: int, churchNumB: lambda) : lambda =>
if n < 0 {assert false}
else {
switch n {
| 0 => churchNumB
| _ => helper(n-1, App(succ, churchNumB))
}
}
helper(n, zero)
}
let test = () => {
Js.log(print_lambda(eval(App(pred,toChurchNumB(8)))))
}
}
let _ = DebruTest.test()
测试输出:
fun -> fun -> 1 (1 (1 (1 (1 (1 (1 0))))))
实现扩展Lambda表达式(+Let)的de Bruijn index形式的解释器。
注意到eval Let(l1, l2) == eval App(Fn(l2),l1)
在实现时将所有Let(l1, l2)替换成App(Fn(l2),l1)即可。
// Homework: support let
module DebruLet = {
type rec lambda =
| Var (int)
| App (lambda, lambda)
| Fn (lambda)
| Let (lambda, lambda)
let rec toString = (t: lambda): string => switch t {
| Var(i) => Js.Int.toString(i)
| Fn(b) => "(λ." ++ toString(b) ++")"
| App(m, n) => "(" ++ toString(m) ++" " ++ toString(n) ++ ")"
| Let(l, b) => "(" ++ toString(l) ++ "->" ++ toString(b) ++ ")"
}
let print_lambda = l => {
let print_paren = (b, s) => {
if b { "(" ++ s ++ ")" } else { s }
}
let rec go = (l, p) => {
switch l {
| Var(x) => Js.Int.toString(x)
| Fn(a) => print_paren(p>0, "fun -> " ++ go(a, 0))
| App(a, b) => print_paren(p>1, go(a, 1) ++ " " ++ go(b, 2))
| Let(l, b) => print_paren(p>1, "let " ++ go(l,1) ++ " in " ++ go(b, 2))
}
}
go(l, 0)
}
// Var(j) becomes Var(i+j) if j >= d
let rec shift_aux = (i, d, u) => {
switch u {
| Var(j) => { if j >= d { Var(i+j) } else { u } }
| Fn(b) => Fn(shift_aux(i, d+1, b))
| App(a, b) =>
App(shift_aux(i, d, a), shift_aux(i, d, b))
| Let(l, b) => shift_aux(i,d,App(Fn(b),l))
}
}
let shift = (i, u) => shift_aux(i, 0, u)
// t[u/i]: use u to replace Var(i) in term t
let rec subst = (t, i, u) => {
switch t {
| Var(j) => if j == i { u } else { t }
| Fn(b) => Fn(subst(b, i+1, shift(1, u)))
| App(a, b) => App(subst(a, i, u), subst(b, i, u))
| Let(l, b) => subst(App(Fn(b),l),i,u)
}
}
// eval Let(l1, l2) == eval App((lambda.l2),l1)
let eval = (t: lambda) => {
let rec evalHelper = (t:lambda) => {
switch t {
| Var(_) => t
| Fn(b) => Fn(evalHelper(b))
| Let(loc, body) => evalHelper(App(Fn(body),loc))
| App(f, arg) => {
switch evalHelper(f) {
| Fn(b) => {
let va = evalHelper(arg)
let va_shifted = shift(1,va)
let b_substed = subst(b, 0, va_shifted)
let b_substed_deshifted = shift(-1, b_substed)
evalHelper(b_substed_deshifted)
}
| k => App(k, evalHelper(arg))
}
}
}
}
evalHelper(t)
}
}
下面是测试代码:
module DebruLetTest = {
open! DebruLet
let id = Fn(Var(0))
let trueB = Fn(Fn(Var(1)))
let falseB = Fn(Fn(Var(0)))
let zero = Fn(Fn(Var(0)))
let one = Fn(Fn(App(Var(1),Var(0))))
let succ = Fn(Fn(Fn(App(Var(1),App(App(Var(2),Var(1)),Var(0))))))
let pair = Fn(Fn(Fn(App(App(Var(0),Var(2)),Var(1)))))
let fst = Fn(App(Var(0),trueB))
let snd = Fn(App(Var(0),falseB))
let pred = Let(
Fn(App(
App(pair,App(snd,Var(0))),
App(succ,App(snd,Var(0))))),
Let(
App(App(pair,zero),zero),
Fn(App(fst,App(App(Var(0),Var(2)),Var(1))))
))
let toChurchNumB = (n : int) => {
let rec helper = (n: int, churchNumB: lambda) : lambda =>
if n < 0 {assert false}
else {
switch n {
| 0 => churchNumB
| _ => helper(n-1, App(succ, churchNumB))
}
}
helper(n, zero)
}
let test = () => {
Js.log(print_lambda(eval(Let(pred,App(Var(0),toChurchNumB(8))))))
}
}
let _ = DebruLetTest.test()
测试结果:
fun -> fun -> 1 (1 (1 (1 (1 (1 (1 0))))))
赞持续学习