轻量函数式 JavaScript:一、为什么要进行函数式编程?

简介:

函数式程序员:(名词)那些将变量命名为“x”,函数命名为“f”,并将代码模式称为“zygohistomorphic prepromorphism”的程序员。

James Iry ‏@jamesiry 5/13/15

https://twitter.com/jamesiry/status/598547781515485184

函数式编程(FP)无论如何都不是一个新概念。它在编程的整个历史中一直存在着。然而可以确定的是 —— 我不确信这样说是否合理,但是!—— 也许直到最近这几年它才被整个开发者世界看作是一个主流概念。我认为 FP 曾经在学术圈里存在的时间要更长。

但一切都变了。关于 FP 的呼声越来越高,不仅是语言的层面,在库与框架的层面也是如此。很有可能,你就是终于意识到 FP 是一个你无法再忽略的东西才在读这本书的。或者你可能像我一样,曾经多次试图学习 FP 但是苦于在术语和数学符号之中挣扎。

无论你是因为什么才来读这本书,欢迎参加这个派对!

自信

作为一个(使用 JavaScript)软件开发的教师,我做每一件事都有一个非常简单的前提作为某种基础:你不理解的代码是不能信任的代码。进一步讲,如果你不能信任或理解你自己的代码,那么对于你写的代码能够完成目标任务这件事来说,你就没有任何自信可言。

我说的信任是什么意思?我的意思是你可以通过阅读一段代码,不是仅靠运行它,来验证这段代码 将会 做什么,而非仅依赖于它 应当 做什么。也许更经常是,我们倾向于通过运行测试套件来验证我们程序的正确性。我不是说测试不好。但我确实认为我们应当有这样的追求:能够对我们的代码有足够的理解,以至于在测试套件运行前我们就知道它能够通过。

仅仅通过阅读我们的程序就能对它建立起强大的自信 —— 我相信构成 FP 基础的那些技术就是根据这样的思维模式设计而来的。理解 FP 的人,以及在他们的程序中孜孜不倦地使用 FP 的人,通过那些业已被证明为真的原理,将会编写出他们能够阅读并验证的代码,他们的程序将会做他们想做的事。

我希望你将开始在你编写的代码中发展出更多自信,而这些函数式编程的原理将在这个方向上开启你的旅程。

交流

函数式编程为什么很重要?要回答这个问题,我们得退后一大步来谈谈程序本身为什么重要。

听到这些你可能很吃惊,但我不认为代码主要是计算机的一套指令。事实上,我认为代码能够驱动计算机几乎是一个欢乐的巧合。

我对此深信不疑 —— 代码在人与人之间的交流中扮演的角色要重要得多的多。

你可能有这样的经验,你在“编码”上花费的大把的时间其实只是在阅读既存的代码。我们之中很少有人有特权能够把全部的或大部分时间都用来敲新代码,而从来不用对付其他人(或者我们过去自己)写过的代码。

你知道吗?研究这个课题的研究员声称我们花在维护代码上的时间有70%都是仅仅用来阅读并理解它的。我很震惊。70%。难怪全球程序员每天的平均代码量只有5行。我们一天花费7小时30分钟只是阅读代码,来搞清楚那5行代码应该放在什么地方!

我认为我们应当更多 —— 更多!—— 地关注代码的可读性。可能,再多一些。顺带一提的是,可读性不是减少字母数量。可读性实际上是受熟悉程度影响的(对,这也是被研究过的)。

那么,如果我们将要花更多的时间来关心如何使我们的代码变得更易读和易理解,在这方面上 FP 被证实是一个非常方便的模式。FP 的各种原理十分成熟,经过深刻的研究和检验,而且是可以通过证明来验证的。

如果使用 FP 的原理,我相信我们可以编写出更容易推理的代码。一旦我们知道了这些原理,它们就可以在代码中被辨识而且为我们所熟知,这意味着在我们阅读一段代码时将花费更少的时间来搞清楚那一部分。我们的注意力将集中在所有那些众所周知的、已经构建好的乐高积木块儿是如何组合在一起的,而非这些积木块儿意味着什么。

FP(至少,刨去所有使它魅力骤减的术语)是编写高可读性代码的最有效的工具之一。这就是为什么它很重要。

可读性曲线

让我花点时间回忆一下一种现象十分重要,几年间它曾让我崩溃和沮丧了许多次,而且在写作这本书时显得尤其尖锐。

我还认为它可能是许多开发者都很容易陷进去的东西。你,亲爱的读者,在读这本书时可能正好发现自己就在这条船上。振作起来,如果拟减持下去,曲线就会降下来。

fig17

我们会在下一章中更多地探讨这个话题,但你可能已经写过指令式代码,比如 if 语句和 for 循环。它关注于准确地指示计算机 如何 做一些事。声明式代码,以及你将努力学习以贯彻 FP 原理的其他种类的代码,更关注于描述结果是 什么

让我透露给你一个痛苦的事实,在写这本书时我一直试着吞掉它:为了改进你代码的可读性,以及为了减少或消灭你可能写出 bug 的地方,要做出非常多的努力,而且通常会导致多得多的代码。

如果你希望 FP 代码重构能立即使你的代码变得平静、优雅、聪明、和简洁,那这不是一个现实的期待 —— 至少一开始不是。

FP 是一种非常不同的思考方法 —— 代码应当如何组织,以使数据的流动更明显并帮助你的读者跟上你的思路。这种努力相当有价值,但它可能是一场艰难的旅行,而且在你花了很多时间来训练自己适应 FP 之前你得到的代码不会变得看起来更易读。

另外,就我的经验而言,将一段指令式代码转换为声明式的 FP 大概要尝试六次左右,才能得到对于我的理解来说足够清晰的东西。对我自己来说,与将一段二进制码从一种范式反转为另一种相比,编写 FP 更像一种处理。

我还对我编写的每一段 FP 代码进行了“稍后再读”测试。我先编写它,然后把这段代码放几个小时或一天,然后回过头来用陌生的眼睛再读一次。通常它都糊涂得像鬼一样,于是我就反复调整它。

对我来说函数式编程不是艺术家的画布上令观众赞叹的点睛之笔。相反,它是一种刻苦、仔细、有时狡猾的技术,在那些被忽视的领域的灌木丛中披荆斩棘。

但我是不在给你泼冷水。我真的希望你去在那些被忽视的领域的灌木丛中披荆斩棘。我很高兴我这么做过。我终于能看到曲线向着更高的可读性倾斜。努力是值得的。我相信这对你来说也一样。

视角

我们将从最基本的东西开始迈向 FP,揭示一些基本的基础原理 —— 我相信真正的 FP 程序员都会承认这些原理是他们做一切事情时的脚手架。但是大部分情况下,我们将与大多数吓人的学术用语或数学符号保持一定距离,它们太容易使初学者感到沮丧了。

我认为你如何称呼一个东西并不那么重要,更重要的是你理解它是什么以及它如何工作。这不是说在学术用语上的共识不重要 —— 它毋庸置疑地在老练的专业人士之间简化了交流。但是对于初学者,我发现它可能有些分散人的注意力。

所以我希望这本书能够更多地关注于基本概念,而少一些花哨的专业术语。这不是说这里将不会有任何术语;绝对会有的。但不要被这些花哨的词儿绊住。越过它们去看背后的思想。那才是这本书想要阐明的。

我称这种不那么正式的做法为“轻量函数式编程”,因为我觉得真正的 FP 的形式主义所遭受的问题是,在你还没习惯正式的想法之前它可能让人感到无所适从。我不是在猜测;这就是我自己的故事。即便是在教授了 FP,写了这本书之后,对我来说 FP 中用语和符号的形式主义依然非常、非常难处理。我曾经试了又试,但看起来没获得太多进展。

我知道许多 FP 程序员相信形式主义本身有助于学习。但是我认为在你对这种形式主义有相当程度的适应之前,还是存在一个很明显的断崖。如果你碰巧拥有数学方面的背景,或者甚至有一些计算机科学相关的经验,这对于你来说就显得更自然。但是我们之中的许多人没有,而且无论我们如何努力尝试,形式主义依然是个拦路虎。

所以这本书介绍我认为的 FP 建立的基础概念,它的目的是帮你加速爬上这个断崖,而非直接把你扔在它面前自己去摸爬滚打。

YAGNI

如果你在编程业界待过很长时间,那么你以前很可能听说过“YAGNI”这句话:“You Ain't Gonna Need It(你不会需要它)”。这个原则主要是从极限编程中演化出来的,强调一个特性在被需要之前就建造的风险与成本。

有时候我们会猜测我们在未来会需要一个特性,并且相信在我们建造其他东西的同时现在就建造它将会更容易,然后发觉我们猜错了,这个特性不需要,或者与我们猜测的十分不同。其他一些时候我们猜对了,但是一个特性建造的太早了,它从现在真正需要的特性那里榨取了时间;我们引入了一个机会成本来稀释我们的精力。

YAGNI 挑战我们这样的想法,以让我们记住:即便在某种情况下与直觉相悖,我们通常应当推迟建造某些东西,直到它当前就是被需要的。对于将一个特性在稍后被需要的时候再加入它这件事,我们倾向于夸大估计未来的重构成本。但很可能的是,稍后再做并不会像我们想象的那么难。

至于把它施用在函数式编程上,我会给出这样的劝诫:在这本书中会讨论相当多的有趣和迷人的模式,但是仅仅因为你发现某些模式用起来激动人心,并不意味着在你代码的特定部分中使用它们是恰当的。

这就是我与许多更正规的 FP 人士之间的不同之处:仅仅因为你 FP 某些东西,不意味着你 应当 FP 它。另外,有许多方法划分一个问题,而且就算你学过一个更精巧的方式,对可维护性与扩展性更加“适应未来”,但这一点上一个更简单的 FP 模式可能就绰绰有余了。

一般说来,我推荐在你的代码中寻找平衡,并在你熟练掌握 FP 的概念时保守地实施它。在决定某种模式或抽象是否能够提高一部分代码的可读性,还是它只是引入了更多(还)不需要的聪明的复杂性时,默认选择 YANGI 原则。

记住,任何永远用不到的扩展点都不只是白费力气,它还很可能成为你的障碍

Jeremy D. Miller @jeremydmiller 2/20/15

https://twitter.com/jeremydmiller/status/568797862441586688

记住,你写的每一行代码都相应的读者成本。这个读者可能是另一个团队的成员,或者是未来的你自己。他们谁也不会对那些用来炫耀你 FP 灵活性的、聪明过头的、没必要的复杂性感到印象深刻。

最好的代码是在未来最易读的代码,因为它在自己能/应当做什么(理想主义)与自己必须做什么(实用主义)之间,达到了恰到好处的平衡。

资源

为了创作这本书我借鉴了大量不同的资源。我相信你同样会从中受益,所以我想花一点儿时间将它们指出来。

书籍

一些你绝对应该读的 FP/JavaScript 书籍:

博客/网站

一些你应该看看的作者与内容:

这本书中的示例代码段没有使用库。我们探索的每一种操作,我们都将推导如何使用独立、普通的 JavaScript 来实现它。然而,在你开始在更多真正的代码上利用 FP 时,你将很快希望能有一个库来提供这些常用工具的优化过的、高度可靠的版本。

顺带一提,你将想要确信自己看过那些你使用的库函数的文档,保证你自己知道它们是如何工作的。在它们与我们这本书中建造的代码之间将会存在许多相似之处,但也毫无疑问地存在一些不同,即使是在流行库之间也是这样。

这里几个 JavaScript 中流行的 FP 库,它们是你开始探索的很好的起点:

附录C中使用这本书中的几个例子来展示了这些库中的一些。

总结

这是轻量函数式 JavaScript。它的目标是在不受制于大量的符号与术语的同时,学习与我们的代码交流。我希望这本书是你旅途的开端!

目录
相关文章
|
前端开发 JavaScript 数据处理
深入学习JavaScript ES8函数式编程:特性与实践指南
深入学习JavaScript ES8函数式编程:特性与实践指南
79 0
|
4月前
|
存储 JavaScript 前端开发
JavaScript——函数式编程Functor(函子)
JavaScript——函数式编程Functor(函子)
26 0
|
6月前
|
前端开发 JavaScript 开发者
函数式编程在JavaScript中的应用
【6月更文挑战第10天】本文探讨了函数式编程在JavaScript中的应用,介绍了函数式编程的基本概念,如纯函数和不可变数据。文中通过示例展示了高阶函数、不可变数据的使用,以及如何编写纯函数。此外,还讨论了函数组合和柯里化技术,它们能提升代码的灵活性和可重用性。掌握这些函数式编程技术能帮助开发者编写更简洁、可预测的JavaScript代码。
|
7月前
|
JavaScript 前端开发
JavaScript 的数组方法 map()、filter() 和 reduce() 提供了函数式编程处理元素的方式
【5月更文挑战第11天】JavaScript 的数组方法 map()、filter() 和 reduce() 提供了函数式编程处理元素的方式。map() 用于创建新数组,其中元素是原数组元素经过指定函数转换后的结果;filter() 则筛选出通过特定条件的元素生成新数组;reduce() 将数组元素累计为单一值。这三个方法使代码更简洁易读,例如:map() 可用于数组元素乘以 2,filter() 用于选取偶数,reduce() 计算数组元素之和。
47 2
|
7月前
|
JavaScript 前端开发 测试技术
JavaScript中的函数式编程:纯函数与高阶函数的概念解析
【4月更文挑战第22天】了解JavaScript中的函数式编程,关键在于纯函数和高阶函数。纯函数有确定输出和无副作用,利于预测、测试和维护。例如,`add(a, b)`函数即为纯函数。高阶函数接受或返回函数,用于抽象、复用和组合,如`map`、`filter`。函数式编程能提升代码可读性、可维护性和测试性,帮助构建高效应用。
|
7月前
|
JavaScript 前端开发 索引
JavaScript函数式编程【进阶】
JavaScript函数式编程【进阶】
58 1
|
7月前
|
存储 JavaScript 前端开发
JavaScript函数式编程[入门]
JavaScript函数式编程[入门]
55 1
|
7月前
|
前端开发 JavaScript 数据处理
深入学习JavaScript ES8函数式编程:特性与实践指南
深入学习JavaScript ES8函数式编程:特性与实践指南
118 0
|
缓存 JavaScript 前端开发
带你读《现代Javascript高级教程》十四、JavaScript函数式编程(1)
带你读《现代Javascript高级教程》十四、JavaScript函数式编程(1)
|
JavaScript 前端开发 测试技术
带你读《现代Javascript高级教程》十四、JavaScript函数式编程(2)
带你读《现代Javascript高级教程》十四、JavaScript函数式编程(2)