[译] 准备充分了嘛就想学函数式编程?(Part 6)

2,294 阅读8分钟
原文链接: github.com

准备充分了嘛就想学函数式编程?(Part 6)

第一步,理解函数式编程概念是最重要的一步,同时也是最难的一步。如果你从正确的角度或方法来理解的话,它也未必会有那么难。

回顾之前的部分: Part 1, Part 2, Part 3, Part 4, Part 5

现在该做什么?

现在你已经学会了所有这些新东西了,你可能在想,“现在该干什么?我如何在日常编程中使用它?”

这得看情况。如果你会使用纯函数式语言(如 Elm 或 Haskell)编程,那么你可以尝试所有这些想法。这些语言能够很容易实现这些想法。

如果你只会使用 Javascript 这样的命令式语言编程(我们中大多数人肯定都是),那么你仍然可以使用很多你学到的知识,但是还需要更多的训练。

Javascript 函数式

Javascript 有许多特性能让你以近乎函数式的方式编程。它不是纯粹的函数式,但你可以从语言中得到不变性,甚至更多的库。

它不是最佳的,但如果你必须使用的时候,那为什么不利用一些函数式语言的优点呢?

不变性

首先要考虑的是不变性。 在 ES2015,或者也叫 ES6,因为它有一个被称为 常量 的新关键字。这意味着一旦设置了变量,则无法修改该变量:

const a = 1;
a = 2; // this will throw a TypeError in Chrome, Firefox or Node
       // but not in Safari (circa 10/2016)

这里的 a 被定义为常量,意味着一旦赋值无法再改变。 这就是为什么 a = 2 会抛出异常 (除了 Safari)。

Javascript 常量 有个问题就是不变性不够深入。以下示例说明了其限制:

const a = {
    x: 1,
    y: 2
};
a.x = 2; // NO EXCEPTION!
a = {}; // this will throw a TypeError

注意 a.x = 2 并没有抛出异常。 const 关键字的不变性只对变量 a 生效。 a 所指向的任何变量都可以改变。

这是非常令人失望的,因为它本可以让 Javascript 更好。

那么我们如何从 Javascript 中获得不变性呢?

很不幸,我们只能通过一个库 Immutable.js 来实现。
这可能给我们更好的不变性,但可悲的是,它实现的方式使我们的代码看起来更像 Java。

柯里化和组合

在本系列之前的文章,我们学习了如何编写柯里化的功能。这是一个更复杂的例子:

const f = a => b => c => d => a + b + c + d

请注意,我们不得不手工编写柯里化部分。

调用 f, 我们必须写成:

console.log(f(1)(2)(3)(4)); // prints 10

但是这么多的括号,足以让 Lisp 程序员哭泣了!(译者注:Lisp 语句中会使用很多括号)

有许多库能够简化这一过程。 我最喜欢的一个是 Ramda.

使用 Ramda 我们可以这样写:

const f = R.curry((a, b, c, d) => a + b + c + d);
console.log(f(1, 2, 3, 4)); // 打印 10
console.log(f(1, 2)(3, 4)); // 也打印 10
console.log(f(1)(2)(3, 4)); // 也打印 10

函数定义并没有什么改进,但我们已经消除了对所有括号的需求。请注意,我们可以应用与我们每次调用 f 时一样多的参数。

通过 Ramda, 我们可以重写 Part 3Part 4 mult5AfterAdd10 功能:

const add = R.curry((x, y) => x + y);
const mult5 = value => value * 5;
const mult5AfterAdd10 = R.compose(mult5, add(10));

事实证明,Ramda 有很多帮助函数来做这些事情,例如。R.addR.multiply,这意味着我们可以少写代码:

const mult5AfterAdd10 = R.compose(R.multiply(5), R.add(10));

Map, Filter 和 Reduce

Ramda 也有它自己的 map, filterreduce。 尽管这些功能在普通 Javascript Array.prototype 中已经存在, Ramda 的版本功能更加丰富:

const isOdd = R.flip(R.modulo)(2);
const onlyOdd = R.filter(isOdd);
const isEven = R.complement(isOdd);
const onlyEven = R.filter(isEven);

const numbers = [1, 2, 3, 4, 5, 6, 7, 8];
console.log(onlyEven(numbers)); // prints [2, 4, 6, 8]
console.log(onlyOdd(numbers)); // prints [1, 3, 5, 7]

R.modulo 用了两个参数. 第一个是 dividend (被除数) ,第二个参数是 divisor (除数)。

isOdd 函数只是除以 2 的余数。余数为 0 是 falsy, 不是奇数,余数为 1 则是 truthy,奇数。
我们翻转 modulo 的第一和第二参数,使得我们可以指定 2 作为除数。

isEven 功能只是 isOddcomplement(补集)

onlyOdd 函数是通过 isOdd断言(只返回布尔类型的方法)过滤器 。它在等待 numbers 数组,即它在执行前需要的的最后一个参数。

The onlyEven 是一个使用 isEven 来断言的 过滤器

当我们将 numbers 传给 onlyEvenonlyOddisEvenisOdd 方法,获取它们最终的参数,最后执行然后返回我们期望的结果。

Javascript 缺点

Javascript 已经有很多的库,语言也得到增强,它仍然需要面对残酷的现实,它是一种命令式语言,对大家来说似乎能够做任何事情。

大多数前端人员在浏览器中一直使用着 Javascript ,因为一直以来只有这一种选择。但现在许多开发人员逐渐不再直接编写 Javascript。

取而代之,他们用不同的语言编写和编译,或者更准确的说,是用其他语言转换成 Javascript。

CoffeeScript 就是这些语言中的第一种。如今,Angular 2 中采用了 Typescript。Babel 也是一种 Javascript 转换编译器。

越来越多的人正在采用这种方法用于生产环境。

但是这些语言还是基于 Javascript ,而且只是稍微改进了一点点。为什么不从一个纯函数式语言转换到 Javascript?

Elm

在这个系列里,我们了解了 Elm 来帮助理解函数式编程。

但是什么才是 Elm?我又该怎么用它呢?

Elm 是一种纯函数式编程语言,最终编译成 Javascript ,所以你可以用它来创建 Web 应用,使用 The Elm Architecture,又叫 TEA(这个架构激励了 Redux 的开发者)。

Elm 程序没有任何运行时错误。

NoRedInk 这样的公司已经在生产环境中使用了 Elm,Elm 的创造者 Evan Czapliki 现在工作的公司(他之前在 Prezi 公司工作)。

看看这个访谈,6 个月应用 Elm 在生产环境, 由来自 NoRedInk 的 Richard Feldman 和 Elm 的布道者讲解。

我需要用 Elm 替换我所有的 Javascript 吗?

不,你可以逐渐替换。 完整的看看这篇文章 How to use Elm at Work,来学习更多知识。

为什么学习 Elm?

  1. 函数式编程是限制和自由并存的。它限制了你可以做什么(大部分是保证你不会“误伤”自己),但是同时也让你远离 bug 和错误的设计决策,因为所有的 Elm 程序遵循 Elm Architecture,一个函数式响应编程模型。
  2. 函数式编程能让你成为一个更好的程序员。本文中的想法只是冰山一角。 你真的需要在实践中看到,它们是如何让你的程序缩小尺寸,增加稳定性。
  3. Javascript 最初是在 10 天内构建的,然后在过去的二十年中修补,以成为一种有点功能,有点面向对象和完全命令式的编程语言。
    Elm 的设计吸取了 Haskell 社区过去 30 年工作中的知识,以及数十年的数学和计算机科学经验。
    Elm 架构(TEA)是经过多年设计和完善的,是 Evan 在功能响应式性编程中论文的结果。看看 Controlling Time and Space(控制时间和空间),以了解这个设计的构思。
  4. Elm 专为前端 Web 开发人员而设计。 它的目的是使他们的工作更容易。 观看 Let’s Be Mainstream(让我们成为主流),更好地了解这一目标。

未来

不可能知道将来会怎样,但我们可以做一些猜测。下面是一些我的:

将会出现一个明确的语言,编译为 Javascript。

已经存在了 40 多年的函数式编程思想将被重新发现,以解决当前的软件复杂性问题。

硬件的状态,例如千兆字节的便宜内存和快速处理器,将使函数式技术成为可行。

CPU 不会变得更快,但内核的数量将继续增加。

可变状态将成为复杂系统中的最大问题之一。

我写这系列文章,因为我相信未来是函数式编程的未来,在过去的几年中,我在努力学习它(我还在学习)。

我的目标就是帮助别人比我更容易和更快的去学习这些概念,帮助别人成为更好的程序员,以便他们将来能有更好的就业前景。

即使我的预测,Elm 在未来将是一门伟大的语言是错误的,我可以肯定地说,函数式编程和 Elm 也会在未来的画卷上留下浓墨重彩的一笔。

我希望在阅读完本系列以后,你会对你的能力和这些概念的理解感到更加自信。

在今后的工作中,祝你好运。

如果你想加入一个 web 开发者社区学习以及相互帮助使用 Elm 函数式编程开发 web 应用的话,来加入我的 Facebook Group, Learn Elm Programmingwww.facebook.com/groups/lear…