Che cos’è una funzione pura?
Vantaggi
L’utilizzo di funzioni pure offre diversi vantaggi, sia in termini di prestazioni che di usabilità.
Leggibilità
Le funzioni pure sono molto più facili da leggere e ragionare. Tutti gli input e le dipendenze rilevanti sono forniti come parametri, quindi non si osservano effetti che alterano le variabili al di fuori dell’insieme di input.
Ciò significa che possiamo capire rapidamente una funzione e le sue dipendenze, semplicemente leggendo la dichiarazione della funzione. Quindi, se una funzione è dichiarata come f(a, b, c)
allora sappiamo che solo a
b
, e c
sono dipendenze di f
.
Portabilità
Poiché tutte le dipendenze sono fornite come parametri di input e non sono accessibili attraverso un contesto globale, queste dipendenze possono essere scambiate a seconda del contesto in cui viene chiamata la funzione.
Ciò significa che la stessa funzione può agire su diverse implementazioni della stessa risorsa, ad esempio.
Questo rende il codice molto più portabile e riutilizzabile in quanto la stessa funzione può essere utilizzata in vari contesti, piuttosto che dover scrivere una funzione diversa solo per utilizzare un’implementazione diversa della stessa classe.
Ad esempio, invece di dover scrivere due diverse funzioni impure per utilizzare due diversi logger memorizzati globalmente, una funzione pura prenderebbe semplicemente il logger desiderato come input.
Testing
La mancanza di effetti collaterali rende le funzioni pure molto facili da testare, poiché abbiamo solo bisogno di testare che gli ingressi producano le uscite desiderate. Non abbiamo bisogno di verificare la validità di qualsiasi stato di programma globale nei nostri test di funzioni specifiche.
Inoltre, poiché tutte le dipendenze sono fornite come input, possiamo facilmente deridere le dipendenze. In un ambiente impuro, dovremmo tenere traccia dello stato di una certa dipendenza globale durante tutti i test.
Tuttavia, in un’impostazione pura, forniremmo semplicemente tutte le dipendenze come input. Non dobbiamo più preoccuparci di mantenere lo stato globale durante i nostri test e ora possiamo potenzialmente fornire versioni diverse di dipendenze a test diversi.
Questo ci consente di testare le funzioni mentre abbiamo esplicitamente il controllo sulle dipendenze fornite in ogni test.
Trasparenza referenziale
La trasparenza referenziale si riferisce alla possibilità di sostituire la chiamata di una funzione con il valore di output corrispondente senza modificare il comportamento di un programma.
Per ottenere la trasparenza referenziale, una funzione deve essere pura. Questo ha vantaggi in termini di leggibilità e velocità. I compilatori sono spesso in grado di ottimizzare il codice che presenta trasparenza referenziale.
Caching
Poiché le funzioni pure restituiscono sempre lo stesso output per lo stesso input, possiamo memorizzare nella cache i risultati delle chiamate di funzione pure.
Il caching si riferisce all’utilizzo di una tecnica, come la memoizzazione, per memorizzare i risultati delle funzioni in modo che abbiamo solo bisogno di calcolarli una volta.
In genere, per una funzionef: Input -> Output
questo viene realizzato attraverso una mappa (come una mappa hash) daInput -> Output
.
Quando si esegue una funzione, prima controlliamo se la mappa contiene l’input come chiave. Se lo fa, restituiamo il valore di output della mappa, altrimenti calcoliamo f(input)
, quindi memorizziamo l’output nella mappa prima di restituirlo.