踏出理解函数式编程概念的第一步是最重要的一步,有时也是最难的一步。不过也不一定,取决于你们的思考方式。
现在该做什么?
既然你已经学会了所以的新东西,可能就会想了,“现在该做什么?如何将它用在我每天的编程中使用它?”
这要视情况而定。如果你能使用像 Elm 或 Haskell 这样的纯函数语言,你就可以尝试全部想法。而且在这些语言中实现起来很方便。
如果你只能使用像 JavaScript 这样的指令式语言,而且大多数肯定都是,仍然可以使用很多前面学到的知识,但将需要大量的规则。
函数式JavaScript
JavaScript有很多特性让你用更函数式的方式编程。它不纯净,但可以通过一些库做到不可变数据甚至更多。
尽管不理想,但如果你不得不用它,为什么不使用一些函数式语言的优点呢。
不可变数据
首先思考一下数据不可变性。在ES2015(或者叫ES6)中,新增了关键字const
。意味着一个变量赋值后,不能再被重新赋值:
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)(译者注: 在最新版的Safari中测试同样会抛异常)
JavaScript中的const
的能力还远远不够。下面的例子阐述了这一限制:
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 而不是 JavaScript。
柯里化和函数组合
这系列的前几篇文章中,我们学习了如何编写柯里化的功能。下面这个更复杂的例子:
const f = a => b => c => d => a + b + c + d
注意,我们不得不手工写柯里化的部分。
而且在调用f
时,不得不写:
console.log(f(1)(2)(3)(4)); // prints 10
但这些括号就足够让一个Lisp程序员哭了。
很多库能简化这个过程。我最喜欢的是Ramda。
我们可以用 Ramda 这么写:
const f = R.curry((a, b, c, d) => a + b + c + d);
console.log(f(1, 2, 3, 4)); // prints 10
console.log(f(1, 2)(3, 4)); // also prints 10
console.log(f(1)(2)(3, 4)); // also prints 10
函数定义上并没有什么不同,但是我们不必再每个括号都写了。现在每次调用f
时可以传入同等或部分参数。
通过 Ramda, 。我们可以重写Part 3和Part 4时的mult5AfterAdd10
:
const add = R.curry((x, y) => x + y);
const mult5 = value => value * 5;
const mult5AfterAdd10 = R.compose(mult5, add(10));
其实Ramda中有很多辅助函数来做这些事情。比如R.add
和R.multiply
,意味着我们可以写更少的代码:
const mult5AfterAdd10 = R.compose(R.multiply(5), R.add(10));
Map, Filter 和 Reduce
Ramda也有自己的map
, filter
和reduce
。虽然原生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
需要两个参数。第一个是被除数(将要被除的)第二个是除数(用来除的)。
isOdd
只是一个除二取余的函数。余数是0表示假值,即不是奇数,1表示真值,是奇数。我们翻转了modulo
的第一个和第二个参数,所以可以传入2
作为除数。
isEven
函数只是isOdd
的取反。
onlyOdd
是一个配合isOdd
断言的过滤函数(返回布尔值的函数)。它接收包含数字的数组作为执行前的最后一个参数。
onlyEven
则是使用isEven
进行断言的过滤函数。
当将数字传入onlyEven
和onlyOdd
时,isEven
和isOdd
收到了他们最后的参数,最终会返回我们预期的数字。
JavaScript的缺点
即时现在的JavaScript有很多库,语言也有改善,但它仍然要面对自身是指令式语言的事实,把什么事都交给人来做。
大部分前端开发还在浏览器开发中使用JavaScript,因为很长一段时间里它是唯一的选择。但是现在很多开发者不再直接写JavaScript了。
取而代之,他们用另一种不同的语言编写代码,然后编译,更准确地说,是翻译到JavaScript。
CoffeeScript是第一个这类语言。并且现在,Angular 2已经结合了TypeScript。Babel也可以被认为是JavaScript的转换器。
越来越多的人在生产环境中使用这些技术。
但是这些语言也是基于JavaScript的,而且只是做了一点点改进。为什么不从一个纯函数语言转换到JavaScript呢?
Elm
在这系列文章中,我们已经使用Elm来帮助理解函数式编程了。
但什么是Elm ?我该如何使用它?
Elm是一个编译到JavaScript的纯函数式语言,所以你可以使用Elm构建Web应用,又叫TEA(这个架构给了Redux作者灵感)。
Elm程序中没有运行时错误。
像NoRedInk已经将Elm用于生产环境,Elm的创建者Evan Czapliki现在的公司(他之前在Prezi工作)。
看这个演讲,Elm在生产环境中的六个月,演讲者是来自NoRedInk的Richard Feldman和Elm的布道者。
我需要将全部JavaScript替换成Elm吗?
不用,你可以逐渐地替换。看下这篇文章,How to use Elm at Work了解更多。
为什么学Elm?
- 使用纯函数式语言即受限又自由。限制了你能做的(让你不容易误入歧途)但同时将你从bug和糟糕的设计中解放出来,因为所有的Elm程序都遵循Elm架构,一个函数式响应编程模型。
- 函数式编程将让你成为更好的程序员。这篇文章中的思想只是冰山一角。你真的应该在实际锻炼中去学习它们,那时你会感谢它简化了代码并增强了稳定性。
- JavaScript最开始用了10天就完成了,后来的的二十年不断地为一些功能,一些面向对象思想和完全的指令式编程语言而修补。Elm的设计吸取了Haskell社区30年积累的经验,以及数十年的数学和计算机科学经验。Elm架构(TEA)经过多年的设计和提炼,是Evan在功能响应式编程论文中的结果。看看Controlling Time and Space来理解这个设计思想。
- Elm是为前端开发者设计的。旨在让他们更好地工作。看下Watch Let’s Be Mainstream来理解这一目标。
未来特性
未来怎么样我们无法知晓,但我们可以做一些合理的猜测。下面是我的猜想:
编译到JavaScript的语言将会明确。 沉淀了40年的函数式编程思想将再次被提出用来解决现在软件开发中的复杂问题。 硬件方面,比如千兆字节的便宜内存和快速处理器将带来函数式技术的可能。 CPU的速度不会更快了,但是核数还会继续增加。 可变状态将成为复杂系统中最大的问题。
我写下这系列文章,因为我相信函数式编程是未来,在过去几年中,我在一直努力地学习它(现在仍然在学)。
我的目标是让其他学习这些概念的人更好理解,帮助他们成为更好的开发者,让他们的职业生涯更好。
即使我语言ELm将成为一个伟大的语言是错的,但我能肯定地说无论未来怎样,函数式编程和Elm都具有历史性的意义。
希望阅读完这一系列文章,你能因自身能力和对这些概念的理解获得信心。
在今后的工作中,祝你好运。
本文根据@Charles Scalfani的《So You Want to be a Functional Programmer (Part 6)》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:https://medium.com/@cscalfani/so-you-want-to-be-a-functional-programmer-part-6-db502830403#.sms2qk304。
如需转载,烦请注明出处:http://www.w3cplus.com/javascript/so-you-want-to-be-a-functional-programmer-part-6.html