Jak zauważył Yannis , istnieje wiele czynników, które wpłynęły na przyjęcie funkcji wyższego rzędu w językach, w których wcześniej nie było. Jednym z ważnych elementów, które dotknął jedynie lekko, jest rozprzestrzenianie się procesorów wielordzeniowych, a wraz z tym pragnienie bardziej równoległego i równoległego przetwarzania.
Styl mapowania / filtrowania / zmniejszania programowania funkcjonalnego jest bardzo przyjazny dla równoległości, umożliwiając programistom łatwe korzystanie z wielu rdzeni bez pisania wyraźnego kodu wątkowego.
Jak zauważa Giorgio, funkcjonalne programowanie to coś więcej niż tylko funkcje wyższego rzędu. Funkcje oraz mapa / filtr / redukcja wzorca programowania i niezmienność są rdzeniem programowania funkcjonalnego. Razem te rzeczy tworzą potężne narzędzia programowania równoległego i współbieżnego. Na szczęście wiele języków obsługuje już pewne pojęcie niezmienności, a nawet jeśli nie, programiści mogą traktować rzeczy jako niezmienne, umożliwiając bibliotekom i kompilatorowi tworzenie i zarządzanie operacjami asynchronicznymi lub równoległymi.
Dodanie funkcji wysokiego rzędu do języka jest ważnym krokiem do uproszczenia programowania współbieżnego.
Aktualizacja
Dodam kilka bardziej szczegółowych przykładów, aby odpowiedzieć na obawy Loki.
Rozważ następujący kod C #, który przegląda kolekcję widżetów, tworząc nową listę cen widżetów.
List<float> widgetPrices;
float salesTax = RetrieveLocalSalesTax();
foreach( Widget w in widgets ) {
widgetPrices.Add( CalculateWidgetPrice( w, salesTax ) );
}
W przypadku dużej kolekcji widżetów lub intensywnej obliczeniowo metody CalculateWidgetPrice (Widget) ta pętla nie wykorzysta dobrze dostępnych rdzeni. Wykonanie obliczeń cen dla różnych rdzeni wymagałoby od programisty jawnego tworzenia wątków i zarządzania nimi, przekazywania pracy i zbierania wyników razem.
Rozważ rozwiązanie po dodaniu funkcji wysokiego rzędu do C #:
var widgetPrices = widgets.Select( w=> CalculateWidgetPrice( w, salesTax ) );
Pętla foreach została przeniesiona do metody Select, ukrywając szczegóły jej implementacji. Jedyne, co pozostaje dla programisty, to powiedzieć Wybierz, jaką funkcję zastosować do każdego elementu. Pozwoliłoby to implementacji Select na uruchamianie obliczeń w trybie parellel, zajmując się wszystkimi problemami z synchronizacją i zarządzaniem wątkami bez udziału programisty.
Ale oczywiście Select nie wykonuje swojej pracy równolegle. Tam właśnie pojawia się niezmienność. Implementacja Select nie wie, że podana funkcja (powyżej CalculateWidgets) nie ma skutków ubocznych. Funkcja może zmienić stan programu poza widokiem Select i jego synchronizacją, psując wszystko. Na przykład w tym przypadku wartość saleTax może zostać zmieniona przez pomyłkę. Czyste języki funkcjonalne zapewniają niezmienność, więc funkcja Wybierz (mapa) może mieć pewność, że żaden stan się nie zmienia.
C # rozwiązuje ten problem, dostarczając PLINQ jako alternatywę dla Linq. To wyglądałoby jak:
var widgetPrices = widgets.AsParallel().Select(w => CalculateWidgetPrice( w, salesTax) );
Który w pełni wykorzystuje wszystkie rdzenie twojego systemu bez wyraźnego zarządzania tymi rdzeniami.