Una rapida introduzione a pipe () e compose () in JavaScript

La programmazione funzionale è stata per me un viaggio davvero sorprendente. Questo post, e post simili, sono un tentativo di condividere le mie intuizioni e prospettive mentre cammino nuove terre di programmazione funzionale.

Ramda è stata la mia libreria FP preferita per la facilità con cui rende la programmazione funzionale in JavaScript. Lo consiglio vivamente.

Comporre blocchi per formare una struttura. Roba abbastanza profonda ...

Tubo

Il concetto di pipe è semplice: combina n funzioni. È un tubo che scorre da sinistra a destra, chiamando ciascuna funzione con l'output dell'ultima.

Scriviamo una funzione che restituisce il nome di qualcuno.

getName = (person) => person.name
getName ({name: 'Buckethead'})
// 'Buckethead'

Scriviamo una funzione che mette in maiuscolo le stringhe.

uppercase = (string) => string.toUpperCase ()
maiuscolo ( 'Buckethead')
// "FIBBIA"

Quindi, se volessimo ottenere e capitalizzare il nome della persona, potremmo farlo:

name = getName ({name: 'Buckethead'})
maiuscolo (nome)
// "FIBBIA"

Va bene, ma eliminiamo quel nome di variabile intermedia.

maiuscolo (getName ({name: 'Buckethead'}))

Meglio, ma non mi piace quella nidificazione. Può diventare troppo affollato. Cosa succede se vogliamo aggiungere una funzione che ottiene i primi 6 caratteri di una stringa?

get6Characters = (string) => string.substring (0, 6)
get6Characters ( 'Buckethead')
// 'Secchio'

Con il risultato di:

get6Characters (maiuscolo (getName ({name: 'Buckethead'})))
'SECCHIO'

Diventiamo davvero pazzi e aggiungiamo una funzione per invertire le stringhe.

reverse = (stringa) => stringa
  .Diviso('')
  .inverso()
  .aderire('')
reverse ( 'Buckethead')
// "daehtekcuB"

Ora abbiamo:

reverse (get6Characters (maiuscolo (getName ({name: 'Buckethead'}))))
// "TEKCUB"

Può andare un po '... molto.

Pipa in soccorso!

Invece di bloccare le funzioni all'interno delle funzioni o creare un mucchio di variabili intermedie, eseguiamo il pipe di tutto!

tubo(
  getName,
  maiuscolo,
  get6Characters,
  inverso
) ({name: 'Buckethead'})
// "TEKCUB"

Arte pura. È come una lista di cose da fare!

Facciamo un passo avanti.

A scopo dimostrativo, userò un'implementazione di pipe da uno degli articoli di programmazione funzionale di Eric Elliott.

pipe = (... fns) => x => fns.reduce ((v, f) => f (v), x)

Adoro questo piccolo one-liner.

Usando i parametri di riposo, vedi il mio articolo al riguardo, possiamo reindirizzare n funzioni. Ogni funzione prende l'output della precedente ed è tutta ridotta a un singolo valore.

E puoi usarlo proprio come abbiamo fatto sopra.

tubo(
  getName,
  maiuscolo,
  get6Characters,
  inverso
) ({name: 'Buckethead'})
// "TEKCUB"

Espanderò pipe e aggiungerò alcune dichiarazioni di debugger e andremo riga per riga.

pipe = (... funzioni) => (valore) => {
  debugger;
  funzioni di ritorno
    .reduce ((currentValue, currentFunction) => {
       debugger;
       return currentFunction (currentValue);
    }, valore)
}

Chiama pipe con il nostro esempio e lascia che le meraviglie si svolgano.

Scopri le variabili locali. funzioni è un array di 4 funzioni e il valore è {nome: 'Buckethead'}.

Dato che abbiamo usato i parametri di riposo (di nuovo, vedi il mio articolo ), pipe consente di utilizzare un numero qualsiasi di funzioni. Sarà solo un loop e chiamerà ciascuno.

Nel prossimo debugger, siamo dentro riduci. Qui è dove currentValue viene passato a currentFunction e restituito.

Vediamo che il risultato è 'Buckethead' perché currentFunction restituisce la proprietà .name di qualsiasi oggetto. Questo verrà restituito in forma ridotta, il che significa che la prossima volta diventerà il nuovo valore corrente. Andiamo al prossimo debugger e vediamo.

Ora currentValue è "Buckethead" perché è quello che è stato restituito l'ultima volta. currentFunction è in maiuscolo, quindi 'BUCKETHEAD' sarà il prossimo currentValue.

La stessa idea, cogliere i primi 6 personaggi di "BUCKETHEAD" e consegnarli alla funzione successiva.

reverse (". aedi emaS")

E hai finito!

Che dire di compose ()?

È solo il tubo nella direzione opposta.

Quindi, se volessi lo stesso risultato della nostra pipa sopra, faresti il ​​contrario.

comporre(
  inverso,
  get6Characters,
  maiuscolo,
  getName,
) ({name: 'Buckethead'})

Notate come getName è l'ultimo nella catena e il contrario è il primo?

Ecco una rapida implementazione di compose, sempre per gentile concessione del magico Eric Elliott, dallo stesso articolo.

compose = (... fns) => x => fns.reduceRight ((v, f) => f (v), x);

Lascerò l'espansione di questa funzione con i debugger come esercizio per te. Divertiti, usalo, apprezzalo. E, soprattutto, buon divertimento!

Fino alla prossima volta!

Stai attento,
Yazeed Bzadough