Currying e Applicazione Parziale in JavaScript
Currying e applicazione parziale sono tecniche di programmazione funzionale che migliorano la modularità e il riutilizzo del codice JavaScript.
Currying e applicazione parziale sono potenti tecniche di programmazione funzionale che possono migliorare significativamente la modularità e il riutilizzo del codice JavaScript. Entrambe si basano sulle closure — la capacità di una funzione interna di ricordare le variabili della funzione esterna che l'ha creata — così una funzione restituita mantiene gli argomenti già forniti.
Questo articolo spiega cos'è il currying e perché è importante, come scrivere un helper curry riutilizzabile, in cosa differisce l'applicazione parziale dal currying, casi d'uso reali e le buone pratiche (e le insidie) da tenere a mente.
Capire il Currying
Il currying è una tecnica in cui una funzione viene trasformata in una sequenza di funzioni, ciascuna che accetta un singolo argomento. Permette di scomporre una funzione che prende più argomenti in una serie di funzioni unarie (a singolo argomento).
Esempio di Currying
Considera una semplice funzione che somma tre numeri:
function add(a, b, c) {
return a + b + c;
}Possiamo trasformare questa funzione in una versione con currying:
Spiegazione:
- La funzione
addprende tre argomenti e restituisce la loro somma. - La funzione
curryAddè una versione con currying diadd. Prende un argomentoae restituisce un'altra funzione che accettab. Questa seconda funzione restituisce a sua volta un'altra funzione che accettac. La funzione più interna restituisce la somma dia,bec.
Vantaggi del Currying
- Riutilizzabilità: Le funzioni con currying permettono di creare nuove funzioni fissando alcuni argomenti. Questo migliora la riutilizzabilità consentendo di creare facilmente versioni specializzate di una funzione senza duplicare il codice.
Qui, curriedAdd è una versione con currying della funzione add. Ci permette di creare funzioni specializzate come add5 fissando il primo argomento (a) a 5. Questo favorisce il riutilizzo del codice poiché possiamo creare più funzioni specializzate senza ripetere la logica per l'addizione.
- Composizione di Funzioni: Il currying rende più facile comporre funzioni. La composizione di funzioni è il processo di combinare due o più funzioni per produrre una nuova funzione.
In questo esempio, componiamo le funzioni multiply e addOne usando il currying. La forma concisa a => b => a * b si basa sulle arrow function, che rendono facile leggere le funzioni annidate a singolo argomento. Usando il currying su queste funzioni, possiamo facilmente creare una nuova funzione addOneThenMultiplyBy5, che prima aggiunge 1 all'input (x) e poi moltiplica il risultato per 5. Questo dimostra come il currying faciliti la composizione di funzioni, rendendo più semplice creare nuove funzioni combinando quelle esistenti.
Implementare il Currying in JavaScript
Possiamo creare una funzione di utilità per applicare il currying a qualsiasi funzione. Ecco un'implementazione di una funzione generica per il currying:
Spiegazione:
-
Funzione di Currying (
curry):- La funzione
curryprende un'altra funzionefncome input. - Restituisce una nuova funzione chiamata
curried. - Questa funzione
curriedaccetta un numero qualsiasi di argomenti usando la sintassi dei parametri rest (...args).
- La funzione
-
Funzione con Currying (
curried):- All'interno di
curried, viene verificato se il numero di argomenti forniti (args.length) è maggiore o uguale al numero di argomenti attesi dalla funzione originalefn(fn.length). - Se vengono forniti abbastanza argomenti, chiama la funzione originale
fncon quegli argomenti usandofn.apply(this, args). - Se non vengono forniti abbastanza argomenti, restituisce una nuova funzione che accetta altri argomenti (
nextArgs) usando l'operatore spread (...nextArgs). - Questa nuova funzione chiama ricorsivamente
curriedcon gli argomenti combinati (args.concat(nextArgs)), assicurando che tutti gli argomenti vengano alla fine raccolti prima di chiamare la funzione originalefn.
Nota:
fn.lengthconta solo i parametri senza valori predefiniti e ignora i parametri rest. - All'interno di
-
Esempio di utilizzo:
- Definiamo una funzione
multiplyche accetta tre argomenti e restituisce il loro prodotto. - Creiamo una versione con currying della funzione
multiplypassandola alla funzionecurry, che restituisce una nuova funzionecurriedMultiply. - Ora,
curriedMultiplypuò essere chiamata con uno, due o tre argomenti. - Ogni volta che chiamiamo
curriedMultiplycon un argomento o più argomenti, restituisce una nuova funzione finché tutti gli argomenti non sono stati raccolti, a quel punto restituisce il risultato della moltiplicazione degli argomenti.
- Definiamo una funzione
Esplorare l'Applicazione Parziale
L'applicazione parziale è una tecnica in cui si crea una nuova funzione pre-riempiendo alcuni argomenti della funzione originale. È particolarmente utile per creare funzioni specializzate.
Esempio di Applicazione Parziale
Considera la seguente funzione che formatta un messaggio:
function formatMessage(greeting, name) {
return `${greeting}, ${name}!`;
}Possiamo creare una funzione con applicazione parziale:
Spiegazione:
- La funzione
formatMessageprende due argomenti,greetingename, e restituisce un messaggio formattato. - La funzione
partialprende una funzionefne alcuni argomenti predefiniti (...presetArgs). Restituisce una nuova funzione che accetta gli argomenti rimanenti (...laterArgs). - Quando la nuova funzione viene chiamata, combina
presetArgselaterArgse chiama la funzione originalefncon questi argomenti. - Usando
partial, creiamogreetHello, una funzione che utilizza sempre "Hello" come saluto. Quando viene chiamata con un nome, restituisce il messaggio completo.
Vantaggi dell'Applicazione Parziale
-
Semplificazione: Crea funzioni più semplici da quelle più complesse
Supponiamo di avere una funzione che calcola il prezzo finale di un articolo dopo aver applicato uno sconto e le tasse.
function calculateFinalPrice(price, discount, tax) {
return price - (price * discount) + (price * tax);
}Questa funzione richiede tre argomenti, il che la rende un po' scomoda da usare ripetutamente se le aliquote di sconto e tasse sono spesso le stesse. Con l'applicazione parziale, possiamo semplificarla.
Nell'esempio sopra, applyDiscountAndTax è una funzione con applicazione parziale che preimposta i valori di discount e tax. Questo rende più facile calcolare il prezzo finale per articoli diversi senza dover specificare ripetutamente le aliquote di sconto e tasse.
-
Riutilizzabilità del Codice: Riutilizza la logica comune delle funzioni con argomenti predefiniti diversi
Immagina di avere una funzione che registra messaggi con diversi livelli di gravità.
function logMessage(level, message) {
console.log(`[${level}] ${message}`);
}Possiamo creare funzioni riutilizzabili per diversi livelli di log usando l'applicazione parziale.
Qui, createLogger è una funzione con applicazione parziale che imposta l'argomento level. Le funzioni infoLogger e errorLogger possono ora essere usate per registrare messaggi con i livelli di log predefiniti, riutilizzando la logica comune di logMessage.
- Maggiore Leggibilità: Rende il codice più leggibile scomponendo funzioni complesse Considera una funzione che formatta le date in stili diversi.
function formatDate(date, format) {
const options = { year: 'numeric', month: '2-digit', day: '2-digit' };
if (format === 'US') {
options.month = 'long';
} else if (format === 'EU') {
options.day = 'numeric';
options.month = 'numeric';
}
return new Date(date).toLocaleDateString(undefined, options);
}Usando l'applicazione parziale, possiamo creare funzioni più leggibili per i diversi formati di data.
createDateFormatter applica parzialmente l'argomento format, risultando in funzioni specifiche per i formati di data US ed EU. Questa scomposizione rende il codice più leggibile e facile da capire, poiché ogni funzione di formattazione è dedicata a un formato specifico.
Questi esempi illustrano come l'applicazione parziale in JavaScript possa semplificare funzioni complesse, migliorare la riutilizzabilità del codice e aumentare la leggibilità, rendendo il codice più facile da gestire e comprendere.
Currying vs. Applicazione Parziale
Queste due tecniche sono strettamente correlate e spesso confuse, ma non sono la stessa cosa:
- Il currying trasforma una funzione di N argomenti in N funzioni annidate che ciascuna prende esattamente uno argomento. Una funzione completamente con currying viene sempre chiamata un argomento alla volta:
f(a)(b)(c). - L'applicazione parziale fissa alcuni degli argomenti di una funzione e restituisce una nuova funzione che accetta il resto in una singola chiamata:
g = partial(f, a)e poig(b, c).
In breve: il currying riguarda la forma della funzione (una catena di chiamate unarie), mentre l'applicazione parziale riguarda il pre-riempimento degli argomenti. Nota che l'helper generico curry sopra è più flessibile del currying stretto — accetta argomenti in gruppi (curriedMultiply(2, 3)(4)), quindi sfuma il confine e supporta efficacemente anche l'applicazione parziale.
Buone Pratiche per Usare Currying e Applicazione Parziale
Mantieni le Funzioni Pure
- Assicurati che le funzioni con currying e applicazione parziale rimangano pure, senza effetti collaterali. Questo le rende più facili da ragionare e testare.
Usa Quando Appropriato
- Usa il currying e l'applicazione parziale quando si adattano naturalmente al problema in questione.
- Evita di abusare di queste tecniche, poiché possono rendere il codice più difficile da capire se non usate con giudizio.
Sfrutta la Composizione di Funzioni
- Combina le funzioni con currying per creare funzionalità più complesse. Il currying funziona bene con la composizione di funzioni, portando a codice più modulare.
Documentazione e Denominazione
- Documenta correttamente le funzioni con currying e applicazione parziale per indicare l'utilizzo previsto.
- Usa nomi chiari e descrittivi per le funzioni per trasmettere il loro scopo.
Attenzione a Queste Insidie
fn.lengthnon è affidabile per il currying automatico. Un helper genericocurryche si basa sufn.lengthsi comporterà in modo errato con parametri predefiniti o parametri rest, perché questi non vengono contati. Se una funzione li usa, passa l'arità attesa esplicitamente invece di fidarti difn.length.- Il currying eccessivo nuoce alla leggibilità. Una lunga catena
f(a)(b)(c)(d)è più difficile da leggere di una singola chiamata ben denominata. Ricorri al currying quando rimuove genuinamente la ripetizione. - Attenzione a
this. Le arrow function non hanno il propriothis, quindi una catena con currying scritta con arrow function non può essere usata come metodo che dipende dall'oggetto chiamante. Se hai bisogno delthisdinamico, vedi il binding delle funzioni.
Se usi frequentemente currying e applicazione parziale, considera l'utilizzo della libreria Lodash, che fornisce utili funzioni di utilità come _.curry e _.partial.
Combina con i Metodi degli Array
Il currying può essere combinato efficacemente con i metodi degli array come map, filter e reduce per un codice conciso ed espressivo.
Spiegazione:
- La funzione
curriedMultiplyviene usata per crearemultiplyByTwo, una funzione che moltiplica il suo argomento per 2. - L'array
numbersviene trasformato usandomap, applicandomultiplyByTwoa ciascun elemento, risultando in un nuovo array di numeri raddoppiati.
Conclusione
Currying e applicazione parziale in JavaScript offrono potenti tecniche per semplificare la composizione di funzioni, migliorare la riutilizzabilità del codice e aumentare la leggibilità. Il currying trasforma funzioni con più argomenti in una serie di funzioni unarie, permettendo un codice più flessibile e modulare. L'applicazione parziale consente la preimpostazione degli argomenti delle funzioni, facilitando il riutilizzo del codice e la semplificazione di funzioni complesse. Sfruttando questi concetti di programmazione funzionale, gli sviluppatori possono scrivere codice JavaScript più pulito, conciso e manutenibile.