图灵(Turing)和丘奇(Church)都为“计算”建立了模型,图灵的模型是图灵机,丘奇的模型是λ-calculus。可以证明这两个模型在计算能力(可计算的问题的集合)上是等价的。基于图灵机的原理诞生了Fortran,Pascal等等的编程语言,他们称为指令式编程语言(imperative programming language),它们是通过按照顺序执行指令控制读写头的移动和读写来实现计算的;与之相对的,基于λ-calculus的原理诞生了Lisp,Coq等等,它们称为函数式编程语言(functional programming language),它们是通过按照一定规则对字符串表达式进行重写(rewrite)而完成计算的。
λ-terms
我们把符号v与若干个撇称为一个变量(variable)符号,全体变量符号的集合为V={v,v′,v′′,⋯}。满足以下构造规则的符号串称为一个λ-term:
- 单个变量x∈V是一个λ-term;
- 如果M,N都是λ-term,那么(MN)是一个λ-term;
- 如果x∈V,并且M是一个λ-term,那么(λxM)是一个λ-term;
所有λ-term组成的集合记为Λ。例如,((λv(v′v))v′′)∈Λ。通常,我们可以用x,y,z,⋯来表示变量,并允许省略多余的括号。例如,(λx(yx))z∈Λ。
自由变量(free variables)
下面归纳地定义一个λ-term中的自由变量(free variable)集合,M中的自由变量集合记为FV(M):
- 对于单个变量x∈V,FV(x)=x;
- 对于两个λ-term M,N,FV(MN)=FV(M)∪FV(N);
- 对于变量x与λ-term M,FV(λxM)=FV(M)∖{x}
在一个λ-term中,如果x不是自由变量,就称x是一个受限变量(bounded variable)。如果M中没有自由变量(比如M=λxx),就称M是一个closed λ-term。全体closed λ-term集合记为Λ0。
替换(substitutions)
假设M,N是λ-term,x是一个变量,我们用记号M[x:=N]表示把M中所有自由出现的x替换为N。具体定义如下:
- x[x:=N]对应N;
- 对于变量y∈V,若y=x,则y[x:=N]对应y;
- 对于λ-term M1,M2,(M1M2)[x:=N]对应(M1[x:=N])(M2[x:=N]);
- 对于变量y,(λyM)[x:=N]对应λy(M[x:=N])
演算规则(Rules)
λ-calculus的第一条演算规则是:对于λ-term M,N和变量x,(λxM)N可以演算得到M[x:=N]。其中,我们用等号来表示“可以演算得到”,所以这一规则可以写作
(λxM)N=M[x:=N](β)
这一规则称为β-conversion,这使得λxM这样的项可以被看作“函数”,当一个函数“作用(apply)”在一个λ-term上时,就是把这个term“代入”该函数的表达式。
从上述规则可以看出,紧跟在λ后面的受限变量x仅仅起到一个临时变量的作用,我们并不关心这个变量的取值,由此定义λ-calculus的第二条演算规则,称为α-equivalance(y是一个全新的变量:y不包含在M中;y不是x):
λxM=λy(M[x:=y])(α)
除了β-conversion和α-equivalance,在λ-calculus中还需定义以下基本的演算规则:
(1) 演算的等价关系性质:
- 自反性:M=M;
- 对称性:M=N⟺N=M;
- 传递性:M=N,N=L⟹M=L;
(2) 相容性(compatibility):
- M=M′⟹MZ=M′Z;
- M=M′⟹ZM=ZM′;
- M=M′⟹λxM=λxM′;
Compatibility意味着rewrite可以仅发生在局部。
柯里化(Currying)
根据β-conversion,λxM可以看作“一元函数”。如果要表示“二元函数”,可以用λx(λyM)的形式。这是因为当一个二元函数apply在两个term上时,可以看作该二元函数首先apply在第一个term上得到了一个一元函数,这个一元函数再apply在第二个term上得到了函数值。这种把多元函数拆分成多个一元函数的想法最早是由H.B. Curry提出的,称为函数的柯里化(Currying)。
记F≡λx(λyM),它apply在N1,N2上时应当写作(FN1)N2,这样就会有(FN1)N2≡((λx(λyM))N1)N2=(λy(M[x:=N1]))(N2) =(M[x:=N1])[y:=N2]。一般地,如果F是n元函数,它apply在N1,⋯,Nn时应当写作(⋯((FN1)N2)⋯Nn)。方便起见,我们规定apply是左结合的,这样我们就可以在表示多元函数时省略括号,写为FN1N2⋯Nn。
注意我们用≡表示元语言上的指代,用=表示λ-calculus的演算。
在函数本身的表示上,对于二元函数我们写λx1(λx2M),对于n元函数我们写λx1(λx2(λx3(⋯(λxnM))))。所以,多个λxi之间是右结合的,不能省略括号。为了方便,我们发明下面的符号来简化书写:只写一个λ,然后把变量并排依次写出,然后写一个点,然后写函数体,这样n元函数简写为λx1x2⋯xn.M。
用归纳法易证(λx1⋯xn.M)N1⋯Nn=((⋯(M[x1:=N1])⋯)[xn:=Nn])。
“函数”一词是集合论或日常语言中的定义。在λ-calculus中,形如λx1⋯xn.M的term称为combinator(组合子)。例如,通常记I≡λx.x,这就是“恒等映射”,IM=M;记K≡λxy.x,它用来取两个term中的前一个,KMN=M;记K∗≡λxy.y,用来取两个term中的后一个,K∗MN=N;…