Perché imparare la programmazione funzionale in JavaScript?

Smoke Art Cubes to Smoke - MattysFlicks - (CC BY 2.0)
Nota: fa parte della serie "Composing Software" (ora un libro!) Sull'apprendimento da zero della programmazione funzionale e delle tecniche software compositive in JavaScriptES6 +. Rimanete sintonizzati. C'è molto di più a venire!
Acquista il libro | Indice |

Dimentica tutto ciò che pensi di sapere su JavaScript e avvicina questo materiale con la mente di un principiante. Per aiutarti a fare ciò, esamineremo le basi di JavaScript da zero, come se non avessi mai visto JavaScript prima. Se sei un principiante, sei fortunato. Finalmente qualcosa che esplora ES6 e la programmazione funzionale da zero! Spero che tutti i nuovi concetti vengano spiegati lungo la strada, ma non contare su troppe coccole.

Se sei uno sviluppatore esperto che ha già familiarità con JavaScript o un linguaggio puramente funzionale, forse stai pensando che JavaScript sia una scelta divertente per un'esplorazione della programmazione funzionale. Metti da parte quei pensieri e cerca di avvicinarti al materiale con una mente aperta. Potresti scoprire che esiste un altro livello nella programmazione JavaScript. Uno che non sapevi mai esistito.

Dato che questo testo si chiama "Software di composizione" e la programmazione funzionale è il modo ovvio per comporre il software (usando la composizione di funzioni, funzioni di ordine superiore, ecc ...), potresti chiederti perché non sto parlando di Haskell, ClojureScript o Elm , anziché JavaScript.

JavaScript ha le caratteristiche più importanti necessarie per la programmazione funzionale:

  1. Funzioni di prima classe: la capacità di utilizzare le funzioni come valori di dati: passare funzioni come argomenti, restituire funzioni e assegnare funzioni a variabili e proprietà dell'oggetto. Questa proprietà consente funzioni di ordine superiore, che consentono l'applicazione parziale, il curry e la composizione.
  2. Funzioni anonime e sintassi lambda concisa: x => x * 2 è un'espressione di funzione valida in JavaScript. I lambda concisi semplificano il lavoro con funzioni di ordine superiore.
  3. Chiusure: una chiusura è il raggruppamento di una funzione con il suo ambiente lessicale. Le chiusure vengono create al momento della creazione della funzione. Quando una funzione viene definita all'interno di un'altra funzione, ha accesso ai collegamenti variabili nella funzione esterna, anche dopo l'uscita dalla funzione esterna. Le chiusure sono il modo in cui le applicazioni parziali ottengono i loro argomenti fissi. Un argomento fisso è un argomento associato nell'ambito di chiusura di una funzione restituita. In add2 (1) (2), 1 è un argomento fisso nella funzione restituita da add2 (1).

Cosa manca a JavaScript

JavaScript è un linguaggio multi-paradigma, nel senso che supporta la programmazione in molti stili diversi. Altri stili supportati da JavaScript includono la programmazione procedurale (imperativa) (come C), in cui le funzioni rappresentano una subroutine di istruzioni che possono essere richiamate ripetutamente per il riutilizzo e l'organizzazione, la programmazione orientata agli oggetti, in cui gli oggetti, non le funzioni, sono i mattoni principali, e, naturalmente, la programmazione funzionale. Lo svantaggio di un linguaggio multi-paradigma è che la programmazione imperativa e orientata agli oggetti tende a implicare che quasi tutto deve essere mutabile.

La mutazione è una modifica alla struttura dei dati che avviene sul posto. Per esempio:

const foo = {
  bar: 'baz'
};
foo.bar = 'qux'; // mutazione

Gli oggetti di solito devono essere mutabili in modo che le loro proprietà possano essere aggiornate con metodi. Nella programmazione imperativa, la maggior parte delle strutture di dati è mutabile per consentire un'efficace manipolazione sul posto di oggetti e matrici.

Ecco alcune caratteristiche che alcuni linguaggi funzionali hanno, che JavaScript non ha:

  1. Purezza: in alcune lingue FP, la purezza è imposta dalla lingua. Le espressioni con effetti collaterali non sono consentite.
  2. Immutabilità: alcune lingue FP disabilitano le mutazioni. Invece di mutare una struttura di dati esistente, come un array o un oggetto, le espressioni valutano nuove strutture di dati. Ciò può sembrare inefficiente, ma la maggior parte dei linguaggi funzionali utilizza strutture di dati trie sotto il cofano, che presentano una condivisione strutturale: il che significa che il vecchio oggetto e il nuovo oggetto condividono riferimenti ai dati che sono gli stessi.
  3. Ricorsione: la ricorsione è la capacità di una funzione di fare riferimento a sé stessa ai fini dell'iterazione. In molte lingue del PQ, la ricorsione è l'unico modo per iterare. Non ci sono istruzioni loop come for, while o do loop.

Purezza: in JavaScript, la purezza deve essere raggiunta per convenzione. Se non stai costruendo la maggior parte della tua applicazione componendo funzioni pure, non stai programmando usando lo stile funzionale. Purtroppo, in JavaScript è facile uscire di pista creando e utilizzando accidentalmente funzioni impure.

Immutabilità: nei linguaggi puramente funzionali, l'immutabilità è spesso imposta. JavaScript manca di strutture dati basate su trie efficienti e immutabili utilizzate dalla maggior parte dei linguaggi funzionali, ma ci sono librerie che aiutano, tra cui Immutable.js e Mori. Spero che le versioni future delle specifiche ECMAScript includano strutture di dati immutabili.

Ci sono segni che offrono speranza, come l'aggiunta della parola chiave const in ES6. Non è possibile riassegnare un'associazione di nome definita con const per fare riferimento a un valore diverso. È importante capire che const non rappresenta un valore immutabile.

Un oggetto const non può essere riassegnato per fare riferimento a un oggetto completamente diverso, ma l'oggetto a cui fa riferimento può avere le sue proprietà mutate. JavaScript ha anche la possibilità di congelare oggetti (), ma tali oggetti vengono congelati solo a livello di radice, il che significa che un oggetto nidificato può ancora avere proprietà delle sue proprietà mutate. In altre parole, c'è ancora molta strada da percorrere prima di vedere veri e propri immutabili compositi nelle specifiche JavaScript.

Ricorsione: JavaScript supporta tecnicamente la ricorsione, ma la maggior parte dei linguaggi funzionali ha una funzione chiamata ottimizzazione delle chiamate di coda. L'ottimizzazione delle chiamate di coda è una funzione che consente alle funzioni ricorsive di riutilizzare i frame dello stack per le chiamate ricorsive.

Senza l'ottimizzazione delle chiamate di coda, uno stack di chiamate può crescere senza limiti e causare un overflow dello stack. JavaScript tecnicamente ha ottenuto una forma limitata di ottimizzazione delle chiamate di coda nella specifica ES6. Sfortunatamente, solo uno dei principali motori di browser lo ha implementato e l'ottimizzazione è stata parzialmente implementata e successivamente rimossa da Babel (il compilatore JavaScript standard più popolare, utilizzato per compilare ES6 in ES5 per l'uso in browser meno recenti).

In conclusione: non è ancora sicuro utilizzare la ricorsione per grandi iterazioni, anche se stai attento a chiamare la funzione nella posizione di coda.

Cosa manca di JavaScript in quei linguaggi funzionali puri

Un purista ti dirà che la mutabilità di JavaScript è il suo principale svantaggio, il che è vero. Tuttavia, a volte effetti collaterali e mutazione sono utili. In effetti, è impossibile creare applicazioni moderne più utili senza effetti collaterali. I linguaggi funzionali puri come Haskell usano effetti collaterali, ma li mimetizzano da funzioni pure usando scatole chiamate monadi, permettendo al programma di rimanere puro anche se gli effetti collaterali rappresentati dalle monadi sono impuri.

Il problema con le monadi è che, anche se il loro uso è abbastanza semplice, spiegare cos'è una monade a qualcuno che non ha familiarità con molti esempi è un po 'come spiegare come appare il colore "blu" a una persona cieca.

"Una monade è un monoide nella categoria degli endofunctor, qual è il problema?" ~ James Iry, citando in modo fittizio Philip Wadler, parafrasando una vera citazione di Saunders Mac Lane. "Una storia breve, incompleta e per lo più sbagliata dei linguaggi di programmazione"

In genere, la parodia esagera le cose per rendere divertente un punto divertente. Nella citazione sopra, la spiegazione delle monadi è in realtà semplificata dalla citazione originale, che va così:

"Una monade in X è solo un monoide nella categoria degli endofunctor di X, con il prodotto × sostituito dalla composizione degli endofunctor e dall'unità impostata dall'identificatore endofunctor." ~ Saunders Mac Lane. "Categorie per il matematico che lavora"

Anche così, secondo me, la paura delle monadi è un ragionamento debole. Il modo migliore per imparare le monadi non è leggere un mucchio di libri e post di blog sull'argomento, ma saltare dentro e iniziare a usarli. Come per la maggior parte delle cose nella programmazione funzionale, l'impenetrabile vocabolario accademico è molto più difficile da comprendere rispetto ai concetti. Fidati di me, non devi capire Saunders Mac Lane per capire la programmazione funzionale.

Anche se potrebbe non essere assolutamente ideale per ogni stile di programmazione, JavaScript non è dispologicamente un linguaggio generico progettato per essere utilizzabile da varie persone con diversi stili e sfondi di programmazione.

Secondo Brendan Eich, questo era intenzionale sin dall'inizio. Netscape doveva supportare due tipi di programmatori:

“... gli autori dei componenti, che hanno scritto in C ++ o (speravamo) Java; e gli "scripters", dilettanti o professionisti, che scrivevano codice direttamente incorporato in HTML. "

Inizialmente, l'intento era che Netscape avrebbe supportato due lingue diverse, e il linguaggio di script avrebbe probabilmente assomigliato a Scheme (un dialetto di Lisp). Ancora una volta, Brendan Eich:

"Sono stato reclutato su Netscape con la promessa di" fare Schema "nel browser."

JavaScript doveva essere una nuova lingua:

"Il diktat della direzione dell'ingegneria superiore era che il linguaggio doveva" assomigliare a Java ". Ciò ha escluso Perl, Python e Tcl, insieme a Scheme. ”

Quindi, le idee nella testa di Brendan Eich erano:

  1. Schema nel browser.
  2. Assomiglia a Java.

Finì per essere ancora più simile a un miscuglio:

"Non sono orgoglioso, ma sono felice di aver scelto le funzioni di prima classe di Scheme e i prototipi di Self-ish (sebbene singolari) come ingredienti principali. Le influenze Java, in particolare i bug di data y2k ma anche la distinzione primitiva vs. oggetto (ad esempio stringa vs. stringa), sono state sfortunate. "

Vorrei aggiungere all'elenco di "sfortunate" funzionalità simili a Java che alla fine si sono fatte strada in JavaScript:

  • Funzioni del costruttore e la nuova parola chiave, con semantica di chiamata e utilizzo diversa dalle funzioni di fabbrica.
  • Una parola chiave di classe con antenato singolo si estende come meccanismo di ereditarietà principale.
  • La tendenza dell'utente a pensare a una classe come se fosse un tipo statico (non lo è).

Il mio consiglio: evitali quando puoi.

Siamo fortunati che JavaScript abbia finito per essere un linguaggio così capace, perché si scopre che l'approccio di scripting ha conquistato l'approccio "componente" (oggi, le estensioni Java, Flash e ActiveX non sono supportate in un gran numero di browser installati).

Ciò che alla fine abbiamo ottenuto è stata una lingua direttamente supportata dal browser: JavaScript.

Ciò significa che i browser sono meno gonfi e meno buggy, perché devono supportare solo un singolo set di associazioni linguistiche: JavaScript. Potresti pensare che WebAssembly sia un'eccezione, ma uno degli obiettivi di progettazione di WebAssembly è quello di condividere i collegamenti linguistici di JavaScript usando un albero di sintassi astratto (AST) compatibile. In effetti, le prime dimostrazioni hanno compilato WebAssembly in un sottoinsieme di JavaScript noto come ASM.js.

La posizione come unico linguaggio di programmazione standard generico per la piattaforma web ha permesso a JavaScript di cavalcare la più grande ondata di popolarità linguistica nella storia del software:

Le app hanno mangiato il mondo, le web hanno mangiato le app e JavaScript ha mangiato il web.

Con più misure, JavaScript è ora il linguaggio di programmazione più popolare al mondo.

JavaScript non è lo strumento ideale per la programmazione funzionale, ma è un ottimo strumento per la creazione di applicazioni di grandi dimensioni su team distribuiti molto grandi, in cui team diversi potrebbero avere idee diverse su come creare un'applicazione.

Alcuni team possono concentrarsi sulla colla per script, dove la programmazione imperativa è particolarmente utile. Altri possono concentrarsi sulla costruzione di astrazioni architettoniche, in cui un po '(moderato, attento) pensiero OO potrebbe non essere una cattiva idea. Altri ancora possono abbracciare la programmazione funzionale, riducendo le azioni dell'utente utilizzando funzioni pure per la gestione deterministica e verificabile dello stato dell'applicazione. I membri di questi team usano tutti la stessa lingua, il che significa che possono scambiare più facilmente idee, imparare gli uni dagli altri e basarsi sul lavoro reciproco.

In JavaScript, tutte queste idee possono coesistere, il che consente a più persone di abbracciare JavaScript, il che ha portato al più grande registro di pacchetti open source al mondo (a febbraio 2017), npm.

Il vero punto di forza di JavaScript è la diversità di pensiero e utenti nell'ecosistema. Potrebbe non essere assolutamente il linguaggio ideale per i puristi della programmazione funzionale, ma può essere il linguaggio ideale per lavorare insieme usando una lingua che funziona su quasi tutte le piattaforme che puoi immaginare - familiare a persone che provengono da altri linguaggi popolari come Java, Lisp o C. JavaScript non si sentirà perfettamente a suo agio con gli utenti con uno di questi sfondi, ma potrebbero sentirsi abbastanza a loro agio da imparare la lingua e diventare rapidamente produttivi.

Sono d'accordo che JavaScript non è la lingua migliore per i programmatori funzionali. Tuttavia, nessun altro linguaggio funzionale può affermare che è un linguaggio che tutti possono usare e abbracciare, e come dimostrato da ES6: JavaScript può e migliora nel soddisfare le esigenze degli utenti interessati alla programmazione funzionale. Invece di abbandonare JavaScript e il suo incredibile ecosistema utilizzato praticamente da tutte le aziende del mondo, perché non accettarlo e renderlo un linguaggio migliore per la composizione del software in modo incrementale?

Così com'è, JavaScript è già un linguaggio di programmazione funzionale abbastanza buono, il che significa che le persone stanno costruendo tutti i tipi di cose utili e interessanti in JavaScript, usando tecniche di programmazione funzionale. Netflix (e ogni app creata con Angular 2+) utilizza utility funzionali basate su RxJS. Facebook utilizza i concetti di funzioni pure, funzioni di ordine superiore e componenti di ordine superiore in React per creare Facebook e Instagram. PayPal, KhanAcademy e Flipkart utilizzano Redux per la gestione dello stato.

Non sono soli: Angular, React, Redux e Lodash sono i principali framework e librerie nell'ecosistema dell'applicazione JavaScript, e tutti sono fortemente influenzati dalla programmazione funzionale - o nei casi di Lodash e Redux, creati per l'espresso scopo di abilitare schemi di programmazione funzionale in vere applicazioni JavaScript.

"Perché JavaScript?" Perché JavaScript è il linguaggio che la maggior parte delle aziende reali utilizza per creare software reali. Lo adori o lo odi, JavaScript ha rubato il titolo di "linguaggio di programmazione funzionale più popolare" da Lisp, che è stato il portabandiera per decenni. È vero, oggi Haskell è un portatore di standard molto più adatto ai concetti di programmazione funzionale, ma la gente non sta costruendo tante applicazioni reali in Haskell.

In qualsiasi momento, ci sono quasi centomila aperture di lavori JavaScript negli Stati Uniti e centinaia di migliaia in tutto il mondo. L'apprendimento di Haskell ti insegnerà molto sulla programmazione funzionale, ma l'apprendimento di JavaScript ti insegnerà molto sulla costruzione di app di produzione per lavori reali.

Le app hanno mangiato il mondo, le web hanno mangiato le app e JavaScript ha mangiato il web.

Acquista il libro | Indice |

Maggiori informazioni su EricElliottJS.com

Le lezioni video con sfide di codice interattivo sono disponibili per i membri di EricElliottJS.com. Se non sei un membro, iscriviti oggi.

Eric Elliott è un esperto di sistemi distribuiti e autore dei libri "Composing Software" e "Programmazione di applicazioni JavaScript". Come co-fondatore di DevAnywhere.io, insegna agli sviluppatori le competenze di cui hanno bisogno per lavorare in remoto e abbracciare l'equilibrio tra lavoro e vita privata. Crea e fornisce consulenza a team di sviluppo per progetti crittografici e ha contribuito alle esperienze software per Adobe Systems, Zumba Fitness, The Wall Street Journal, ESPN, BBC e i migliori artisti discografici tra cui Usher, Frank Ocean, Metallica e molti altri.

Gode ​​di uno stile di vita remoto con la donna più bella del mondo.