本文作者: Eric Elliott
编译:胡子大哈

翻译原文:http://huziketang.com/blog/posts/detail?postId=58e3062ba58c240ae35bb8de
英文连接:The Rise and Fall and Rise of Functional Programming (Composing Software)

转载请注明出处,保留原文链接以及作者信息

本文是“组合式软件”系列的一篇文章,从头开始学习函数式编程和使用 JavaScript ES6+ 进行软件编程。请继续关注,后面还有很多相关内容。

在我 6 岁的时候,我每天花很多时间和我最好的朋友一起打游戏,他们家里有很多电脑。对我来讲它有种魔幻版不可抗拒的力量。有一天我突然问我朋友:“我们怎样才能自己做一个游戏呢?”

他也不知道,所以我们一起去问他的爸爸,叔叔从很高的架子上拿下来一本书:Basic。我也由此开始了我的编程之旅。后来大学都有代数这门课程,而我对它已经很熟悉了,因为编程里面代数是基础,到处都是代数。

组合式软件的兴起

在计算机科学刚刚起步的时候,很多计算机科学的理论都还没有落地。那时候有两个伟大的计算机科学家:阿隆佐·丘奇和阿兰·图灵。他们创造了两个不同的,但是具有同等效力的通用计算模型。两个模型都可以计算任何可以计算的东西(着重强调,“通用”)。

阿隆佐·丘奇发明了 λ 演算, λ 演算是基于函数应用的通用计算模型。阿兰·图灵则因图灵机而广为人知。图灵机定义了一个理论上的设备,它可以控制条带上的符号。他们合作证明了 λ 演算和图灵机是功能等价的。

λ 演算全部都是关于函数组合。函数组合在软件开发中是非常富有表现力和说服力的。本文中,我们会讨论函数组合在软件设计中的重要性。

这里有三点关于 λ 演算的特殊说明:

  1. 函数通常是匿名的。在 JavaScript 中,const sum = (x, y) => x + y 的右边是匿名函数,即 (x, y) => x + y

  2. λ 演算中的函数只接受单一输入,它是一元的。如果你需要传递多参数,函数会接受第一个输入并且返回一个新的函数来接受第二个参数,以此类推。一个 n 元函数 (x, y) => x + y 可以表达为一个一元函数:x => y => x + y。这种 n 元函数到一元函数的转化叫做柯里化。

  3. 函数是一级的。意思是说一个函数可以作为另一个函数的输入,并且一个函数可以返回另一个函数。

这些特征一起构成了简单且容易理解的规则,在组合式软件中使用函数作为主要编码单元。在 JavaScript 中,匿名函数和柯里化函数都是可选特征,也就是说 JavaScript 支持 λ 演算的主要特征但是并不强制使用。

经典的函数组合是把一个函数的输出作为另一个函数的输入,例如下面的组合:

    f·g

可以写成:

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

下面是如何使用它:

    double = n => n * 2inc = n => n + 1compose2(double)(inc)(3)

compose2() 函数接受 double 函数作为第一个参数,inc 函数作为第二个参数,最后应用参数 3 到这两个函数组合上。再看一下 compose2() 的声明,fdouble()ginc()x3。函数调用 compose2(double)(inc)(3),实际上是三个不同的函数调用:

  1. 首先传递 double 返回一个新函数 1;

  2. 新函数 1 以 inc 为参数并且返回一个新函数 2;

  3. 新函数 2 以 3 为参数并且计算 f(g(x)),即 double(inc(3))

  4. x=3 传递给 inc()

  5. inc(3) 计算结果是 4;

  6. double(4) 计算结果是 8

  7. 最终返回结果是 8

组合式软件的过程可以用函数组合图来表达,看下面代码:

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

可以用图来模拟表示:

λ 演算对软件设计的影响是深远的,直到大约 1980 年,计算机科学界很多有影响力的品牌,都是采用函数组合的方式来开发自己的软件。Lisp 是 1958 年发明的,它深受 λ 演算的影响。直到今天,Lisp 是依旧广为使用的第二大历史悠久的语言。

我是通过 AutoLISP 知道的 Lisp,AutoLISP 是在最流行的电脑辅助设计(CAD)软件——AutoCAD,中使用的脚本语言。AutoCAD 太流行了,使得其他所有的 CAD 应用几乎都支持 AutoLISP 以保持其兼容性。Lisp 依然能够在计算机科学课程中广为使用有三个主要原因:

  1. Lisp 非常简单,基本上可以在一天之内学习完它的基本语法和语义;

  2. Lisp 基本上全部是函数组合,函数组合来做应用架构的方式非常优雅;

  3. 我所知道的最好的计算机科学课本使用Lisp:计算机程序的结构和解释

组合式软件的没落

在 1970 到 1980 年期间,软件开发的方式开始发生变化,简单的组合式开发不再受宠。出现了面向对象编程,它基于组件封装和信息传递的思想,在当时是非常先进的。代码通过继承来实现复用,继承关系是一种叫做 is-a 的关系。

函数式编程逐渐被边缘化,被抛弃到学术界和非主流的场外。在 1990 — 2010 年期间对三种人形成了甜蜜的困扰,一种是编程极客,一种是大学教授,一种是逃离了 Java 思想强制灌输的幸运的学生。而对于我们来讲,这 30 年的软件开发有一点噩梦般的感觉,黑暗的年代。

组合式编程的重新崛起

2010 年左右,有个巨大的变化:JavaScript 爆发了。在 2006 年以前,JavaScript 一直被认为是一种玩具式的编程语言,可以在浏览器中做一些很可爱的动画,但是在这背后隐藏着潜力巨大的特点,即 λ 演算的重要特征。人们开始逐渐在私下里谈论“函数式编程”。

到 2015 年,用函数组合来开发软件重新开始流行起来。为了简化使用,JavaScript 规范也做了 10 年以来的首次重大升级,增加了箭头函数。箭头函数使创建和使用函数、柯里化和 λ 演算变得很容易。

箭头函数对于 JavaScript 函数式编程的爆发起到了推动剂的作用。现在很少看到那种不用函数式编程的大型应用了。

组合的方式可以简洁清晰地描述软件的行为,把一些小的、确定性的函数组合成大的组件,进而形成软件,这样的软件很容易组织、理解、调试、扩展、测试和维护。

在读接下来文章的时候,希望你能通过例子自己动手做实验。回想一下当自己还是的孩子的时候,把一些东西拆开,自己再学着组装、拼接。重新找回童年探索事物的感觉,希望你能享受这个过程。

如果本文对你有帮助,欢迎关注我的专栏-前端大哈,定期发布高质量前端文章。


我最近正在写一本《React.js 小书》,对 React.js 感兴趣的童鞋,欢迎指点。

函数式编程的兴衰与当前之崛起相关推荐

  1. 从JS对象开始,谈一谈“不可变数据”和函数式编程

    文章转载自:https://segmentfault.com/a/1190000008780076 作为前端开发者,你会感受到JS中对象(Object)这个概念的强大.我们说"JS中一切皆对 ...

  2. Scala - 快速学习08 - 函数式编程:高阶函数

    函数式编程的崛起 函数式编程中的"值不可变性"避免了对公共的可变状态进行同步访问控制的复杂问题,能够较好满足分布式并行编程的需求,适应大数据时代的到来. 函数是第一等公民 可以作为 ...

  3. 一篇文章看懂函数式编程与命令式编程

    文章目录 1 历史来源 2 编程范式 3 函数式编程的崛起 4 函数式编程 4.1 函数 4.2 纯函数 4.3 变量与表达式 4.5 函数与方法 4.6 状态 4.7 函数式编程的特性 4.7.1 ...

  4. Scala 编程基础 C(函数式编程)

    文章目录 1. 函数定义和高阶函数 2. 针对集合的操作 3. 函数式编程实例:WordCount 1. 函数定义和高阶函数 Scala是一门多范式编程语言,混合了面向对象编程和函数式编程的风格.在过 ...

  5. 深入理解函数式编程(上)

    总第539篇 2022年 第056篇 函数式编程是一种历史悠久的编程范式.作为演算法,它的历史可以追溯到现代计算机诞生之前的λ演算,本文希望带大家快速了解函数式编程的历史.基础技术.重要特性和实践法则 ...

  6. 2021年大数据常用语言Scala(二十一):函数式编程 遍历 foreach

    目录 遍历  foreach 使用类型推断简化函数定义 使用下划线来简化函数定义 遍历  foreach 之前,学习过了使用for表达式来遍历集合.我们接下来将学习scala的函数式编程,使用fore ...

  7. 2021年大数据常用语言Scala(二十):函数式编程 介绍

    目录 函数式编程 介绍 函数式编程的意义在哪? 函数式编程 介绍 我们将来使用Spark/Flink的大量业务代码都会使用到函数式编程.下面的这些操作是学习的重点. 现在我们将会逐渐接触函数式编程的方 ...

  8. Scala函数式编程(三) scala集合和函数

    前情提要: scala函数式编程(二) scala基础语法介绍 scala函数式编程(二) scala基础语法介绍 前面已经稍微介绍了scala的常用语法以及面向对象的一些简要知识,这次是补充上一章的 ...

  9. scala函数式编程(二) scala基础语法介绍

    上次我们介绍了函数式编程的好处,并使用scala写了一个小小的例子帮助大家理解,从这里开始我将真正开始介绍scala编程的一些内容. 这里会先重点介绍scala的一些语法.当然,这里是假设你有一些ja ...

最新文章

  1. tar xvf实现的是什么功能呢?
  2. 使用jmeter测试java程序
  3. 思科光传输功率查询_各品牌网络设备的光功率查看方法(不完全统计)
  4. Starting httpd: httpd: Could not reliably determine the server's fully qualified domain name 解决
  5. oracle服务未启动失败,windows服务未启动导致 ORA-12560和RMAN-00554错误 | 信春哥,系统稳,闭眼上线不回滚!...
  6. 组件注入 # 注入的属性_注入域对象而不是基础结构组件
  7. navigator 携带参数_福州振动时效参数视频,振动时效设备用途
  8. linux的启动流程和加载程序
  9. 【面经】各大AI研究院共35场NLP算法岗面经奉上
  10. 在计算机领域提到的假说,量子力学中假说的发展及相关影响
  11. 基于AT89C51的多层电梯控制系统
  12. K9G8G08U0A升级到K9GAG08U0D烧录NK要注意的地方
  13. 上行PHR余量提升优化思路
  14. 例题(15.6) 细菌实验分组
  15. 遥感图像预处理与土地利用动态监测
  16. 微博情感分析的表情符号平滑语言模型(A11, AAAI2012)
  17. PHP有哪些优势和劣势
  18. wps云文档 wps自动备份怎么设置和取消
  19. 微信公众号发送消息通知
  20. 【计算几何】大自然的数学模型--分形几何

热门文章

  1. 笔记本电脑没有鼠标怎么右键_联想笔记本电脑没有声音怎么修复
  2. [BZOJ2017][Usaco2009 Nov]硬币游戏
  3. 自我总结和学习表单提交的几种方式 (一)
  4. SQL联合查询:子表任一记录与主表联合查询
  5. 【VS开发】动态创建ActiveX控件
  6. Mysql -- SQL常用命令实例
  7. ASP网站实现防止被采集
  8. qt中判断对象是否为空的方式
  9. vs中寄存器调试窗口可看出程序是多少位运行的及cpu寄存器使用情况
  10. C++中this指针的用法详解