Как указал Яннис , существует ряд факторов, которые повлияли на принятие функций высокого порядка в языках, которые ранее не использовались. Одним из важных моментов, которые он лишь слегка коснулся, является распространение многоядерных процессоров и, следовательно, стремление к более параллельной и параллельной обработке.
Стиль функционального программирования map / filter / Reduce очень удобен для распараллеливания, что позволяет программисту легко использовать несколько ядер без написания какого-либо явного кода многопоточности.
Как отмечает Джорджио, функциональное программирование - это нечто большее, чем просто функции высокого порядка. Функции, а также шаблон программирования карта / фильтр / сокращение и неизменность являются ядром функционального программирования. Вместе эти вещи создают мощные инструменты параллельного и параллельного программирования. К счастью, многие языки уже поддерживают некоторое понятие неизменности, и, даже если они этого не делают, программисты могут воспринимать вещи как неизменяемые, позволяя библиотекам и компилятору создавать и управлять асинхронными или параллельными операциями.
Добавление функций высокого порядка к языку является важным шагом для упрощения параллельного программирования.
Обновить
Я добавлю пару более подробных примеров, чтобы решить проблемы, отмеченные Локи.
Рассмотрим следующий код 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 выполнять вычисления в parellel, обрабатывая все проблемы синхронизации и управления потоками без участия программиста.
Но, конечно же, Select не работает параллельно. Вот тут-то и возникает неизменность. Реализация Select не знает, что предоставленная функция (CalculateWidgets выше) не имеет побочных эффектов. Функция может изменить состояние программы вне представления Select и его синхронизации, нарушая все. Например, в этом случае значение salesTax может быть изменено по ошибке. Чистые функциональные языки обеспечивают неизменность, поэтому функция Select (map) может точно знать, что состояние не меняется.
C # решает эту проблему, предоставляя PLINQ в качестве альтернативы Linq. Это будет выглядеть так:
var widgetPrices = widgets.AsParallel().Select(w => CalculateWidgetPrice( w, salesTax) );
Который в полной мере использует все ядра вашей системы без явного управления этими ядрами.