网络埋伏纪事

组合软件:1. 函数式编程的兴衰

网络埋伏纪事 · 2017-04-26翻译 · 866阅读 原文链接

注:这是从头开始学习 JavaScript ES6+ 中函数式编程和组合软件技术的 “组合软件” 系列教程的第一部分。请继续关注。后面会有更多部分出来!

我 6 岁的时候,花了很多时间与最好的哥们一起玩电脑游戏。他家里有一个满是电脑的房间。它们对于我来说是无法抵御的、神奇的。我花了很多时间探索所有游戏。有一天我问我哥们,“我们做一个游戏怎么样?”。

他也不知道怎么做,所以我们就问他老爸。他老爸爬上一个高架子,拿出一本用 Basic 编写的游戏书。于是我们就开始了编程之旅。等到公立学校开始教代数时,我已经对代数掌握的不错了,因为编程基本上都是代数。大致就是这样的。

可组合软件的兴起

在计算机科学的开始,在大多数计算机科学可以实际在计算机上完成之前,有两个伟大的计算机科学家:Alonzo Church(阿伦佐.邱奇)和 Alan Turing(艾兰.图灵)。他们发明了两种不同的,但是等效的通用计算模型。这两种模型都可以计算任何可以被计算的东西(因此称为通用的)。

Alonzo Church 发明了 lambda 演算,这是一种基于函数应用的通用计算模型。而 Alan Turing 以图灵机闻名。图灵机是一种通用的计算模型,它定义了一种通过操作纸带上的符号进行计算的理论设备。

他们在一起协作展示 lambda 演算和图灵机在功能上是等效的。

Lambda 演算都是与函数组合有关。以函数组合来思考是一种相当有表现力和说服力的组合软件的方式。在本文中,我们将讨论函数组合在软件设计中的重要性。

有三个要点让 Lambda 演算变得很特别:

  1. 函数总是匿名的。在 JavaScript 中,const sum = (x, y) => x + y 的右边是匿名函数表达式 (x, y) => x + y
  2. Lambda 演算中的函数只接受一个输入。它们是一元的(unary)。如果需要多个形参,函数会带一个输入,并返回一个带有下一个形参的新函数,依此类推。多元函数 (x, y) => x + y 可以被表示为一个一元函数,比如:x => y => x + y。这种从多元函数到一元函数的转换被称为柯里化(currying)。
  3. 函数是头等的,意思是说函数可以用作为其它函数的输入,以及函数可以返回函数。

这些特性用在一起,形成一种简单,不过依然是富有表现力的词汇,使用函数作为主要的构造单元来组合软件。在 JavaScript 中,匿名函数和柯里化的函数是可选的特性。虽然 JavaScript 支持 lambda 演算的重要特性,但是并不强制使用它们。

传统的函数组合用一个函数的输出作为另一个函数的输入。例如,组合:

f . g

可以被写为:

compose2 = f => g => x => f(g(x))

如下是使用它的方式:

double = n => n * 2
inc = n => n + 1

compose2(double)(inc)(3)

compose2() 函数将 double 函数作为第一个实参,将 inc 函数作为第二个,然后将这两个函数的组合应到到实参 3。再看看 compose2() 的签名,fdouble()ginc()x3。函数调用 compose2(double)(inc)(3) 实际上是 3 个不同的函数调用:

  1. 先传递 double 并返回一个新函数。
  2. 返回的函数以 inc 为参数,并返回一个新函数。
  3. 下一个返回函数以 3 为参数,并求现在变为 double(inc(3))f(g(x)) 的值。
  4. x 求值为 3,并传递到 inc()
  5. inc(3) 求值为 4
  6. double(4) 求值为 8
  7. 8 从函数返回。

当软件被组合时,可以用函数组合图来表示。考虑如下的函数组合:

append = s1 => s2 => s1 + s2
append('Hello, ')('world!')

可以用如下的图形可视化表示它:

Lambda 演算在软件设计上产生了巨大影响,在 1980 年之前,很多有重大影响的计算机科学象征都是用函数组合来构建软件。Lisp 是在 1958 年发明的,是深受 lambda 演算的影响。今天,Lisp 是依然在广泛使用的第二种最古老的语言。

我接触它是通过 AutoLisp,它是最流行的计算机辅助设计(CAD)软件 AutoCAD 的脚本语言。AutoCAD 是如此流行,以至于几乎所有其它 CAD 应用程序都支持 AutoLISP,以相互兼容。Lisp 也是计算机科学课程中的一种热门教学语言,这主要有三个原因:

  1. 其简单性让学习 Lisp 的基础语法和语义变得很容易,大约一天就可以搞定。
  2. Lisp 都是与函数组合有关,而函数组合是一种组织应用程序的优雅方式。
  3. 我知道的使用 Lisp 的最佳计算机科学教材是:计算机程序的构造和解释

可组合软件的衰落

在 1970 到 1980 之间的某个时候,软件创建的方式渐渐远离简单的组合,开始变成一个计算机要听从的线性指令列表。然后面向对象编程出来了,这是一种关于组件封装和消息传递的不错的理念,不过被流行语言扭曲成一种为特性重用而实现的继承层次结构和 "is-a" 关系的糟糕理念。

函数式编程被打入冷宫和学术界,只有最极客的编程爱好者、常春藤大学中的教授,以及一些在二十世纪九十年代到二十一世纪十年代间逃脱 Java 强迫喂养的幸运的学生,还在幸福地痴迷着。

对于我们大多数人来说,这三十年间创建软件是个噩梦。这是黑暗的年代。

可组合软件的兴起

2010 年左右,一些伟大的事情开始发生了:JavaScript 爆发了。大约 2006 年之前,JavaScritp 被广泛当作是一门玩具语言,用来在浏览器中创建卡通动漫,不过它有一些强大的特性被埋没了。那就是,最重要的 lambda 演算特性。人们开始暗中窃窃私语 “函数式编程” 的这种很酷的新东西。

到了 2015 年,用函数组合来创建软件的理念再度流行。为让它更简单,JavaScript 规范做了十年来第一次重大升级,并添加了箭头函数,而箭头函数让创建和读函数、柯里化以及 lambda 表达式变得更简单。

箭头函数像函数式编程的火箭推进剂一样,在 JavaScript 中爆发。现在不大量使用函数式编程技术的大型应用程序已经很罕见了。

组合是一种为软件的行为清晰建模的简单、优雅而富于表现力的方式。组合小的、确定性的函数,来创建更大的软件组件和功能的过程,会生成更容易组织、理解、调试、扩展、测试和维护的软件。

在阅读后续的文章时,请用示例来实验。在学习时,请回忆孩童时代拆掉东西、探索和玩是什么感觉。重拾童年探索的欢乐。让我们来施点魔法吧。

相关文章