JavaScript: l'idea della programmazione funzionale.

Mar 21 2023
La programmazione funzionale è un paradigma di programmazione che enfatizza la scrittura di programmi utilizzando funzioni pure, che non hanno effetti collaterali e restituiscono sempre lo stesso output dato lo stesso input. JavaScript è un linguaggio multi-paradigma che supporta la programmazione funzionale, insieme ad altri paradigmi di programmazione come la programmazione orientata agli oggetti e la programmazione imperativa.
Foto di Growtika su Unsplash

La programmazione funzionale è un paradigma di programmazione che enfatizza la scrittura di programmi utilizzando funzioni pure, che non hanno effetti collaterali e restituiscono sempre lo stesso output dato lo stesso input. JavaScript è un linguaggio multi-paradigma che supporta la programmazione funzionale, insieme ad altri paradigmi di programmazione come la programmazione orientata agli oggetti e la programmazione imperativa.

Le funzioni pure sono importanti nella programmazione funzionale perché sono deterministiche e prive di effetti collaterali, rendendole più facili da ragionare, testare ed eseguire il debug. L'effetto collaterale è qualsiasi effetto o interazione osservabile che si verifica al di fuori dell'ambito di una funzione o di un metodo come risultato della sua esecuzione. Ciò può includere la modifica di una variabile o di un oggetto al di fuori dell'ambito della funzione, la scrittura su un file o l'esecuzione di qualsiasi operazione di I/O.

function add(a, b) {
  return a + b;
}

Poiché funzioni che non hanno effetti collaterali e producono lo stesso output ogni volta che vengono chiamate con lo stesso input, possiamo renderle più flessibili e applicare queste interessanti proprietà/tecniche elencate di seguito:

  • Idempotenza : può essere utile in una varietà di scenari, ad esempio nelle chiamate API, negli aggiornamenti del database o negli aggiornamenti dell'interfaccia utente, in cui si desidera garantire che la stessa operazione non venga eseguita più volte.
  • // Applying this function multiple times on the same number will always return the same result.
    
    Math.abs(-5) // Returns 5
    
    Math.abs(Math.abs(-5)) // Also returs 5
    
    

    // Example of currying
    function multiply(a) {
      return function(b) {
        return a*b;
      }
    }
    
    const multiplyBy2 = multiply(2);
    multiplyBy2(6);
    // Returns 6*2 = 12
    

  • Applicazione parziale : questa tecnica è simile al curry ma leggermente diversa. È un modo per noi di applicare parzialmente una funzione, processo di produzione di una funzione con un numero minore di parametri.
  • // Example of Partial Application
    function multiply(a,b,c) {
      return a*b*c;
    }
    
    const multiplyBy2 = multiply.bind(null, 2);
    multiplyBy2(3,4)
    // Returns 2*3*4 = 24
    

  • Memoizzazione : è una tecnica utilizzata per ottimizzare le prestazioni delle funzioni memorizzando nella cache i risultati di chiamate di funzioni costose e restituendo il risultato memorizzato nella cache quando si verificano nuovamente gli stessi input. In JavaScript, la memorizzazione può essere implementata utilizzando chiusure e oggetti.
  • // Example of Memoization
    const memoizedOperation = (() => {
        let cache = {};
        return (n) => {
            if (n in cache) {
                console.log("Returning cached value: ");
                return cache[n];
            } else {
                console.log("Performing operation: ");
                cache[n] = n*10;
                return cache[n];
            }
        }
    })();
      
    console.log(memoizedOperation(4));
    console.log(memoizedOperation(4));
    console.log(memoizedOperation(5));
    
    /*
    Returns: 
      Performing operation: 
      40
      Returning cached value: 
      40
      Performing operation: 
      50
    */
    
    

  • Composizione: è una tecnica che combina più funzioni in un'unica funzione, in cui l'output di una funzione viene passato come input alla funzione successiva. Questo ci permette di creare funzioni complesse componendo funzioni più semplici. La composizione delle funzioni può essere ottenuta utilizzando compose()e pipe()(fornito da librerie come Lodash o Ramda) oppure possiamo anche definire le nostre funzioni.
  • // Example of Composition by creating our own function
    const compose = (f,g) => (n) => f(g(n));
    const pipe = (f,g) => (n) => g(f(n));
    const multiplyBy2 = (n) => n * 2;
    const add2 = (n) => n + 2;
    const multiplyAndAddComp = compose(multiplyBy2, add2)
    const multiplyAndAddPipe = pipe(multiplyBy2, add2)
    
    console.log("Compose: ", multiplyAndAddComp(5));
    console.log("Pipe: ", multiplyAndAddPipe(5));
    
    /*Returns: 
      Compose:  14
      Pipe:  12
    */
    

Queste funzioni sono utilizzate per la composizione di funzioni in JavaScript. La composefunzione accetta due funzioni, fe g, come argomenti e restituisce una nuova funzione che è la composizione di fe g. La funzione restituita accetta un argomento n, si applica ga ne quindi si applica fal risultato. La pipefunzione funziona in modo simile a compose, ma applica le funzioni in ordine inverso. Prende due funzioni, fe g, come argomenti e restituisce una nuova funzione che si applica fal risultato di g. Il codice definisce anche due semplici funzioni, multiplyBy2e add2, che vengono usate per comporre le funzioni. Il codice quindi crea due nuove funzioni multiplyAndAddCompemultiplyAndAddPipe, componendo le funzioni multiplyBy2e add2utilizzando rispettivamente le funzioni composee pipe. Infine, le console.logdichiarazioni stampano i risultati dell'applicazione multiplyAndAddCompe multiplyAndAddPipeal numero 5.

multiplyAndAddCompmoltiplica prima 5 per 2 e poi aggiunge 2 usando la composizione della funzione, quindi il risultato è 12. multiplyAndAddPipesomma prima 2 a 5 e poi moltiplica il risultato per 2 usando la composizione della funzione, quindi il risultato è 14.

Meno numero di parametri ci sono in una funzione, più facile è usare quella funzione con le tecniche di cui sopra.

Conclusione

Ora, l'idea della programmazione funzionale è quella di separare i dati e le funzioni o la logica, il che rende il codice chiaro e comprensibile, estendibile, gestibile e, soprattutto, ASCIUTTO.