untyped lambda calculus

(阅读笔记,低质,请直接翻看引用)

强烈推荐 https://www.youtube.com/watch?v=FITJMJjASUs

------更新---------

最近在看TAPL,遇到了lambda-calculus,这算是我第一次系统性的学习这个概念。然后就像了解到信息论和图灵机一样,跪倒在地。按照自己不记录一下就没有学透彻的惯例,记录一下学习心得。

  • Functions are the only data type
  • λ \lambda λ binding is the only way to associate values to variables
  • Calculation happens via Beta (or Alpha) reduction

lambda 相关约定

t : : = t e r m s x v a r i a b l e λ x . t a b s t r a c t i o n t   t a p p l i c a t i o n t ::= \qquad \qquad \qquad \qquad terms\\ \qquad x \qquad \qquad \qquad\qquad variable \\ \qquad \lambda x.t \qquad \qquad \qquad abstraction \\ \qquad t \ t \qquad \qquad \qquad application t::=termsxvariableλx.tabstractiont tapplication

  • application associates to the left,例如 s   t   u s\ t\ u s t u,左结合就是 ( s   t )   u (s\ t)\ u (s t) u
  • the bodies of abstractions are taken to extend as far to the right as possible,例如 λ x .   λ y .   x   y   x \lambda x. \ \lambda y.\ x\ y\ x λx. λy. x y x,首先 λ x .   ( λ y .   x   y   x ) \lambda x. \ (\lambda y.\ x\ y\ x) λx. (λy. x y x),然后 λ x .   ( λ y .   ( x   y   x ) ) \lambda x. \ (\lambda y.\ (x\ y\ x)) λx. (λy. (x y x)),下面就是最里面的左结合 λ x .   ( λ y .   ( ( x   y )   x ) ) \lambda x. \ (\lambda y.\ ((x\ y)\ x)) λx. (λy. ((x y) x))
  • λ y .   x   y \lambda y. \ x \ y λy. x y中的 x x xfree
  • ( λ x . x )   x (\lambda x.x)\ x (λx.x) x中最后面的x是free
  • 如果在一个term中没有free variable,那么term就是closed的,被称作combinators。最简单的combinator i d = λ x .   x id = \lambda x.\ x id=λx. x

柯里化

In the lambda-calculus everything is a function: the arguments accepted by functions are themselves functions and the result returned by a function is another function.

每个abstraction只能限定一个参数,例如 λ x \lambda x λx只有一个参数 x x x,那么 λ \lambda λ只能处理一个参数吗?

In mathematics and computer science, currying is the technique of translating the evaluation of a function that takes multiple arguments into evaluating a sequence of function, each with a single argument.

我比较熟悉SML,SML的柯里化如下代码:

fun add x y = x + y;
val add3 = add 3;
val res = add3 4;

关于Alpha转换和Beta规约的内容,我的最爱Lambda演算——开篇讲的比较浅显易懂。

Call by Value

Call by value means: reduce the argument to normal form and then bind the parameter to it

对于 ( λ x .   x )   ( ( λ x .   x )   ( λ z .   ( λ x .   x )   z ) ) (\lambda x.\ x)\ ((\lambda x.\ x)\ (\lambda z.\ (\lambda x.\ x)\ z)) (λx. x) ((λx. x) (λz. (λx. x) z))这个reducible expression,我们可以按照下面的方式来进行reduction,也就是转化成 a p p l i c a t i o n application application。这种方式就是call by valueallowing no reductions inside abstractions
Call-By-Name
call by value被应用在了很多种编程语言中,例如我熟知的C++和Golang,本质上就是在函数调用之前先把参数的值求出来,然后再调用。

Call by Name

Call by name means: replace each occurrence of the parameter in the body of the function by the unevaluated argument

对于call-by-name来说,就是先replace,然后再evaluate evaluate,是一种lazy的方式,一些函数式语言中有这样的方式。
call-by-value

Haskell uses an optimized version known as call-by-need that, instead of re-evaluating an argument each time it is used, avoiding the need for sub-sequent re-evaluation.

Church booleans

lambda演算是一种自洽的系统,其中布尔和自然数和我们常识中的可能不太一样。Church booleans定义如下:

t r u e = λ t .   λ f .   t true = \lambda t.\ \lambda f.\ t true=λt. λf. t
f a l s e = λ t .   λ f .   f false = \lambda t.\ \lambda f.\ f false=λt. λf. f

所以truefalse本身也是一个函数,true返回第一个参数的值,false返回第二个参数的值。为此我们可以定义另外一个application,

t e s t   =   λ l .   λ m .   λ n .   l   m   n ; test \ =\ \lambda l.\ \lambda m.\ \lambda n.\ l\ m\ n; test = λl. λm. λn. l m n;

对于 t e s t   b   v   w test\ b\ v\ w test b v w而言,如果 btrue,则返回v,如果bfalse则返回第二个w

例如对于 t e s t   t r u e   v   w test\ true\ v\ w test true v w,整个evaluation的过程如下:
在这里插入图片描述
这里有一个小trick,就是church boolean不只是表征的一个状态,它还会参与到后续的evaluate过程中。

类似我们还可以定义 a n d and and运算符, a n d and and接受两个实参,返回一个值,如果两个实参都为 t r u e true true则返回 t r u e true true,否则返回 f a l s e false false

a n d   =   λ m .   λ n .   m   n   f a l s e and\ =\ \lambda m.\ \lambda n.\ m\ n\ false and = λm. λn. m n false

o r or or运算符如下,

o r   =   λ m .   λ n .   m   t r u e   n or\ =\ \lambda m.\ \lambda n.\ m\ true\ n or = λm. λn. m true n

n o t not not运算符如下,
n o t   =   λ m .   m   f a l s e   t r u e not\ =\ \lambda m.\ m\ false\ true not = λm. m false true

Church Pairs

church pairs是比较精巧的,定义如下
p a i r   =   λ f .   λ s .   λ b .   b   f   s pair\ =\ \lambda f.\ \lambda s.\ \lambda b.\ b\ f\ s pair = λf. λs. λb. b f s
f s t   =   λ p .   p   t r u e fst\ =\ \lambda p.\ p\ true fst = λp. p true
s e c o n d   =   λ p .   f a l s e second\ =\ \lambda p.\ false second = λp. false

例如 p a i r   v   w = λ b .   b   v   w pair\ v\ w= \lambda b.\ b\ v\ w pair v w=λb. b v w,右侧可以看做左侧柯里化以后的结果。而 f s t fst fst或者 s n d snd snd可以将剩下的参数填充进去。

Church numerals

类似于 c h u r c h   b o o l e a n s church\ booleans church booleans c h u r c h   n u m e r a l s church\ numerals church numerals将一系列的自然数定义为了一组函数。
c 0   = λ s .   λ z .   z ; c_0\ = \lambda s.\ \lambda z.\ z; c0 =λs. λz. z;
c 1   = λ s .   λ z .   s   z ; c_1\ = \lambda s.\ \lambda z.\ s\ z; c1 =λs. λz. s z;
c 2   = λ s .   λ z .   s   ( s   z ) ; c_2\ = \lambda s.\ \lambda z.\ s\ (s\ z); c2 =λs. λz. s (s z);
c 3   = λ s .   λ z .   s   ( s   ( s   z ) ) ; c_3\ = \lambda s.\ \lambda z.\ s\ (s\ (s\ z)); c3 =λs. λz. s (s (s z));

s s s的个数来表征具体的数值, z z z可以看做一个辅助性结尾的功能。我们可以看到,对任何数字,例如 c 0 c_0 c0传入 c 0   s   z c_0\ s\ z c0 s z得到的还是 c 0 c_0 c0本身。

对应的 s c c scc scc可以定义成下面的形式,最前面的 s s s就相当于在 s s s的计数上加了一个1。
s u c c   = λ n .   λ s .   λ z .   s   ( n   s   z ) succ\ = \lambda n.\ \lambda s.\ \lambda z.\ s\ (n\ s\ z) succ =λn. λs. λz. s (n s z)

succ
加法如下所示,可以看到 m m m后面的 s s s起着一个连接的作用。
p l u s   = λ m .   λ n .   λ s .   λ z .   m   s   ( n   s   z ) plus\ =\lambda m.\ \lambda n.\ \lambda s.\ \lambda z.\ m\ s\ (n\ s\ z) plus =λm. λn. λs. λz. m s (n s z)
在这里插入图片描述
关于church numerals的加减乘除,CSE 340 11-30-15 Lecture: "Lambda Calculus Pt. 3中有非常好的解释。

p o w e r power power如下所示:

p o w e r = λ m .   λ n .   m   n power=\lambda m.\ \lambda n.\ m\ n power=λm. λn. m n

Recusion

这一部分是最复杂的,可以查看康托尔、哥德尔、图灵——永恒的金色对角线,刘未鹏的推导也是极其复杂,这里采用Clear, intuitive derivation of the fixed-point combinator (Y combinator)?和《Programming Languages and Lambda Calculi》的推导过程。

只想直观感受lambda calculus中的recusion是如何实现的,可以参考CSE 340 12-2-15 Lecture: "Lambda Calculus Pt. 4 and Midterm 2 Review"Essentials: Functional Programming’s Y Combinator - Computerphile

我们先思考一个问题,如何在lambda calculus中实现阶乘?对于有编程经验的人,第一反应应该是下面的代码:

f a c t = λ n .   if   iszero   n   then   1 else   n ∗ f a c t ( pred   n ) fact=\lambda n.\ \textbf{if}\ \textbf{iszero}\ n\ \\ \qquad \qquad \quad \textbf{then}\ 1 \\ \qquad \qquad \quad \textbf{else}\ n * fact(\textbf{pred}\ n) fact=λn. if iszero n then 1else nfact(pred n)

但是上面的代码在lambda calculus中是不可能实现的。因为本质上lambda calculus都是“匿名”的。也就是我们需要填满下面的something

f a c t = λ n .   if   iszero   n   then   1 else   n ∗ s o m e t h i n g ( pred   n ) fact=\lambda n.\ \textbf{if}\ \textbf{iszero}\ n\ \\ \qquad \qquad \quad \textbf{then}\ 1 \\ \qquad \qquad \quad \textbf{else}\ n * something(\textbf{pred}\ n) fact=λn. if iszero n then 1else nsomething(pred n)

一种可能的方式就是把something提出来做成一个参数,然后在apply的时候,填充进去。如下所示的参数f

f a c t = λ f .   ( λ n .   if   iszero   n   then   1   else   n ∗ f ( pred   n ) ) fact=\lambda f.\ (\lambda n.\ \textbf{if}\ \textbf{iszero}\ n\ \textbf{then}\ 1\ \textbf{else}\ n * f(\textbf{pred}\ n)) fact=λf. (λn. if iszero n then 1 else nf(pred n))

能不能把上面的body复制一遍呢?如下所示:

f a c t = ( λ f .   ( λ n .   if   iszero   n   then   1   else   n ∗ f ( pred   n ) ) )   ( λ f .   ( λ n .   if   iszero   n   then   1   else   n ∗ f ( pred   n ) ) ) fact=(\lambda f.\ (\lambda n.\ \textbf{if}\ \textbf{iszero}\ n\ \textbf{then}\ 1\ \textbf{else}\ n * f(\textbf{pred}\ n)))\ (\lambda f.\ (\lambda n.\ \textbf{if}\ \textbf{iszero}\ n\ \textbf{then}\ 1\ \textbf{else}\ n * f(\textbf{pred}\ n))) fact=(λf. (λn. if iszero n then 1 else nf(pred n))) (λf. (λn. if iszero n then 1 else nf(pred n)))

那这样可不可行呢?其实不太可行,进行到第(5)步的时候,我们少了一个实参,也就是 f f f
recusion
那么怎么才可以呢?上面的形式太复杂,我们用另外一种方式呈现,例如下面的递归形式,我们要在 f f fbody里调用 f f f

f =   . . . .   f   . . .   f   . . . f = \ ....\ f\ ...\ f\ ... f= .... f ... f ...

为了达到递归的目的,我们需要将f提出来,然后将其作为参数,也就是下面的形式。

f = ( λ r . ( . . . r   . . . r   . . . ) ) f f = (\lambda r.(...r\ ...r\ ...))f f=(λr.(...r ...r ...))f
f = ( λ r . ( . . . r   . . . r   . . . ) ) ( λ r . ( . . . r   . . . r   . . . ) ) f = (\lambda r.(...r\ ...r\ ...))(\lambda r.(...r\ ...r\ ...)) f=(λr.(...r ...r ...))(λr.(...r ...r ...))

现在我们有了什么呢?我们有了M,也就是一个被抽离了自我调用的带有body的形式。此时就回到了我们最开始的尝试,当然这种尝试并没有什么有意义的进步。

M

上面的其实不是递归的,因为里面已经没有 f f f了,怎样才能在 b o d y body body里构造出来 f f f呢,可能的一种方式就是构造 M M MM MM出来,如下所示,如果我们用 M M M替换其中的 x x x,然后 r r r替换成 x x xx xx,那么里面就会出现 M M MM MM,也就是 f f f。但是下面的形式不是递归的,因为需要实参 M M M的参与,才能构造出 f f f本身。
MM
我们暂时放弃在 f f f上构造递归的可能,先把上面的形式泛化,注意其中的 M M M是真正的函数体。我们可不可以把函数体 M M M提出来,如下所示:

Y

此时我们有了一个成为 Y Y Y的东西, Y   M Y\ M Y M就会构造出 f f f。但是这个东西有什么卵用呢?这个东西有下面的性质,就是 Y   M = Y   ( Y   M ) Y\ M = Y\ (Y\ M) Y M=Y (Y M)。由于 f = Y   M f=Y\ M f=Y M,所以 Y Y Y有这样的性质,就是将它应用在我们想要递归的函数体 M M M上,那么就会源源不断的产生 M M M,也就是 Y   M = M   ( M ( . . . ( Y   M ) ) ) Y\ M=M\ (M(...(Y\ M))) Y M=M (M(...(Y M)))至此我们大概已经意识到我们想要构造出来的 f f f其实就是 M M M
YM
综上lambda calculus上真正的递归是无法实现的,但是我们可以通过一个 Y c o m b i n a t o r Y combinator Ycombinator间接实现的递归,如下所示:

M(Y M)
注意到,这里我们需要将 Y c o m b i n a t o r Y combinator Ycombinator作为参数传进去,这也解释了我们刚开始构造递归形式失败的原因,也就是 f = M   M f=M\ M f=M M失败的原因,因为它只能进行两次 M M M的递归调用,而 Y Y Y就是一个源源不断的 M M M生成器。其实 Y Y Y内部借助了下面 o m e g a omega omega的形式, o m e g a omega omega的特点就是不断地重复自己,一个死循环。正由于 o m e g a omega omega是lambda calculus能实现递归的核心,所以TAPL将其放在了recursion这一节的开头。

omega
前面看似几步就推出了 Y   c o m b i n a t o r Y\ combinator Y combinator,但这都是后事之师,真正从一张白纸找到 Y   c o m b i n a t o r Y\ combinator Y combinator还是极其困难的。

https://cgnail.github.io/academic/lambda-1/
https://cgnail.github.io/academic/lambda-2/
https://cgnail.github.io/academic/lambda-3/
https://cgnail.github.io/academic/lambda-4/
https://liujiacai.net/blog/2014/10/12/lambda-calculus-introduction/
https://blog.csdn.net/g9yuayon/article/details/748684
https://blog.csdn.net/pongba/article/details/1336028
https://github.com/txyyss/Lambda-Calculus/releases
https://www.youtube.com/watch?v=6goESiHNhIo
http://www-sop.inria.fr/members/Yves.Bertot/misc/types-eng.pdf
https://en.wikipedia.org/wiki/Lambda_calculus
http://fsl.cs.illinois.edu/images/2/26/CS522-Spring-2013-Lambda-and-Combinators.pdf
https://www.youtube.com/watch?v=FITJMJjASUs
http://www.cs.bham.ac.uk/~axj/pub/papers/lambda-calculus.pdf
http://palmstroem.blogspot.com/2012/05/lambda-calculus-for-absolute-dummies.html
https://www.youtube.com/watch?v=SphBW9ILVPU&feature=youtu.be&t=575
https://news.ycombinator.com/item?id=19835615
https://news.ycombinator.com/item?id=13258037
https://mvanier.livejournal.com/2897.html
Lambda calculus: Call by value / Call by name (lazy)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值