Come ha sottolineato Yannis , ci sono una serie di fattori che hanno influenzato l'adozione di funzioni di alto livello in lingue che prima erano prive. Uno degli elementi importanti su cui ha solo leggermente accennato è la proliferazione di processori multi-core e, con ciò, il desiderio di un'elaborazione più parallela e simultanea.
La mappa / filtro / riduzione dello stile di programmazione funzionale è molto amichevole alla parallelizzazione, consentendo al programmatore di utilizzare facilmente più core, senza scrivere alcun codice di threading esplicito.
Come osserva Giorgio, la programmazione funzionale offre molto di più che funzioni di alto livello. Le funzioni, oltre a una mappa / filtro / riduzione del modello di programmazione e l' immutabilità sono il nucleo della programmazione funzionale. Insieme, queste cose costituiscono potenti strumenti di programmazione parallela e concorrente. Per fortuna, molte lingue supportano già alcune nozioni di immutabilità e, anche se non lo fanno, i programmatori possono considerare le cose come immutabili, consentendo alle librerie e al compilatore di creare e gestire operazioni asincrone o parallele.
L'aggiunta di funzioni di alto livello a una lingua è un passaggio importante per semplificare la programmazione concorrente.
Aggiornare
Aggiungerò un paio di esempi più dettagliati per rispondere alle preoccupazioni che Loki ha notato.
Considera il seguente codice C # che attraversa una raccolta di widget, creando un nuovo elenco di prezzi dei widget.
List<float> widgetPrices;
float salesTax = RetrieveLocalSalesTax();
foreach( Widget w in widgets ) {
widgetPrices.Add( CalculateWidgetPrice( w, salesTax ) );
}
Per una vasta raccolta di widget o un metodo CalculateWidgetPrice (Widget) ad alta intensità computazionale, questo ciclo non farebbe buon uso di tutti i core disponibili. Per eseguire i calcoli dei prezzi su diversi core, il programmatore dovrebbe creare e gestire esplicitamente thread, passando il lavoro e raccogliendo i risultati insieme.
Prendi in considerazione una soluzione una volta aggiunte le funzioni di ordine superiore a C #:
var widgetPrices = widgets.Select( w=> CalculateWidgetPrice( w, salesTax ) );
Il ciclo foreach è stato spostato nel metodo Select, nascondendone i dettagli di implementazione. Tutto ciò che rimane al programmatore è dire Seleziona quale funzione applicare a ciascun elemento. Ciò consentirebbe all'implementazione Select di eseguire i calcoli in parallelo, gestendo tutti i problemi di sincronizzazione e gestione dei thread senza il coinvolgimento del programmatore.
Ma, ovviamente, Select non fa il suo lavoro in parallelo. È qui che entra in gioco l'immutabilità. L'implementazione di Select non sa che la funzione fornita (CalculateWidgets sopra) non ha effetti collaterali. La funzione potrebbe cambiare lo stato del programma al di fuori della vista di Select e della sua sincronizzazione, rompendo tutto. Ad esempio, in questo caso il valore di salesTax potrebbe essere modificato per errore. I linguaggi funzionali puri forniscono immutabilità, quindi la funzione Seleziona (mappa) può sapere con certezza che nessuno stato sta cambiando.
C # risolve questo problema fornendo PLINQ in alternativa a Linq. Sembrerebbe che:
var widgetPrices = widgets.AsParallel().Select(w => CalculateWidgetPrice( w, salesTax) );
Il che fa pieno uso di tutti i core del tuo sistema senza una gestione esplicita di quei core.