Bootstrap

编程语言的一些基础概念(一):静态函数式编程

世界上最好的编程语言是什么?

这就好像问 世界上最好的车是什么车?F1 比赛的,日常家用的和跑山路的最好的车显然是不一样的。同理,不同的编程语言也有他们最适合的使用场景,程序员们通常都会个几种语言,因为工作需要可能要学新的语言。不同编程语言之间是不是完全不一样呢?他们之间有没有什么共同点是不同语言间类似的呢?有没有一些最基本的概念?

最近在看了 Coursera 上的 Programming Languages, Part A,对于这个问题做了部分解答,这里做个阶段性的总结。

这是门什么样的课?

Programming Languages 这个课程是华盛顿大学的公开课,评分很高,包括在知乎上,有很多人推荐。

课程在 Coursera 上一共有三部分,分别用不同的编程语言来做基础概念的说明和对比,既有函数式编程,也有面向对象编程,Part A 用的是 Standard ML (SML),Part B 是 Racket,Part C 是 Ruby,分别代表了不同类型的编程语言,为什么是这三门语言,作者在课程里有详细的解释。

[外链图片转存失败(img-9IPD7xDV-1562051143383)(https://i.imgur.com/r3yrs8R.png)]

这个课的重点不是在这几门语言本身,而是一些编程语言的概念,和使得这些语言变得优雅的语言特性。

函数式编程 和 SML 的一些语言特性

整个课程的系列就介绍到这,Part A 的重点是函数式编程以及 SML 的一些语言特性。包括了函数式编程的一些概念 数据不可变 (Immutable Data)头等函数 (First-Class Function)高阶函数 (Higher-Order Function) 等,以及 SML 本身的一些特性 类型推论 (Type Inference)柯里化和偏函数应用 (Currying and Partial Application)模块 (Module) 等。

类型推论(Type Inference)

SML 是静态类型语言( Statistically Typed),在编译时进行类型检查,同时它又是隐式类型 (Implicit Typed),可以省略类型声明,在编译时会自动做类型的推导,来判断类型是不是正确的。

val x = 42
fun f(y,z,w) = if y then z+x else 0

函数 f 的参数省略了声明类型,编译器从 x = 42z+x 可以推导出 x 和 z 的类型是 int,从 if y 可以推导出 y 的类型是 bool。这个特性能让代码简洁不少。

List 的用法和非函数式编程很不一样

在 SML 里,List 的最主要功能有两个 hd 代表 head,取数组的第一个元素,tl 表示 tail,数组的第一个之外的全部元素,因为有这样的特性,使得函数式编程的数组递归变得很顺其自然。

fun sum_list (xs : int list) =
	if null xs
	then 0
	else hd(xs) + sum_list(tl xs)

多态的数据类型(Polymorphic Datatypes)

SML 的数据类型有 int, bool, real, string 等具体的,函数接受的数据类型除了这些之外,也可以是多态的。

fun length xs =
	if null xs
	then 0
	else 1 + length(tl xs)

在这个例子里, xs 是一个 list,可以是一个 int list, string list 或者 bool list 等,所以 SML 表示 xs 的类型是 'a list 'a 表示数据类型的多态,也是能够通过 Type Inference 推导出来的。

数据不可变(Immutable Data)

在 SML 里,没有赋值的概念,而是由一系列的 **绑定(bindings)**组成的。比如说

(* 这里不是赋值,而是建立了一个新的 x 的 binding,
   在当前的环境下,x 是不能更改的,永远是等于 1 的 *)
val x = 1

(* 这里并不是对 x 的值进行修改,而是新建了一个 x 的 binding,
	 前面的 x 被这个 shadow 了,所以当前的环境下,x 永远是 3 *)
val x = 3

数据不可变是函数式编程最主要的特性之一,优点在于去除了不同代码之间的依赖性,不需要像 Java 这类语言一样去考虑List 传址,一个地方代码对值的更改,不会导致另外一个部分的代码出错。

fun append (xs : int list, ys : int list) =
    if null xs
    then ys
    else (hd xs) :: append(tl xs, ys)

val xs = [1, 2]
val ys = [3, 4]
val z = append(xs, ys)
;