正如Yannis所指出的,有许多因素影响了以前没有的语言对高阶函数的采用。他仅轻描淡写的重要项目之一就是多核处理器的激增,以及对更多并行和并发处理的渴望。
函数式编程的map / filter / reduce样式对并行化非常友好,从而使程序员可以轻松使用多个内核,而无需编写任何显式的线程代码。
正如Giorgio所指出的,函数式编程不仅仅是高级函数。函数,加上映射/过滤/减少编程模式,以及不变性是函数编程的核心。这些东西共同构成了强大的并行和并发编程工具。值得庆幸的是,许多语言已经支持某种不变性的概念,即使不支持,程序员也可以将它们视为不变性,从而允许库和编译器创建和管理异步或并行操作。
向语言添加高阶函数是简化并发编程的重要步骤。
更新资料
我将添加一些更详细的示例,以解决Loki指出的问题。
考虑下面的C#代码,该代码遍历小部件的集合,从而创建新的小部件价格列表。
List<float> widgetPrices;
float salesTax = RetrieveLocalSalesTax();
foreach( Widget w in widgets ) {
widgetPrices.Add( CalculateWidgetPrice( w, salesTax ) );
}
对于大量的小部件或计算量大的CalculateWidgetPrice(Widget)方法,此循环将无法充分利用任何可用的内核。要在不同内核上进行价格计算,将需要程序员显式创建和管理线程,传递工作并一起收集结果。
一旦将高阶函数添加到C#中,请考虑一个解决方案:
var widgetPrices = widgets.Select( w=> CalculateWidgetPrice( w, salesTax ) );
foreach循环已移到Select方法中,隐藏其实现详细信息。程序员剩下的就是告诉选择要应用于每个元素的功能。这将允许Select实现以并行方式运行计算,无需程序员参与即可处理所有同步和线程管理问题。
但是,当然,Select不能并行运行。这就是不可变性的地方。Select的实现不知道所提供的函数(上面的CalculateWidgets)没有副作用。该功能可能会在Select及其同步范围之外更改程序的状态,从而破坏所有功能。例如,在这种情况下,salesTax的值可能会错误地更改。纯功能语言提供了不变性,因此Select(映射)功能可以确定没有状态改变。
C#通过提供PLINQ替代Linq来解决此问题。看起来像:
var widgetPrices = widgets.AsParallel().Select(w => CalculateWidgetPrice( w, salesTax) );
这将充分利用系统的所有核心,而无需明确管理这些核心。