Quindi vuoi essere un programmatore funzionale (parte 3)

Fare il primo passo per comprendere i concetti di Programmazione funzionale è il passo più importante e talvolta il più difficile. Ma non deve essere. Non con la giusta prospettiva.

Parti precedenti: parte 1, parte 2

Composizione delle funzioni

Come programmatori, siamo pigri. Non vogliamo costruire, testare e distribuire codice che abbiamo scritto più e più volte.

Cerchiamo sempre di capire come svolgere il lavoro una volta e come riusarlo per fare qualcos'altro.

Il riutilizzo del codice suona alla grande ma è difficile da ottenere. Rendi il codice troppo specifico e non puoi riutilizzarlo. Renderlo troppo generale e può essere troppo difficile da usare in primo luogo.

Quindi ciò di cui abbiamo bisogno è un equilibrio tra i due, un modo per creare pezzi più piccoli e riutilizzabili che possiamo usare come blocchi per costruire funzionalità più complesse.

Nella programmazione funzionale, le funzioni sono i nostri mattoni. Li scriviamo per svolgere compiti molto specifici e poi li mettiamo insieme come blocchi Lego ™.

Questo si chiama composizione delle funzioni.

Quindi, come funziona? Cominciamo con due funzioni Javascript:

var add10 = funzione (valore) {
    valore restituito + 10;
};
var mult5 = funzione (valore) {
    valore restituito * 5;
};

Questo è troppo dettagliato quindi riscriviamolo usando la notazione con la freccia grassa:

var add10 = value => value + 10;
var mult5 = valore => valore * 5;

Così va meglio. Ora immaginiamo di voler anche avere una funzione che accetta un valore e ne aggiunge 10 e quindi moltiplica il risultato per 5. Potremmo scrivere:

var mult5AfterAdd10 = value => 5 * (value + 10)

Anche se questo è un esempio molto semplice, non vogliamo ancora scrivere questa funzione da zero. Innanzitutto, potremmo fare un errore come dimenticare le parentesi.

In secondo luogo, abbiamo già una funzione che aggiunge 10 e un altro che si moltiplica per 5. Stiamo scrivendo un codice che abbiamo già scritto.

Quindi, invece, usiamo add10 e mult5 per creare la nostra nuova funzione:

var mult5AfterAdd10 = value => mult5 (add10 (value));

Abbiamo appena usato le funzioni esistenti per creare mult5AfterAdd10, ma esiste un modo migliore.

In matematica, f ∘ g è una composizione funzionale e viene letta “f composta con g” o, più comunemente, “f dopo g”. Quindi (f ∘ g) (x) equivale a chiamare f dopo aver chiamato g con x o semplicemente f (g (x)).

Nel nostro esempio, abbiamo mult5 ∘ add10 o “mult5 dopo add10”, da cui il nome della nostra funzione, mult5AfterAdd10.

Ed è esattamente quello che abbiamo fatto. Abbiamo chiamato mult5 dopo aver chiamato add10 con valore o semplicemente mult5 (add10 (valore)).

Poiché Javascript non esegue la composizione funzionale in modo nativo, diamo un'occhiata a Elm:

valore add10 =
    valore + 10
valore mult5 =
    valore * 5
valore mult5AfterAdd10 =
    (mult5 << add10) valore

L'operatore << infisso è come componi le funzioni in Elm. Ci dà un senso visivo di come i dati scorrono. Innanzitutto, il valore viene passato ad add10, quindi i suoi risultati vengono passati a mult5.

Nota le parentesi in mult5AfterAdd10, ovvero (mult5 << add10). Sono lì per assicurarsi che le funzioni siano composte prima di applicare il valore.

Puoi comporre tutte le funzioni che vuoi in questo modo:

f x =
   (g << h << s << r << t) x

Qui x viene passato alla funzione t il cui risultato viene passato a r il cui risultato viene passato a se così via. Se hai fatto qualcosa di simile in Javascript, sembrerebbe g (h (s (r (t (x))))), un incubo tra parentesi

Notazione senza punti

Esiste uno stile di funzioni di scrittura senza dover specificare i parametri denominati Point-Free Notation. All'inizio, questo stile sembrerà strano ma man mano che prosegui, apprezzerai la brevità.

In mult5AfterAdd10, noterai che il valore è specificato due volte. Una volta nell'elenco dei parametri e una volta quando viene utilizzato.

- Questa è una funzione che prevede 1 parametro
valore mult5AfterAdd10 =
    (mult5 << add10) valore

Ma questo parametro non è necessario poiché add10, la funzione più a destra nella composizione, prevede lo stesso parametro. La seguente versione senza punti è equivalente:

- Questa è anche una funzione che prevede 1 parametro
mult5AfterAdd10 =
    (mult5 << add10)

I vantaggi dell'utilizzo della versione senza punti sono numerosi.

Innanzitutto, non è necessario specificare parametri ridondanti. E dal momento che non dobbiamo specificarli, non dobbiamo pensare a nomi per tutti loro.

In secondo luogo, è più facile da leggere e ragionare poiché è meno prolisso. Questo esempio è semplice, ma immagina una funzione che ha assunto più parametri.

Guai in Paradiso

Finora abbiamo visto come funziona la composizione delle funzioni e come dovremmo specificare le nostre funzioni in Notazione senza punti per brevità, chiarezza e flessibilità.

Ora proviamo a utilizzare queste idee in uno scenario leggermente diverso e vediamo come vanno. Immagina di sostituire add10 con add:

aggiungi x y =
    x + y
valore mult5 =
    valore * 5

Come scriviamo mult5After10 con solo queste 2 funzioni?

Pensaci un po 'prima di continuare a leggere. No sul serio. Pensa a. Prova a farlo.

Ok, quindi se hai davvero trascorso del tempo a pensarci, potresti aver trovato una soluzione come:

-- Questo è sbagliato !!!!
mult5AfterAdd10 =
    (mult5 << aggiungi) 10

Ma questo non funzionerebbe. Perché? Perché add accetta 2 parametri.

Se questo non è ovvio in Elm, prova a scrivere questo in Javascript:

var mult5AfterAdd10 = mult5 (aggiungi (10)); // questo non funziona

Questo codice è sbagliato ma perché?

Poiché la funzione di aggiunta ottiene solo 1 dei suoi 2 parametri qui, i suoi risultati errati vengono passati a mult5. Ciò produrrà risultati errati.

In effetti, in Elm, il compilatore non ti permetterà nemmeno di scrivere un codice così sbagliato (che è una delle cose più belle di Elm).

Proviamo di nuovo:

var mult5AfterAdd10 = y => mult5 (aggiungi (10, y)); // non privo di punti

Questo non è privo di senso ma probabilmente potrei conviverci. Ma ora non sto più solo combinando le funzioni. Sto scrivendo una nuova funzione. Inoltre, se questo diventa più complicato, ad es. se voglio comporre mult5AfterAdd10 con qualcos'altro, mi metterò davvero nei guai.

Quindi sembrerebbe che la composizione delle funzioni abbia un'utilità limitata poiché non possiamo sposare queste due funzioni. È un peccato perché è così potente.

Come possiamo risolverlo? Di cosa avremmo bisogno per risolvere questo problema?

Bene, ciò che sarebbe davvero fantastico sarebbe se avessimo un modo di dare alla nostra funzione add solo uno dei suoi parametri in anticipo e poi otterrebbe il suo secondo parametro in seguito quando viene chiamato mult5AfterAdd10.

Si scopre che c'è un modo e si chiama Currying.

Il mio cervello!!!!

Per ora basta.

Nelle parti successive di questo articolo, parlerò di Currying, funzioni funzionali comuni (ad es. Mappa, filtro, piega ecc.), Trasparenza referenziale e altro.

A seguire: parte 4

Se ti è piaciuto, fai clic su in basso in modo che altre persone lo vedano qui su Medium.

Se vuoi unirti a una comunità di sviluppatori web che si apprendono e si aiutano a vicenda nello sviluppo di app Web utilizzando la Programmazione funzionale in Elm, controlla il mio gruppo Facebook, Impara la programmazione Elm https://www.facebook.com/groups/learnelm/

Il mio Twitter: @cscalfani