【TAPL学习笔记 3】The Untyped Lambda-Calculus

在lambda 演算中,所有的计算都可以归结为函数定义和应用,它在语言功能的规约、语言设计与实现以及类型系统的研究中有广泛应用。它的重要性来自于既可以视作简单的程序语言,又可以看作一个能够严格证明的数学对象。
lambda演算仅仅是core calculi中的一个,除此之外pi-caculus可以用来定义基于消息的并发语言语义;object calculus蒸馏了OO语言的核心功能。

lambda calculus可以通过不同方式来进行扩充。首先,添加特殊的具体语法如numbers,tuples,records等很方便,它们的行为可以在core language中模拟。甚至还可以添加更复杂的特性如可变引用等。正如我们将看到的,对core language的扩展也经常牵扯到类型系统的扩展。

5.1 Basics

过程式(或者函数式)的抽象是所有语言的核心功能。不用一遍遍写重复的计算过程,我们只需要写一个通用的过程或者函数,之后实例化这个函数,为每个参数提供值。
这里我们给出一个阶乘的定义:
在这里插入图片描述
本质上,在lambda演算中,所有事情都是函数。它的语法只包含三类terms。
在这里插入图片描述
下面的小节探讨了定义的细节。

抽象和具体语法

当讨论程序语言的语法时,我们需要区分两类结构:

  • 具体语法: 程序员直接读和写的字符串
  • 抽象语法:程序作为标记树

从具体语法到抽象语法的转化过程,一般分为两个阶段:

  1. 词法分析
  2. parser:将一系列tokens转为抽象语法树

这本书的重点是抽象语法,我们一般需要在心里认为所写的term实际上是一棵抽象语法树。

为了少写括号,我们采用了两个习惯:

  1. 函数应用是左结合的,例如s t u表示 (s t) u
  2. 函数抽象的内部要尽可能向右扩展,例如λx. λy. x y x -> λx. (λy. ((x y) x))

变量和元变量

t表示任意term;而x表示任意变量。

Scope

我们必须处理的最后一个问题是变量的作用域(Scope)。变量x称为bound,当它出现在函数体t中。我们可以说lambda x是一个binder,它的作用域是t。变量x称为free,当它没有被一个x上的抽象绑定。

一个没有free variables的term称为封闭的。closed terms也称为combinators(组合子)。最简单的组合子,是恒等函数,
id = lambda x.x;

操作语义

在纯粹的lambda演算中,并不存在内置的常量或者原始的算子–没有numbers,算术操作,条件,records,循环,I/O等。唯一的terms计算的方式就是函数的应用。计算的每一步如下表示:
在这里插入图片描述
我们称(lambda x.t12) t2为redex,并把上述rewrite redex的操作称为beta reduction。存在一些不同的求值规则:

  • full beta-reduction: 任何redex都可以在任何时候退化。
    在这里插入图片描述
  • normal oder:最外层的首先被规约。
    在这里插入图片描述
  • call by name: 不允许抽象内部进行规约
    在这里插入图片描述
  • call by value: 只有最外层的redex被规约,并且它的右边应该已经被规约成一个值,注意到这里唯一的值其实只有函数抽象。
    在这里插入图片描述
    注意到这个策略是严格的,无论函数参数是否被用到,它都必须先求值。

求值策略的选择事实上不会对类型系统的讨论产生影响。在本书中,我们使用call by value。

5.2 Programming in Lambda-Calculus

这一节我们将看一些使用lambda calculus编程的例子。

Multiple Arguments

我们可以观察到lambda calculus并没有提供内置的对于多参数函数的支持。这里我们可以使用high-order函数达到相同的目的。这种转换也称为currying。

f = λx.λy.s --> f v w

Church Booleans

我们可以在lambda演算中编码布尔值。可以定义tru和fls,如下:
在这里插入图片描述
这里我们想定义一个组合子test,具有性质test b v w规约为v当b为tru时;规约为w当b为fls时。
在这里插入图片描述
这里 test b v w 实际上规约为b v w。
在这里插入图片描述
我们接着定义and操作:
在这里插入图片描述
or = lambda b. lambda c. b tru c
not = lambda b. b fls tru

Pairs

在这里插入图片描述
在这里插入图片描述

Church Numerals

在这里插入图片描述
我们在这里可以定义后继函数:
在这里插入图片描述
其实就是继续应用一次s。我们进一步定义plus:
在这里插入图片描述
可以认为这里的s其实不变,但是z我们可以认为并不是从0开始。对于乘法:
在这里插入图片描述
我们可以认为s变成plus n;应用了m次。
我们可以定义iszero函数:
在这里插入图片描述
减法相比于加法来说很难构造,它将使用下面的技巧“前驱函数”,给c0做参数返回c0;给ci+1 返回ci。
在这里插入图片描述

扩展演算

在λNB中,我们对于布尔值以及数字有两种不同的实现,我们可以通过如下方法进行转换:
在这里插入图片描述
在这里插入图片描述
我们也可以将church numeral转为对应原始数字:
在这里插入图片描述
我们不能直接将succ应用到m上,这是因为我们定义的succ必须需要应用到一个表达式上。
注意到如果我们直接应用 scc c1,那么实际上如果采用call by value,我们只能得到一个抽象,而不是c2。主要的问题是我们很难检查这个值是否与我们的预期一致。一个比较直接的方法直接应用 realnat (times c2 c2)

Recursion

我们称没有normal form的term是diverge的。
在这里插入图片描述
Omega组合子有一个有用的泛化形式,称为不动点(fixed-point)组合子,可以用来定义递归函数:
在这里插入图片描述
我们定义阶乘如下:
在这里插入图片描述

Representation

我们可以认为在实际的数字和Church-numeral表示之间对于程序的运行结果并不具有较大差异。

5.3 Formalities

对于本章的剩余部分,我们更细节地考虑lambda演算的语法以及操作语义。

Syntax

在这里插入图片描述
在这里插入图片描述
我们可以定义一个term的自由变量。

Substitution

我们将使用两种不同的定义,第一种,我们将在本节引入,是紧凑和直观的;第二种我们将在第6章引入,记号上更加繁琐,具名变量将被数值索引替代。

首先让我们尝试一下可能的递归定义:
在这里插入图片描述
注意到如果选择bound变量,那么这个定义将不满足。例如,
在这里插入图片描述
很明显,我们在naive定义中犯的第一个错误是我们没有区分free和bound变量。下面是第二个尝试,
在这里插入图片描述
注意到这里仍会出现问题,
在这里插入图片描述
我们将一个常函数变为了恒等函数!
我们利用以下convention:
在这里插入图片描述
只有bound变量名字不同的terms我们认为是同一个term。例如,我们想计算[x -> y z] (lambda y. x y)我们可以首先rewrite (lambda y. x y) -> lambda w. x w。

在这里插入图片描述

操作语义

lambda演算的操作语义总结为下图:
在这里插入图片描述
有一个计算规则【E-APPABS】,以及两个一致规则【E-APP1, E-APP2】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值