Como señaló Yannis , hay una serie de factores que han influido en la adopción de funciones de alto orden en idiomas que anteriormente no tenían. Uno de los elementos importantes que solo tocó a la ligera es la proliferación de procesadores multinúcleo y, con eso, el deseo de un procesamiento más paralelo y concurrente.
El estilo de programación funcional de mapa / filtro / reducción es muy amigable con la paralelización, lo que permite al programador utilizar fácilmente múltiples núcleos, sin escribir ningún código de subproceso explícito.
Como señala Giorgio, la programación funcional tiene más que funciones de alto orden. Las funciones, más un patrón de programación de mapa / filtro / reducción, y la inmutabilidad son el núcleo de la programación funcional. En conjunto, estas cosas hacen poderosas herramientas de programación paralela y concurrente. Afortunadamente, muchos lenguajes ya admiten alguna noción de inmutabilidad e, incluso si no lo hacen, los programadores pueden tratar las cosas como inmutables permitiendo que las bibliotecas y el compilador creen y administren operaciones asincrónicas o paralelas.
Agregar funciones de alto orden a un lenguaje es un paso importante para simplificar la programación concurrente.
Actualizar
Agregaré un par de ejemplos más detallados para abordar las preocupaciones que señaló Loki.
Considere el siguiente código C # que atraviesa una colección de widgets, creando una nueva lista de precios de widgets.
List<float> widgetPrices;
float salesTax = RetrieveLocalSalesTax();
foreach( Widget w in widgets ) {
widgetPrices.Add( CalculateWidgetPrice( w, salesTax ) );
}
Para una gran colección de widgets, o un método CalculateWidgetPrice (Widget) computacionalmente intensivo, este bucle no haría un buen uso de los núcleos disponibles. Hacer los cálculos de precios en diferentes núcleos requeriría que el programador cree y administre explícitamente hilos, pasando el trabajo y recopilando los resultados juntos.
Considere una solución una vez que se hayan agregado funciones de orden superior a C #:
var widgetPrices = widgets.Select( w=> CalculateWidgetPrice( w, salesTax ) );
El bucle foreach se ha movido al método Seleccionar, ocultando los detalles de su implementación. Todo lo que le queda al programador es decirle a Select qué función aplicar a cada elemento. Esto permitiría que la implementación de Select ejecute los cálculos en paralelo, manejando todas las preocupaciones de sincronización y gestión de subprocesos sin la participación del programador.
Pero, por supuesto, Select no hace su trabajo en paralelo. Ahí es donde entra en juego la inmutabilidad. La implementación de Select no sabe que la función proporcionada (CalculateWidgets arriba) no tiene efectos secundarios. La función podría cambiar el estado del programa fuera de la vista de Select y su sincronización, rompiendo todo. Por ejemplo, en este caso, el valor de salesTax podría modificarse por error. Los lenguajes funcionales puros proporcionan inmutabilidad, por lo que la función Seleccionar (mapa) puede saber con certeza que ningún estado está cambiando.
C # aborda esto proporcionando PLINQ como una alternativa a Linq. Eso se vería así:
var widgetPrices = widgets.AsParallel().Select(w => CalculateWidgetPrice( w, salesTax) );
Lo que hace un uso completo de todos los núcleos de su sistema sin una gestión explícita de esos núcleos.