【译】为什么学习 Javascript 函数式编程

这是软件编写系列文章中关于学习函数式编程和使用 ES6 构建软件技术的部分,敬请关注,更多的相关内容
Start over at Part 1 | Next >

忘记你对 Javascript 的任何认知并且用初学者的思想去阅读这篇文章。为了帮助你,我们将从头开始复习 Javascript 基础就像从未见过它一样。假如你是个初学者,那么你是幸运的。所有的新概念都会被解释,但是不要指望得到太多的呵护。

假如你是经验丰富的开发者并且已经熟悉 Javascript 或者其他的纯函数语言,可能你会觉得用 Javascript 去探索函数式编程是一个有趣的选择。把其他的想法都放置一旁并且用开放的思想去体会,你将会发现一种闻所未闻的 Javascript 编程境界。

由于本文被称为 “软件编写”,并且函数式编程是构成软件的明显方式(使用函数组成,高阶函数等等),你可能在想为什么我没有谈论Haskell,ClojureScript 或者 Elm,而是 Javascript。

Javascript 拥有函数式编程所需要的最重要的功能:

1、一等函数:使用函数作为数据的能力:传递函数作为参数,返回函数,分配函数作为变量和对象属性。这个属性可以是部分应用、柯里化和组成的高阶函数。

2、匿名函数和简洁的 lambda 语法x => x * 2 在 Javascriot 中是有效地函数表达式。简洁的 lambda 语法让它作为高阶函数简单地运行。

3、闭包:闭包是一个函数和它的词汇环境形成的捆绑。闭包在函数创建时形成。当函数被定义在另一个函数中时,闭包可以访问在外部函数绑定的变量,即使外部函数已经销毁。闭包就是部分应用如何去获取固定参数,而固定参数是在返回函数中闭包环境中绑定的参数。比如 add(1)(2),1 就是返回函数add(1)中的固定参数。

Javascript 失去了什么

Javascript 是一门多范式语言,意味着它支持多种不同风格的编写代码。Javascript 支持的其他风格有:面向过程编程(比如 C),面向对象编程和函数式编程。多范式的语言优势就是,在面向对象和面向过程中意味着几乎所有事物都是可变的。

突变是发生在数据结构中具体位置上的改变。举个例子:

const foo = {
  bar: 'baz'
};
foo.bar = 'qux'; // mutation

对象是经常需求被改变的,所以它们的属性可以被方法更新。在面向过程编程中,大多数数据结构都是可变的,去实现有效地操作对象和数组。

以下是一些函数式语言拥有而 Javascript 没有的功能。
1、纯度:在一些函数式编程语言中,纯度是被强制执行的,不允许使用有副作用的表达式。

2、不变性:一些函数式编程语言中是禁止改变。而不是去变更现有的数据结构,比如数组、对象或者表达式去创建新的数据结构。这样的做法可能听起来效率低下,但是大多数的函数式语言在结构共享的“罩”中使用 trie 数据结构,旧对象和新对象都共享着相同的数据。

3、递归:递归是函数调用自己去实现迭代的能力。在许多函数式编程语言中,递归是迭代的唯一方式,其中没有循环语法比如:for、while 或者 do。

纯度:在 Javascript中,纯度必须通过管理去实现。假如之前你不同使用纯函数去开发大多数应用的话,那么你就没有使用函数式的编程范式。不幸的是,在 Javascript 中非常容易就偶然创建和使用非纯函数。

不变性:在纯函数语言中,不变性经常被强制执行。Javascript 中缺少在大多数函数式语言中使用的高效、不变的 trie 数据结构,但是可以通过一些 js 库区实现比如:Immutable.js 和 Mori。我希望未来的
ECMAScript 版本可以拥抱不变的数据结构。

其中的一些标志基于了希望,比如在 ES6 中新增的 const 关键字,使用 const 定义的变量不能重新赋值为不同的值,重要的是理解 const 并不意味着一个不变的值。

一个 const 对象不能被重新赋值为完全不同的对象,但是这个对象的属性可以被改变。Javascript 同样拥有能力去freeze()对象,但是对象只能冻结在顶级的属性,这就意味着一个嵌套对象的属性仍然可以发生改变。换句话来说,我们想要看到在 Javascript 规范中真正的综合不变性还需要很长的路要走。

递归:Javascript 支持递归但是大多数函数式编程语言拥有叫做尾部调用优化的功能,就是允许递归函数重用堆栈去实现递归调用。

没有尾部调用优化,调用堆栈将会变得没有限制和导致栈溢出,Javascript 在 ES6 规范中新增了有界形式的尾部调用优化。不幸的是,只有一种主流的浏览器支持它,优化只是部分实现并且在 Babel 中去除掉了(Babel 是当下最流行的 Javascript 标准编译器,使用在旧浏览器中将 ES6 转化为 ES5)。

总结一下:在大的迭代中使用递归是不安全的,即使在尾部谨慎地调用函数。

Javascript 拥有什么特性是纯函数式语言所缺少的

一个纯粹主义者会告诉你 Javascript 的不变性是主要的缺点,但是引起的副作用和突变在某些情况下是有益的。事实上,创建一个有用的现代应用并且没有副作用是不可能的。纯函数式语言比如 Haskell 使用副作用,使用 monads 将有副作用的纯函数伪装成纯函数,尽管使用 monads 包会有不纯的副作用。

monads 的问题是尽管它们使用起来足够简单,但是跟一个不熟悉的人去解释就像是对牛弹琴。

“Monad说白了不过就是自函子范畴上的一个幺半群而已,这有什么难以理解的?” ~James Iry 所引用 Philip Wadler 的话,解释一个 Saunders Mac Lane 说过的名言。“编程语言简要、不完整之黑历史”

是的,这是在调侃这个有趣的点,在之前的引用中,对于 monads 的解释对比最初的版本是进行了简化,原文是这样的:

“X "中的 monad 是其 endofunctor 范畴的幺半群,生成 endofunctor 和被 endofunctor 单位 set 组合所代替的 X
” ~ Saunders Mac Lane。 "Categories for the Working Mathematician"

尽管这样,我还是认为没有必要害怕 monads。最好的学习方式不是去读大量的书或者相关主题的博客文章,而是投入其中并开始使用它们。对于大多是的函数式编程语言来说,不可思议的学识名词比概念更加难以理解,相信我,你不必用过理解 Saunders Mac Lane 来理解函数式编程。

尽管它不是绝对使用于每种编程风格,Javascript 是为了适应不同编程风格和背景的工程师使用而设计出来的。

根据 Brendan Eich 所言,在设计之初,网景有意地去支持两类开发者。

使用 c++ 或者 Java 编写组件的;业余或者职业编写嵌入 html 脚本的。

本来,网景公司有意地去支持两种不同的语言,这门脚本语言可能会类似于 Scheme(Lisp 的一种方言)。 Brendan Eich:

我被招聘到网景公司就是为了在浏览器中去 "实现 Scheme"

Javascript 应该是一门新的语言。

上级工程管理部门的要求是这门语言必须看起来像 Java,然后就排除了 Perl,Python,Tcl 以及 Scheme。

所以,Brendan Eich 的想法是这样的:
1、在浏览器中的 Scheme
2、看起来像 Java

它最终成为了一个大杂烩。

我不觉得骄傲,但是我很高兴我选择了 Scheme 的一等函数和 Self-ish 的原型作为主要的组成部分。由于 Java 的影响,尤其是 y2k 和 Date 的 Bug 以及对象的区别,就感到不幸了。

我列出了“不好的”类 Java 特性,最终实现了 Javascript。
1、构造函数和 new 关键字,与工厂函数不同的调用方法和语义
2、单祖先 class 关键字扩展作为继承的主要机制
3、用户倾向于将 class 看做是它的静态类型(其实不然)

我的建议是:避免使用这些。

值得庆幸的是, Javascript 成为了一门这么棒的语言,因为它证明了脚本方式胜过了建立在组件之上的方式(今天,Java、Flash 和 ActiveX 扩展都不被大多数的浏览器支持)

最终我们实现了一种被浏览器直接支持的语言:Javascript

这意味着浏览器更加轻便和稳定,因为他们只需要支持一种语言:Javascript。你可能会认为 WebAssembly 是一个例外,但是设计 WebAssembly 的其中一个目的就是用兼容的抽象语法树来共享 JavaScript 的语言绑定(AST)。事实上,最早的把 WebAssembly 编译成 JavaScript 的子集的示范是 ASM.js。

作为 web 平台唯一的标准通用语言,在软件历史上掀起了最大的语言热潮:

app 吞噬了世界,web 吞噬了app,Javascript 吞噬了 web。

通过多平台的调查,Javascript 是世界上最流行的编程语言。

但是 Javascript 不是实践函数式编程的理想工具,但是它是为分布式团队去建立大型应用的好工具,因为不同的团建在开发项目上会有不同的想法。

一些团队致力于脚本化,那么命令式编程就会显得特别有用。一些则会专注于抽象架构,其中会保留的一些面向对象的方法也不错。还有一些会拥抱函数式编程,使用纯函数来确保稳定性、可测试性和管理应用状态以便减少用户反馈,团队成员都使用同一种语言,似的他们更好地交换想法,互相学习和协作开发。

在 Javascript 中,所有的这些想法都可以实现,使得更多的人开始拥抱 Javascript,这就诞生了世界上最大的开源包管理工具 (as of February, 2017), npm.

Javascript 真正的优势是生态系统的思想的多样性和用户。它可能不是函数式编程纯粹主义者心中绝对理想的语言,但它是你可以想象的工作在不同平台的人共同合作的理想语言,比如说 Java、Lisp 或者 C。JavaScript 也许并不对有这些背景的用户完全友好,但是这些人很乐意学习这门语言并迅速投入生产。

我同意 Javascript 不是对函数式编程开发者最好的语言。但是,没有任何一门语言可以说它们可以被所有人去使用,正如 ES6 所说那样:Javascript 可以更好地满足函数式开发者的需求,相比于抛弃 JavaScript 和世界上几乎每家公司都使用的令人难以置信的生态系统,为什么不拥抱它,把它变成一个更适合软件组合化的语言?

他们并不是孤独的,Angular,React,Redux 和 Lodash 是 Javascript 生态系统中主要的框架和库,并且对函数式编程产生了重大的影响 -- 在 Lodash 和 redux 中,明确地表示了可以在真实的 Javascript 环境中使用函数式编程模式。

“为什么是 Javascript?” 因为 Javascript 被大多数公司用来开发实际应用。无视是喜欢还是讨厌它,Javascript 已经从 Lisp 手中接手了数十年以来 “最流行的函数式语言” 的名号。事实上,Haskell 更适合当今函数式编程概念的标准,但是人们并不使用它来开发实际应用。

在任何时候,在美国都有近十万的 JavaScript 工作需求,世界其他地方也有数十万的量。学习 Haskell 可以帮助你很好的学习函数式编程,但学习 JavaScript 将会教会你在实际工作中开发应用。

App 正在吞食世界, web 正在吞食 app, 同时 JavaScript 正在吞食 web。

**第三部分: 一个函数式开发者介绍 Javascript **

你可能感兴趣的:(【译】为什么学习 Javascript 函数式编程)