martedì 20 agosto 2013

Funzioni "First Class" e "Higher Order" nella Programmazione Funzionale



In un recente post ho parlato della Programmazione Funzionale (FP) come di un paradigma di programmazione per computer che è sostanzialmente esente da side-effect, cioè a dirla in modo un po' brutale: è esente da comportamenti anomali a run-time.


In pratica, un programma scritto in FP pura si comporta come un foglio Excel. Si è mai visto un foglio Excel avere comportamenti anomali? Ci sono dei valori in input, ci sono delle formule e tutto quello che si ottiene sono dei valori di output che derivano dall'applicazione di quelle formule.

In modo simile, un programma FP è una funzione che prende come input una funzione - quindi, si potrebbe dire: una formula - e dà in output un certo risultato. A complicare le cose, c'è il fatto che una funzione FP solitamente prende in input una funzione che a sua volta prende in input una funzione e così via, fin quando ce ne sia bisogno per realizzare lo scopo del programma.

Per indicare questo tipo di comporamento, si dice che un generico linguaggio FP supporta le First Class Functions.

Una funzione, per essere "First Class", deve avere questa caratteristica: può essere passata ad un'altra funzione come qualsiasi altro valore.

Facciamo un esempio. (L'esempio in basso è scritto in linguaggio Scala, che è un linguaggio di programmazione che supporta molto bene il paradigma funzionale).


val areaDelCerchio = (raggio: Double) => { 
    raggio*raggio*Math.PI 
}
    
def printResult(result: Any) {
    println("L'area del cerchio e' : " + result.toString)
}
    
printResult( areaDelCerchio(10.0) )


La formula dell'area del cerchio viene memorizzata come valore (val). Questo basta per far sì che Scala possa dirsi un linguaggio dove le funzioni sono First Class, cioè vengono trattate come valori di prima classe, come lo sono solitamente gli interi o le stringhe. Significa che qualunque funzione può prendere come parametro un valore di prima classe: quindi, tipi elementari, oggetti e funzioni.

La funzione printResult è una funzione che si dice di ordine superiore, Higher Order Function, perché può prendere come parametro un qualsiasi valore e - anche! - una funzione.

Da un punto di vista di controllo dei tipi, la funzione/valore areaDelCerchio prende come parametro un Double (raggio, un valore decimale a doppia precisione) e ritorna un Double.

Qualsiasi oggetto in Scala - e quindi un Double, o una First Class Function - può essere convertito in Stringa. Ed ecco che printResult prende come parametro Any, cioè qualsiasi tipo o funzione, che possa essere rappresentato come stringa - che possegga il metodo .toString.

Poichè la funzione areaDelCerchio non mantiene variabili di stato e non accetta input dall'esterno che potrebbero modificarla, il suo comportamento è predicibile a priori ed esente da side-effect.

Una funzione Higher Order che prende come input tipi semplici o funzioni o funzioni di funzioni, ha un comportamento sempre predicibile e, una volta compilata, sarà esente da bug a run-time. Detto in altri termini: se un programma funzionale puro si compila - quindi non dà errori a compile-time - sarà esente da bug.

La capacità di esprimere un programma per computer, anche complesso, in termini di Higher Order Functions è dunque una garanzia che lo stesso, se si compila, avrà un comportamento predicibile e senza malfunzionamenti.

Se si pensa alle ingenti risorse che vengono impiegate normalmente in informatica per il test e il controllo della qualità del software, le implicazioni economiche di un simile approccio alla programmazione sono del tutto evidenti.