Como Yannis apontou, há vários fatores que influenciaram a adoção de funções de alta ordem em idiomas que anteriormente não existiam. Um dos itens importantes nos quais ele apenas tocou levemente é a proliferação de processadores com vários núcleos e, com isso, o desejo de um processamento mais paralelo e simultâneo.
O estilo de mapear / filtrar / reduzir a programação funcional é muito amigável à paralelização, permitindo que o programador faça uso fácil de múltiplos núcleos, sem escrever nenhum código explícito de encadeamento.
Como observa Giorgio, há mais na programação funcional do que apenas funções de alta ordem. Funções, além de um mapa / filtro / redução de padrão de programação e imutabilidade são o núcleo da programação funcional. Juntas, essas coisas criam ferramentas poderosas de programação paralela e simultânea. Felizmente, muitas linguagens já suportam alguma noção de imutabilidade e, mesmo que não o façam, os programadores podem tratar as coisas como imutáveis, permitindo que as bibliotecas e o compilador criem e gerenciem operações assíncronas ou paralelas.
Adicionar funções de alta ordem a um idioma é uma etapa importante para simplificar a programação simultânea.
Atualizar
Vou adicionar alguns exemplos mais detalhados para abordar as preocupações que Loki observou.
Considere o seguinte código C # que percorre uma coleção de widgets, criando uma nova lista de preços de widgets.
List<float> widgetPrices;
float salesTax = RetrieveLocalSalesTax();
foreach( Widget w in widgets ) {
widgetPrices.Add( CalculateWidgetPrice( w, salesTax ) );
}
Para uma grande coleção de widgets ou um método CalculateWidgetPrice (Widget) intensivamente computacional, esse loop não faria bom uso de nenhum núcleo disponível. Para fazer os cálculos de preços em diferentes núcleos, é necessário que o programador crie e gerencie explicitamente threads, passando soluções e coletando os resultados juntos.
Considere uma solução depois que funções de ordem superior forem adicionadas ao C #:
var widgetPrices = widgets.Select( w=> CalculateWidgetPrice( w, salesTax ) );
O loop foreach foi movido para o método Select, ocultando seus detalhes de implementação. Tudo o que resta ao programador é dizer ao Select que função aplicar a cada elemento. Isso permitiria que a implementação Select execute os cálculos em paralelo, lidando com todas as preocupações de sincronização e gerenciamento de encadeamentos sem o envolvimento do programador.
Mas, é claro, o Select não faz seu trabalho em paralelo. É aí que entra a imutabilidade. A implementação do Select não sabe que a função fornecida (CalculateWidgets acima) não tem efeitos colaterais. A função pode alterar o estado do programa fora da exibição do Select e sua sincronização, interrompendo tudo. Por exemplo, nesse caso, o valor de salesTax pode ser alterado por engano. Linguagens funcionais puras fornecem imutabilidade, portanto a função Select (map) pode ter certeza de que nenhum estado está mudando.
O C # resolve isso fornecendo o PLINQ como uma alternativa ao Linq. Seria assim:
var widgetPrices = widgets.AsParallel().Select(w => CalculateWidgetPrice( w, salesTax) );
O que faz pleno uso de todos os núcleos do seu sistema sem gerenciamento explícito desses núcleos.