Comme Yannis l'a souligné, plusieurs facteurs ont influencé l'adoption de fonctions de haut niveau dans des langues qui étaient auparavant dépourvues. L’un des points importants qu’il a abordé à la légère est la prolifération de processeurs multicœurs et, partant, le souhait d’un traitement plus parallèle et simultané.
Le style de programmation fonctionnelle carte / filtre / réduction est très favorable à la parallélisation, permettant au programmeur d'utiliser facilement plusieurs cœurs, sans écrire de code de thread explicite.
Comme le note Giorgio, la programmation fonctionnelle ne se limite pas à des fonctions d'ordre élevé. Les fonctions, plus un schéma de programmation carte / filtre / réduction et l’ immuabilité sont au cœur de la programmation fonctionnelle. Ensemble, ces éléments constituent de puissants outils de programmation parallèle et simultanée. Heureusement, de nombreux langages supportent déjà une certaine notion d'immutabilité et, même s'ils ne le font pas, les programmeurs peuvent traiter les choses comme immuables, permettant ainsi aux bibliothèques et au compilateur de créer et de gérer des opérations asynchrones ou parallèles.
L'ajout de fonctions d'ordre élevé à un langage est une étape importante dans la simplification de la programmation simultanée.
Mise à jour
Je vais ajouter quelques exemples plus détaillés afin de répondre aux préoccupations de Loki.
Considérez le code C # suivant qui traverse une collection de widgets, créant une nouvelle liste de prix de widgets.
List<float> widgetPrices;
float salesTax = RetrieveLocalSalesTax();
foreach( Widget w in widgets ) {
widgetPrices.Add( CalculateWidgetPrice( w, salesTax ) );
}
Pour une grande collection de widgets ou une méthode de calcul à calculer complexe (Widget), cette boucle ne ferait pas bon usage des cœurs disponibles. Pour effectuer les calculs de prix sur différents cœurs, le programmeur devrait explicitement créer et gérer des threads, passer le travail et collecter les résultats.
Envisagez une solution une fois que des fonctions d'ordre élevé ont été ajoutées à C #:
var widgetPrices = widgets.Select( w=> CalculateWidgetPrice( w, salesTax ) );
La boucle foreach a été déplacée dans la méthode Select, masquant ses détails d'implémentation. Le programmeur n'a plus qu'à indiquer à la fonction à appliquer à chaque élément. Cela permettrait à l’implémentation Select d’exécuter les calculs dans Parallel, en gérant toutes les préoccupations de synchronisation et de gestion des threads sans l’implication du programmeur.
Mais, bien sûr, Select ne fait pas son travail en parallèle. C'est là que l'immuabilité entre en jeu. L'implémentation de Select ne sait pas que la fonction fournie (CalculateWidgets ci-dessus) n'a pas d'effets secondaires. La fonction peut changer l'état du programme en dehors de la vue de Select et de sa synchronisation, en cassant tout. Par exemple, dans ce cas, la valeur de salesTax peut être modifiée par erreur. Les langages fonctionnels purs fournissent l’immuabilité, ainsi la fonction Select (map) peut savoir avec certitude qu’aucun état ne change.
C # résout ce problème en proposant PLINQ comme alternative à Linq. Cela ressemblerait à:
var widgetPrices = widgets.AsParallel().Select(w => CalculateWidgetPrice( w, salesTax) );
Ce qui exploite pleinement tous les cœurs de votre système sans une gestion explicite de ces cœurs.