函数式编程终于成为主流
微wx笑 2022-07-14【前路之思】 7 0关键字: 函数式编程
面向对象和命令式编程并没有消失,但函数式编程正在进入更多代码库。
paul Louth在他于 2005 年创立的医疗保健软件公司Meddbase拥有一支出色的开发团队。但随着公司的发展,他们的 bug 数量也在增加。这是意料之中的,在某种程度上。更多的代码和更多的功能意味着更多的缺陷。但缺陷率的增长速度超出了 Louth 的预期。
“我们看到越来越多的相同类型的错误,”劳斯说。“很明显存在问题,但不清楚是什么问题。我的直觉告诉我,问题在于复杂性。”
Louth 和公司使用 C#,它刚刚添加了对语言集成查询 (LINQ) 的支持,这是一种用于查询从数据库到代码库中的对象的数据源的替代语言。LINQ 介绍了将称为“函数式编程”的编程范式应用于 C# 代码的方法,了解 LINQ 的工作原理使他总体上了解了更多关于函数式编程的知识。
函数——可以在程序中一次又一次地完成给定任务的代码块,无需一遍又一遍地重写代码——是编程的标准部分。例如,您可以编写一个在给定直径时返回圆的周长的函数,或者编写一个根据某人的出生日期返回占星符号的函数。但正如 Cassidy Williams 在她的函数式编程指南中所写对于 ReadME 项目,函数式编程不仅仅是编写函数。这是一种强调使用“纯函数”编写程序的范例:无状态函数在给定相同输入时总是返回相同的值,并且在返回其输出时不会产生“副作用”。换句话说,纯函数不会更改任何现有数据或修改应用程序逻辑的其他部分。例如,如果我们的圆周函数改变了一个全局变量,它就不是纯粹的。
在函数式编程中,数据通常被视为不可变的。例如,以函数式风格编写的程序不会修改数组的内容,而是生成一个包含新更改的数组副本。与数据结构和逻辑交织在一起的面向对象编程不同,函数式编程强调数据和逻辑的分离。
“当您考虑结构良好的软件时,您可能会想到易于编写、易于调试并且其中很多可重复使用的软件,”使用功能的公司 Remote 的开发人员体验负责人 Williams 写道。编程语言 Elixir 用于他们的后端软件。“简而言之,这就是函数式编程!”
函数式编程对于使用大型代码库的成长中的团队来说可能是一个福音。通过编写具有很少副作用和不可变数据结构的代码,在代码库的某个部分工作的程序员不太可能破坏另一个程序员正在开发的功能。而且跟踪错误会更容易,因为正如威廉姆斯所写的那样,代码中可以发生更改的地方更少。
随着 Louth 对函数式编程世界的深入研究,他意识到这种方法可以帮助解决大型面向对象代码库所带来的一些问题,例如他的团队在 Meddbase 开始遇到的问题。他并不孤单。在过去十年中,随着越来越多的开发人员和组织寻找方法来控制复杂性并构建更安全、更强大的软件,人们对函数式编程的兴趣呈爆炸式增长。
“一旦函数式编程真正在我的脑海中出现,我就想'为什么要这样做呢?'”威廉姆斯说,他在大学课堂上使用了 LISP 编程语言方言 Scheme 和 Racket 时爱上了函数式编程。“面向对象的编程还不错,它仍然运行在世界的很多地方。但是很多开发人员一旦开始使用函数式编程,就会对它充满信心。”
当然,面向对象和命令式编程仍然是现代软件开发的主要范式,而像 Haskell 和 Elm 这样的“纯”函数式编程语言在生产代码库中相对较少。但是随着编程语言扩大对函数式编程方法的支持,并且新框架采用了软件开发的替代方法,函数式编程正迅速通过各种不同的途径进入越来越多的代码库。
主流语言中函数式编程的兴起
函数式编程的起源可以追溯到 1950 年代后期创建 LISP 的早期编程语言。但是,尽管 LISP 及其方言如 Common LISP 和 Scheme 几十年来一直保持着忠实的追随者,但面向对象编程在 1970 年代和 1980 年代使函数式编程黯然失色。
到 2010 年,随着开发人员发现自己在使用越来越大的代码库,人们对函数式编程的兴趣重新高涨,并且团队面临着与 Louth 和他的开发团队相同的问题。Scala 和 Clojure 将函数式编程引入了 Java 虚拟机,而 F# 将范式引入了 .NET。Twitter将他们的大部分代码迁移到 Scala,Facebook部署了古老的函数式语言,如 Haskell 和 Erlang来解决特定问题。
与此同时,在 JavaScript、Ruby 和 Python 等面向对象语言中应用函数式编程技术的兴趣也在增长。“所有这些语言都越来越多地支持使函数式风格变得更容易的功能,” Elm in Action 的作者 Richard Feldman 说。“几乎每一种语言都越来越多范式。”
2014 年,Apple 推出了 Swift,这是一种新的多范式语言,包括对函数式编程的强大支持。此次发布证明,函数式编程支持已成为新编程语言的必备特性,就像对面向对象编程的支持一样。在 2019 年的一次题为“为什么函数式编程不是常态?”的演讲中 Feldman 总结说,函数式编程确实正在成为常态。或者至少是规范的一部分。“有些事情发生了变化,”他说。“这种风格在 90 年代并不是人们认为是积极的东西,而认为它是积极的想法现在已经成为一种主流。”
对函数式编程的支持级别和性质因语言而异,但一项特定功能已成为标准。“可以说,看似‘赢’的是高阶函数——接受或返回另一个函数的函数,” Remote 的后端工程师Tobias Pfeiffer说。“这让你可以创建函数块来优雅地解决问题。”
对高阶函数的支持使开发人员可以将函数式编程引入他们的代码库,而无需重新构建现有应用程序。当 Louth 和他的团队决定转向函数式编程模型时,他们决定将 F# 用于现有应用程序中的新代码,因为它提供了强大的功能支持,同时保持与 .NET 平台的兼容性。对于现有的 C# 代码,他们使用了该语言自己对函数式编程的支持,以及Language-ext,Louth 自己的函数式 C# 开源框架。
“完全重写不是一种选择,”劳斯说。“因此,当我们向现有代码添加新功能时,我们会按照函数式编程范式编写它。”
对于某些人来说,使用 Java、JavaScript 或 C# 等面向对象的语言进行函数式编程感觉就像逆流而上。Arista Networks 的工程经理Gabriella Gonzalez说:“一种语言可以引导您采用某些解决方案或解决方案风格。” “在 Haskell 中,阻力最小的路径是函数式编程。你可以用 Java 进行函数式编程,但这不是阻力最小的途径。”
对于那些混合范式来说,一个更大的问题是,如果你的代码包含其他编程风格,你不能期望从纯函数中获得相同的保证。“如果你正在编写可能有副作用的代码,它就不再起作用了,”Williams 说。“你或许可以依赖部分代码库。我已经制作了各种非常模块化的功能,因此没有任何东西可以触及它们。”
使用严格的函数式编程语言会更难在代码中意外引入副作用。“用 C# 之类的东西编写函数式编程的关键是你必须小心,因为你可以走捷径,然后你就会遇到完全不使用函数式编程时会遇到的混乱,”劳斯说。“Haskell 强制执行功能风格,它鼓励你第一次就做好。” 这就是 Louth 和他的团队在他们的一些全新项目中使用 Haskell 的原因。
但纯洁往往在旁观者的眼中。“LISP 是最初的函数式编程语言,它具有可变的数据结构,”Pfeiffer 说。即使你的语言是纯函数式的并且使用不可变的数据结构,在你包含用户输入的那一刻就会引入杂质。虽然用户输入被认为是一个“不纯”的元素,但如果没有它,大多数程序都不会很有用,所以纯洁性只能到此为止。Haskell 管理用户输入和其他不可避免的“杂质”,方法是保持它们清晰的标签和离散,但不同的语言有不同的界限。
拓展功能思维
尽管现在大多数主要编程语言都可以使用函数式编程,但开发人员不一定会利用这些功能。函数式编程方法需要一种与命令式或面向对象编程截然不同的编程思维方式。“让我大吃一惊的是,你不能在函数式编程中使用传统的数据结构,”Williams 说。“我不必为一棵树制作一个对象,我就可以做到。感觉它不应该起作用,但它确实起作用。”
JavaScript 库 React 是当今开发人员接触这种新思维方式的最大方式之一。因为 React 允许如此多的可变性,所以最好将其视为“功能相邻”,但生态系统确实支持并促进了对传统上可以通过其他方法解决的常见编程问题的功能解决方案。
“尽管它看起来是面向对象的,但 React 为 UI 编程提供了第一个功能友好的方法,”ClojureScript 的维护者 David Nolen 在他的 The ReadME Project故事中写道,这是一种编译为 JavaScript 的 LISP 方言。
例如,最近推出的 React Hooks 是一组帮助开发人员管理状态和副作用的函数,而 Redux(一个经常与 React 和其他前端库一起使用的用于管理状态的流行库)采用了一种很大程度上是函数式的方法。
“React 鼓励人们以更实用的方式思考,即使它不是纯粹的功能,”Williams 说。“例如,它向前端开发人员介绍了使用 map 和 reduce 函数而不是'for'循环的想法。你现在可以成为一名全职的前端开发人员,并且永远不会编写一个 for 循环。” 查看 Williams 的指南,了解这在 JavaScript 中如何工作的示例。
与此同时,Gonzalez 认为函数式编程通过另一种途径变得更加主流:特定领域的语言。真正擅长一件事通常是更容易采用的途径。例如,Nix包管理系统的表达语言. “它比 Haskell 更纯粹是功能性的,因为它更难编写带有副作用或可变性的东西,”她说。Nix 对于构建 Web 服务器没有用处。它仅用于一个目的:构建包。但是因为它是为特定任务而构建的,所以需要构建工具的开发人员可能会使用它,即使他们不会尝试使用函数式编程语言。“我认为随着时间的推移,通用语言会越来越少,而专业语言会越来越多,”她说。“其中许多都将发挥作用。”
构建函数式编程的未来
与软件开发中的许多其他事情一样,函数式编程的未来很大程度上取决于围绕函数式语言和概念构建的开源社区。例如,纯函数式编程采用的一个障碍就是为面向对象或命令式编程编写的库要多得多。从 Python 的数学和科学计算包到 Node.js 的 Web 框架,对于个人来说,忽略所有这些解决常见问题的现有代码是没有意义的,因为它不是以函数式风格编写的。这是 Louth 有时在使用 Haskell 时遇到的问题。“它有一个相当成熟的生态系统,但它被废弃或不专业的图书馆所困扰,”他说。
为了克服这个障碍,函数式编程社区必须团结起来创建新的库,使开发人员更容易选择函数式编程而不是更容易产生副作用的方法。
从语言扩展库到 Redux 再到 Nix,这项工作已经在进行中。
本文为转载文章,版权归原作者所有,不代表本站立场和观点。