Wie Yannis betonte, gibt es eine Reihe von Faktoren, die die Übernahme von Funktionen höherer Ordnung in Sprachen beeinflusst haben, die zuvor ohne waren. Einer der wichtigsten Punkte, auf die er nur kurz eingegangen ist, ist die Verbreitung von Multi-Core-Prozessoren und damit der Wunsch nach einer paralleleren und gleichzeitigeren Verarbeitung.
Der Map / Filter / Reduce-Stil der funktionalen Programmierung ist sehr parallelisierbar, sodass der Programmierer problemlos mehrere Kerne verwenden kann, ohne expliziten Threading-Code schreiben zu müssen.
Wie Giorgio bemerkt, geht es bei der funktionalen Programmierung nicht nur um Funktionen höherer Ordnung. Funktionen sowie ein Map / Filter / Reduce-Programmiermuster und Unveränderlichkeit bilden den Kern der funktionalen Programmierung. Zusammen ergeben diese Dinge leistungsstarke Werkzeuge für die parallele und gleichzeitige Programmierung. Zum Glück unterstützen viele Sprachen bereits den Begriff der Unveränderlichkeit, und selbst wenn dies nicht der Fall ist, können Programmierer die Dinge als unveränderlich behandeln, sodass Bibliotheken und Compiler asynchrone oder parallele Operationen erstellen und verwalten können.
Das Hinzufügen von Funktionen höherer Ordnung zu einer Sprache ist ein wichtiger Schritt, um die gleichzeitige Programmierung zu vereinfachen.
Aktualisieren
Ich werde ein paar detailliertere Beispiele hinzufügen, um auf die Bedenken von Loki einzugehen.
Betrachten Sie den folgenden C # -Code, der eine Sammlung von Widgets durchläuft und eine neue Liste von Widgetpreisen erstellt.
List<float> widgetPrices;
float salesTax = RetrieveLocalSalesTax();
foreach( Widget w in widgets ) {
widgetPrices.Add( CalculateWidgetPrice( w, salesTax ) );
}
Für eine große Sammlung von Widgets oder eine rechenintensive CalculateWidgetPrice (Widget) -Methode würde diese Schleife verfügbare Kerne nicht sinnvoll nutzen. Um die Preisberechnungen für verschiedene Kerne durchführen zu können, muss der Programmierer Threads explizit erstellen und verwalten, die Arbeit weitergeben und die Ergebnisse zusammenfassen.
Betrachten Sie eine Lösung, sobald Funktionen höherer Ordnung zu C # hinzugefügt wurden:
var widgetPrices = widgets.Select( w=> CalculateWidgetPrice( w, salesTax ) );
Die foreach-Schleife wurde in die Select-Methode verschoben, wobei ihre Implementierungsdetails ausgeblendet wurden. Der Programmierer muss nur noch mitteilen, welche Funktion auf die einzelnen Elemente angewendet werden soll. Dies würde es der Select-Implementierung ermöglichen, die Berechnungen parallel auszuführen und alle Synchronisations- und Thread-Management-Probleme ohne Beteiligung des Programmierers zu lösen.
Aber Select funktioniert natürlich nicht parallel. Hier kommt Unveränderlichkeit ins Spiel. Die Implementierung von Select weiß nicht, dass die bereitgestellte Funktion (CalculateWidgets oben) keine Nebenwirkungen hat. Die Funktion kann den Status des Programms außerhalb der Ansicht von Select und seiner Synchronisierung ändern und alles beschädigen. In diesem Fall könnte beispielsweise der Wert von salesTax irrtümlich geändert werden. Reine funktionale Sprachen bieten Unveränderlichkeit, sodass die Select (Map) -Funktion sicher weiß, dass sich kein Zustand ändert.
C # behebt dies, indem es PLINQ als Alternative zu Linq bereitstellt. Das würde so aussehen:
var widgetPrices = widgets.AsParallel().Select(w => CalculateWidgetPrice( w, salesTax) );
Damit können Sie alle Kerne Ihres Systems voll ausnutzen, ohne diese Kerne explizit verwalten zu müssen.