作为程序员的我们用不同编程语言写程序多年了。 我们是否反思过,为什么需要程序,为什么我们需要编程语言,究竟什么是编程语言,它们是怎样运作的?同时,函数式编程范式现在是一门很热的潮流,那什么是函数式编程?是 Haskell, Scalar 还是其它呢?在此,我想基于自己对类型理论(Type Theory),范畴论(Category Theory), 证明论(Proof Theory) 的理解,分享下我对以上问题看法。
(更详尽的内容请点击前往我的另一遍文章<Type Theory in Swift>)
为什么我们需要程序呢?所有的人类活动都是为了创造有形的和无形的价值。其中生产力就是衡量我们创造价值的速度。因此我们需要程序的原因是为了提高生产力。换言之,通过程序去自动化解决问题。我们对现实生活的问题抽象建模成一个概念和关系的体系,以便于我们思考如何一步步地解决问题。例如,我们在研究物理问题时候,把物体看作为质点,然后研究其的运动轨迹,即其于其环境的空间关系及变化。也就是说,我们思考出来的解决问题的步骤,就是算法。然后,我们把算法用某种编程语言表达及编译成程序,让其运行在机器上,那么,机器就会根据我们的想法一步步的解决问题。因此,编程语言是一门语言,是表达计算(Computation)的一种方式,可以同时让我们人类和机器理解。
那什么是计算(Computation)呢?计算是基于当前状态,知道如何走到下一步,如此一步步走向目标。因此,计算是允许有结束的,得出结果,和没有结束的,无限循环。算法就是一连串的计算组成起来去解决特定的问题。
现在,我们了解为什么需要程序和编程语言了。因此是时候问,什么是编程语言。基于Robert Harper的论述,编程语言有那个组成部分,静态(Statics)和动态(Dynamics). 静态(Statics)包括具体语法(Concrete syntax),抽象(Abstract Syntax) 以及 上下文相关的条件(Context sensitive conditions),那些条件指的是 类型形成规则(Type Formation rules) 和 表达式类型规则 (Typing judgement for expression).
而动态(Dynamics)包括 值规范(Specifications of Values) 和 表达式化简与计算 (Expression transitions or Expression Evaluation).
这两部分是相互一致,共同地保证类型安全(Type safety),即所有类型正确的程序(well-typed programs)都会在运行时正确地执行(well behave),也就是说,类型正确的程式永远不会执行未定义的行为(well-typed programs never get stuck).
编程语言的具体语法规定了我们如何在文本编辑器上编写程序,即程序如何通过字符串表达出来。即,具体语法决定了源代码的样子。在源代码被编译器解析后,编译器会根据程序逻辑去构建抽象语法树(Abstract Syntax Trees) 和 抽象绑定树(Abstract Binding Trees) ,其中抽象绑定树就是抽象语法树上加上变量(Variable Bindings)和作用域(Scoping)。它们都是树结构,节点为操作器,叶子为变量或值。同时,它们确定了什么是表达式,如何构建表达式。这里的变量是数学上的变量,而非编程语言中所谓的变量,一旦赋值后,其值不变,就系 Swift 中的常量(Constant)的概念。而编程语言的变量,我们给另一个名字,可重赋值量(Assignable),可以重复对其赋值,以便区分。因为这不同特性,会根本性地改变我们的推理。
下面说下类型形成规则(Type Formation rules) 和 表达式类型规则 (Typing judgement for expression)。它们指定了什么是类型,表达式是什么类型。在Swift的语言规范(The Swift Programming Language Reference)中,我们见到有不同章节(Sections),包括介绍类型的(Type),表达式的(Expression),声明的(Declaration)。那些章节告诉我们怎样用Swift创建类型和表达式。在声明章节中,我们知道如何创建类型,变量,及其他内容。在类型章节中,我们知道,在Swift中,什么是类型。在表达式章节中,我们知道如何在Swift中写表达式。这些内容就是Swift 静态的一部分。
转到动态。动态指程序的运行,也就是怎么实现计算。动态规范了不同的规则去指出怎么样的表达式是值,怎样去得到一个表达式的值。它就像一个有限状态机,指明开始状态(initial state, any expression), 结束状态(final state, values),以及怎么实现状态变换(state transition, expression evaluation).
在动态求值的过程中,是无关类型信息的。因为静态与动态的一致保证了类型正确的程序必会正确执行(well-typed program will well behave when it runs)。换言之,类型在状态转换时会被保留(type is preserved by transition (Preservation)),同时如果一个表达式是类型正确的,那它必定是一个值或者可以通过状态转换到另一个表达式(if an expression is well typed, then it will be a value or can transition to another expression (Progress))。从而我们可以定义函数式编程就是具备这样的属性,即程序的可决定性。
值得一提的是,类型不等同集合,因为类型允许分函数(Partial Function)和全函数(Total Function),分函数指不收敛的函数,有可能没有返回值,全函数指收敛函数,肯定会有返回值。而集合只允许全函数。具体的例子可以查看递归类型(Recursive Types)。
类型是表达式的行为规范。这意味着一个表达式的类型指明了那表达式的行为。而且,在一个函数里面,函数只知道参数的类型,而不知参数的具体值。这也保证了程式的抽象力和组合力。因此不同类型和不同种的类型给出了不同的行为规范,也就保证了程式的运行。再者,一门编程语言的语言特性是由它的类型体系决定的。因此,在一定程度上,对类型体系的了解决定了程序的质量。