导读:面向对象编程和命令式编程仍然是现代软件开发的主要范式,而函数式编程语言在生产代码库中相对较少。随着编程语言扩展对函数式编程方法的支持,以及软件开发新框架的迭代,函数式编程正迅速通过各种不同的途径进入越来越多的代码库。
本文经授权转自公众号CSDN(ID:CSDNnews)
作者:KLINT FINLEY       
译者:弯月,责编:屠敏
Meddbase是一家于2005年创立的医疗保健软件公司,Paul Louth在这家公司内创建了一支出色的开发团队。但随着公司的发展,他们的代码Bug数量也在增加。从某种程度上说,这是必然的结果。功能越多,代码越多;代码越多,Bug就越多。但缺陷率的增长速度超出了Louth的预期。
他表示:
我们看到越来越多相同类型的错误。很明显某个地方出了问题,但我们不清楚究竟是什么。我的直觉告诉我这个问题是由复杂性引起的。
01 函数式编程
他们使用的编程语言是C#,该语言刚刚添加了对语言集成查询(LINQ)的支持。LINQ是一种查询数据源的语言,不仅可以查询数据库,而且还可以查询代码库中的对象。LINQ将函数式编程范式引入了C#,了解LINQ可以帮助你了解更多关于函数式编程的知识。
函数是可在程序中重复完成特定任务的一段代码,可以避免重复编写相同功能的代码,是一种编程标准。例如,你可以编写一个根据圆的直径计算周长的函数,或者编写一个根据某人的出生日期计算星座的函数。
但是,正如Cassidy Williams(Netlify远程开发者体验与教育主管)在ReadME项目的函数式编程指南中写道,函数式编程不仅仅是编写函数,这是一种强调使用“纯函数”编写程序的范例,即无状态的函数,在输入相同的情况下总能返回相同的结果,而且在返回结果时不会产生“副作用”。
换句话说,纯函数不会更改任何现有数据或修改应用程序的其他逻辑。例如,如果计算圆周的函数修改了一个全局变量,那么它就不是纯函数了。
在函数式编程中,数据通常被视为是不可变的。例如,用函数式编写的程序不会修改数组的内容,而是会生成一个修改后的数组副本。与数据结构和逻辑交织在一起的面向对象编程不同,函数式编程强调数据和逻辑的分离。
Remote的开发者体验负责人Williams写道:
在考虑结构良好的软件时,你可能会想到易于编写、易于调试并且包含很多可重复使用功能的软件,而这说的就是函数式编程!
对于需要负责不断增长的大型代码库的团队来说,函数式编程是一个福音。由于编写的代码具有很少副作用,且数据结构不会变化,因此负责代码库某个部分的程序员不太可能破坏另一个程序员正在开发的功能。此外,追踪错误也更加容易,因为代码中可以发生变化的地方很少。
随着Louth对函数式编程的深入研究,他意识到这种方法可以解决大型面向对象代码库所面临的一些问题,就好像他们团队遇到的问题。然而,遇到此类问题的人不止他一个。在过去十年中,随着越来越多的开发人员和组织寻找方法来控制软件的复杂度,渴求构建更安全、更强大的软件,人们对函数式编程的兴趣呈爆炸式增长。
Williams表示:“在真正了解函数式编程后,我就不打算再使用其他范式了。”实际上,他在大学期间使用LISP的衍生编程语言Scheme和Racket 时就爱上了函数式编程。
面向对象编程很不错,至今仍有很多人使用。但很多开发人员在接触函数式编程后,就会成为忠实的粉丝。
当然,面向对象编程和命令式编程仍然是现代软件开发的主要范式,而Haskell和Elm之类的“纯”函数式编程语言在生产代码库中相对较少。但随着编程语言扩展对函数式编程方法的支持,以及软件开发新框架的迭代,函数式编程正迅速通过各种不同的途径进入越来越多的代码库。
02 函数式编程在主流语言中的兴起
函数式编程的起源可以追溯到20世纪50年代后期LISP编程语言的创建。尽管几十年来LISP及其衍生语言(如Common LISP和Scheme)一直有一批忠实的追随者,但到了70~80年代,随着面向对象编程的兴起,函数式编程黯然失色。
到了2010年,随着代码库规模的日渐壮大,很多团队都面临着与Louth开发团队相同的问题,于是人们对函数式编程的兴趣再次高涨。 
Scala和Clojure将函数式编程引入到了Java虚拟机,而F# 将这种范式引入到了.NET。推特将他们的大部分代码迁移到了Scala,而Facebook则选用了Haskell和Erlang等古老的函数式语言来解决特定问题。
与此同时,人们对在JavaScript、Ruby和Python等面向对象语言中应用函数式编程技术的兴趣也在增长。《Elm in Action》一书的作者Richard Feldman表示:“所有这些语言都开始支持方便实现函数式风格的功能,几乎每一种语言支持的范式都在增加。”
2014年,苹果公司推出了Swift,这是一种新的多范式语言,包括对函数式编程的强大支持。该语言的发布证明,函数式编程支持已成为新编程语言的必备特性,就像对面向对象编程的支持一样。Feldman在2019年的一次题为“为什么函数式编程不是主流?”的演讲中说,函数式编程马上就要成为主流了。或者至少是主流的一部分。他说:
有些事情发生了变化,在90年代人们不认为这种风格是积极的,而如今它是积极的想法已经成为一种主流。
对函数式编程的支持级别和性质因语言而异,但有一项特定功能已成为标准。Remote后端工程师Tobias Pfeiffer表示:
可以说,有一项功能已经胜出,那就是高阶函数,即接受或返回另一个函数的函数。你可以通过这种方式创建函数块来优雅地解决问题。
由于对高阶函数的支持,如今开发人员可以将函数式编程引入到自己的代码库,而无需重新构建现有应用程序。当Louth团队决定转而使用函数式编程模型时,他们决定使用F#编写应用程序中的新代码,因为它提供了强大的函数式支持,而且还可以与.NET平台保持兼容。对于已有的C#代码,他们决定使用支持函数式编程的语言,同时还会使用Language-ext(这是Louth自己编写的函数式C#开源框架)。
Louth表示:
我们不想推倒一切重写,因此在现有代码的基础之上添加新功能时,我们会按照函数式编程范式来编写。
对于某些人来说,使用Java、JavaScript或C#等面向对象的语言进行函数式编程感觉就像逆流而上。Arista Networks的工程经理Gabriella Gonzalez说:
语言可以引导你采用某些解决方案或风格。在Haskell中,阻力最小的方法是函数式编程。你可以在Java中进行函数式编程,但肯定不是最顺利的方式。
对于一些混合范式来说,一个更大的问题是,如果代码中包含其他编程风格,那你就无法获得纯函数的保证。Williams表示:
如果你正在编写的代码可能有副作用,那么就不再是函数式了。你或许可以依赖部分代码库。我编写了各种非常模块化的函数,因此没有任何代码会影响到它们。
使用严格的函数式编程语言可以降低在代码中意外引入副作用的可能性。Louth表示:
使用C#之类的语言编写函数式代码的关键是你必须小心,因为你可以走捷径,一旦你的代码不是纯函数式编程,那么就会导致混乱。Haskell会强制你编写函数式的代码,可以保证你的代码不出乱子。
这就是Louth团队在一些新项目中使用Haskell的原因。
但纯粹与否往往是主观感受。Pfeiffer说:
LISP是最早的函数式编程语言,但它具有可变的数据结构。
即使你的语言是纯函数式,而且使用不可变的数据结构,但在接受用户输入的那一刻就有可能引入“杂质”。虽然用户输入可能包含“不纯”的元素,但大多数程序都离不开用户输入,所以纯粹是有限度的。Haskell管理用户输入和其他不可避免的“杂质”的方法是给代码贴上明确的标签,并保证隔离,但不同的语言所划定的界限有所不同。
03 拓展函数式思维
尽管现如今大多数主要编程语言都可以使用函数式编程,但开发人员不一定会利用这些特性。函数式编程的思维方式与命令式或面向对象编程截然不同。Williams表示:
让我大吃一惊的是,你不能在函数式编程中采用传统的数据结构。我不必为一棵树建立一个对象,直接使用树就可以。感觉应该是不行,但确实可行。
JavaScript库React是如今开发人员接触这种新思维方式的主要方式。由于React接受很多可变性,因此最好是把React视为“近似于函数式”,但生态系统确实支持这种方式,而且还鼓励人们将函数式编程作为常见问题的解决方案,但以前人们肯定会寻求其他方式。
ClojureScript的维护者David Nolen在他的ReadME项目故事中写道:
尽管React表面看起来是面向对象,但它率先为UI编程提供了函数式方法。
例如,最近推出的React Hooks 是一组帮助开发人员管理状态和副作用的函数,而Redux则大量采用了函数式方法。
Williams表示:
虽然React不是纯函数式,但它鼓励人们以函数式编程的方式思考。例如,它为前端开发人员引入了map和reduce循环。自此,前端开发人员无需再编写任何for循环。
另一方面,Gonzalez则认为函数式编程通过另一种途径跻身主流之列:特定领域的语言。真正擅长解决某个问题的语言会更加容易被采纳。例如,Nix包管理系统的表达式语言。
它是甚至比Haskell更纯粹的函数式语言,因为使用这种语言编写的代码更加难以附带有副作用或可变性。
Nix的用途很单一,至少不适合构建Web服务器。它的目的只有一个:构建包。但由于它是为这种特定任务而构建的,所以需要构建工具的开发人员都会使用它,即使他们不会尝试使用函数式编程语言。
我认为,随着时间的推移,通用语言会越来越少,而专用语言越来越多,其中许多都是函数式语言。
04 函数式编程的未来
与软件开发领域的许多其他方面一样,函数式编程的未来在很大程度上取决于围绕函数式语言和概念构建的开源社区。
例如,采用纯函数式编程的一个障碍是,面向对象编程或命令式编程的库在数量上拥有绝对优势。从Python的数学与科学计算包到Node.js的Web框架,很多解决常见问题的代码库都不是以函数式风格编写的,但我们无法因此而弃用它们。Louth团队中使用Haskell时就遇到了这样的问题:
Haskell有一个非常成熟的生态系统,但我们的困扰在于无法使用一些非专业的库。
为了克服这个障碍,函数式编程社区必须团结起来,创建新的库,帮助开发人员选择函数式编程。
从language-ext库到Redux,再到Nix,许多人已经在努力中了。
原文链接:
https://github.com/readme/featured/functional-programming
本文转自公众号“CSDN”,ID:CSDNnews
延伸阅读👇
延伸阅读《函数程序设计算法
直播预告👇
据统计,99%的大咖都关注了这个公众号
👇
继续阅读
阅读原文