DennyQi's Log

01 λ-calculus的语法

图灵(Turing)和丘奇(Church)都为“计算”建立了模型,图灵的模型是图灵机,丘奇的模型是λ\lambda-calculus。可以证明这两个模型在计算能力(可计算的问题的集合)上是等价的。基于图灵机的原理诞生了Fortran,Pascal等等的编程语言,他们称为指令式编程语言(imperative programming language),它们是通过按照顺序执行指令控制读写头的移动和读写来实现计算的;与之相对的,基于λ\lambda-calculus的原理诞生了Lisp,Coq等等,它们称为函数式编程语言(functional programming language),它们是通过按照一定规则对字符串表达式进行重写(rewrite)而完成计算的。

λ\lambda-terms

我们把符号vv与若干个撇称为一个变量(variable)符号,全体变量符号的集合为V={v,v,v,}V=\{v,v',v'',\cdots\}。满足以下构造规则的符号串称为一个λ\lambda-term:

  • 单个变量xVx\in V是一个λ\lambda-term;
  • 如果M,NM,N都是λ\lambda-term,那么(MN)(MN)是一个λ\lambda-term;
  • 如果xVx\in V,并且MM是一个λ\lambda-term,那么(λxM)(\lambda xM)是一个λ\lambda-term;

所有λ\lambda-term组成的集合记为Λ\Lambda。例如,((λv(vv))v)Λ((\lambda v(v'v))v'')\in \Lambda。通常,我们可以用x,y,z,x,y,z,\cdots来表示变量,并允许省略多余的括号。例如,(λx(yx))zΛ(\lambda x(yx))z\in \Lambda

自由变量(free variables)

下面归纳地定义一个λ\lambda-term中的自由变量(free variable)集合,MM中的自由变量集合记为FV(M)FV(M)

  • 对于单个变量xVx\in VFV(x)=xFV(x) = {x}
  • 对于两个λ\lambda-term M,NM,NFV(MN)=FV(M)FV(N)FV(MN) = FV(M)\cup FV(N)
  • 对于变量xxλ\lambda-term MMFV(λxM)=FV(M){x}FV(\lambda xM) = FV(M)\setminus \{x\}

在一个λ\lambda-term中,如果xx不是自由变量,就称xx是一个受限变量(bounded variable)。如果MM中没有自由变量(比如M=λxxM=\lambda x x),就称MM是一个closed λ\lambda-term。全体closed λ\lambda-term集合记为Λ0\Lambda^0

替换(substitutions)

假设M,NM,Nλ\lambda-term,xx是一个变量,我们用记号M[x:=N]M[x:=N]表示把MM中所有自由出现的xx替换为NN。具体定义如下:

  • x[x:=N]x[x:=N]对应NN
  • 对于变量yVy\in V,若yxy\neq x,则y[x:=N]y[x:=N]对应yy
  • 对于λ\lambda-term M1,M2M_1,M_2(M1M2)[x:=N](M_1M_2)[x:=N]对应(M1[x:=N])(M2[x:=N])(M_1[x:=N])(M_2[x:=N])
  • 对于变量yy(λyM)[x:=N](\lambda yM)[x:=N]对应λy(M[x:=N])\lambda y(M[x:=N])

演算规则(Rules)

λ\lambda-calculus的第一条演算规则是:对于λ\lambda-term M,NM,N和变量xx(λxM)N(\lambda xM)N可以演算得到M[x:=N]M[x:=N]。其中,我们用等号来表示“可以演算得到”,所以这一规则可以写作

(λxM)N=M[x:=N](β)(\lambda x M)N=M[x:=N]\tag{$\beta$}

这一规则称为β\beta-conversion,这使得λxM\lambda xM这样的项可以被看作“函数”,当一个函数“作用(apply)”在一个λ\lambda-term上时,就是把这个term“代入”该函数的表达式。

从上述规则可以看出,紧跟在λ\lambda后面的受限变量xx仅仅起到一个临时变量的作用,我们并不关心这个变量的取值,由此定义λ\lambda-calculus的第二条演算规则,称为α\alpha-equivalance(yy是一个全新的变量:yy不包含在MM中;yy不是xx):

λxM=λy(M[x:=y])(α)\lambda xM=\lambda y(M[x:=y])\tag{$\alpha$}

除了β\beta-conversion和α\alpha-equivalance,在λ\lambda-calculus中还需定义以下基本的演算规则:

(1) 演算的等价关系性质:

  • 自反性:M=MM=M
  • 对称性:M=N    N=MM=N \iff N=M
  • 传递性:M=N,N=L    M=LM=N,N=L\implies M=L

(2) 相容性(compatibility):

  • M=M    MZ=MZM=M'\implies MZ=M'Z
  • M=M    ZM=ZMM=M'\implies ZM=ZM'
  • M=M    λxM=λxMM=M'\implies \lambda xM=\lambda xM'

Compatibility意味着rewrite可以仅发生在局部。

柯里化(Currying)

根据β\beta-conversion,λxM\lambda x M可以看作“一元函数”。如果要表示“二元函数”,可以用λx(λyM)\lambda x(\lambda y M)的形式。这是因为当一个二元函数apply在两个term上时,可以看作该二元函数首先apply在第一个term上得到了一个一元函数,这个一元函数再apply在第二个term上得到了函数值。这种把多元函数拆分成多个一元函数的想法最早是由H.B. Curry提出的,称为函数的柯里化(Currying)。

Fλx(λyM)F\equiv\lambda x(\lambda y M),它apply在N1,N2N_1,N_2上时应当写作(FN1)N2(FN_1)N_2,这样就会有(FN1)N2((λx(λyM))N1)N2=(λy(M[x:=N1]))(N2)(FN_1)N_2\equiv((\lambda x(\lambda y M))N_1)N_2=(\lambda y (M[x:=N_1]))(N_2) =(M[x:=N1])[y:=N2]=(M[x:=N_1])[y:=N_2]。一般地,如果FFnn元函数,它apply在N1,,NnN_1,\cdots,N_n时应当写作(((FN1)N2)Nn)( \cdots((FN_1)N_2)\cdots N_n)。方便起见,我们规定apply是左结合的,这样我们就可以在表示多元函数时省略括号,写为FN1N2NnFN_1N_2\cdots N_n

注意我们用\equiv表示元语言上的指代,用==表示λ\lambda-calculus的演算。

在函数本身的表示上,对于二元函数我们写λx1(λx2M)\lambda x_1(\lambda x_2 M),对于nn元函数我们写λx1(λx2(λx3((λxnM))))\lambda x_1(\lambda x_2(\lambda x_3(\cdots (\lambda x_nM))))。所以,多个λxi\lambda x_i之间是右结合的,不能省略括号。为了方便,我们发明下面的符号来简化书写:只写一个λ\lambda,然后把变量并排依次写出,然后写一个点,然后写函数体,这样nn元函数简写为λx1x2xn.M\lambda x_1x_2\cdots x_n.M

用归纳法易证(λx1xn.M)N1Nn=(((M[x1:=N1]))[xn:=Nn])(\lambda x_1\cdots x_n.M)N_1\cdots N_n=(( \cdots(M[x_1:=N_1])\cdots)[x_n:=N_n])

“函数”一词是集合论或日常语言中的定义。在λ\lambda-calculus中,形如λx1xn.M\lambda x_1\cdots x_n.M的term称为combinator(组合子)。例如,通常记Iλx.x\textsf{I}\equiv \lambda x.x,这就是“恒等映射”,IM=M\textsf{I}M=M;记Kλxy.x\textsf{K}\equiv \lambda xy.x,它用来取两个term中的前一个,KMN=M\textsf{K}MN=M;记Kλxy.y\textsf{K}_\ast\equiv \lambda xy.y,用来取两个term中的后一个,KMN=N\textsf{K}_\ast MN=N;…