I ternari nidificati sono fantastici

Smoke Art Cubes to Smoke - MattysFlicks - (CC BY 2.0)
Nota: fa parte della serie "Composing Software" sull'apprendimento da zero della programmazione funzionale e delle tecniche software compositive in JavaScript ES6 +. Rimanete sintonizzati. C'è molto di più a venire!

La saggezza convenzionale ti farebbe credere che i ternari nidificati siano illeggibili e dovrebbero essere evitati.

La saggezza convenzionale a volte non è saggia.

La verità è che i ternari sono di solito molto più semplici delle dichiarazioni if. Le persone credono al contrario per due motivi:

  1. Hanno più familiarità con le dichiarazioni if. Il pregiudizio alla familiarità può portarci a credere a cose che non sono vere, anche quando ci vengono presentate prove contrarie.
  2. Le persone cercano di usare le dichiarazioni ternarie come se fossero dichiarazioni. Non funziona, perché le espressioni ternarie sono espressioni, non dichiarazioni.

Prima di entrare nei dettagli, definiamo un'espressione ternaria:

Un'espressione ternaria è un'espressione condizionale che valuta un valore. Consiste in una condizionale, una clausola di verità (il valore da produrre se il condizionale valuta un valore di verità) e una clausola di falsa (il valore da produrre se il condizionale valuta un valore di falsa).

Sembrano così:

(condizionale)
  ? truthyClause
  : falsyClause

Espressioni vs dichiarazioni

Diversi linguaggi di programmazione (inclusi Smalltalk, Haskell e la maggior parte dei linguaggi di programmazione funzionali) non hanno affatto delle dichiarazioni if. Hanno invece espressioni se.

Un'espressione if è un'espressione condizionale che restituisce un valore. Consiste in una condizionale, una clausola di verità (il valore da produrre se il condizionale valuta un valore di verità) e una clausola di falsa (il valore da produrre se il condizionale valuta un valore di falsa).

Ti sembra familiare? La maggior parte dei linguaggi di programmazione funzionale usa espressioni ternarie per la loro parola chiave if. Perché?

Un'espressione è un pezzo di codice che restituisce un singolo valore.

Un'istruzione è un blocco di codice che potrebbe non essere valutato in alcun modo. In JavaScript se le dichiarazioni non valutano i valori. Affinché un'istruzione if in JavaScript possa fare qualcosa di utile, deve causare un effetto collaterale o restituire un valore dalla funzione contenitore.

Nella programmazione funzionale, tendiamo ad evitare mutazioni e altri effetti collaterali. Poiché se in JavaScript offre naturalmente mutazioni ed effetti collaterali, molti programmatori funzionali cercano invece ternari - nidificati o meno. Incluso me.

Pensare in termini di espressioni ternarie è un po 'diverso dal pensare in termini di affermazioni if, ma se ti alleni molto per un paio di settimane, inizierai a gravitare naturalmente verso i ternari, anche solo perché è meno battitura, come Presto vedrò.

Bias di familiarità

L'affermazione comune che sento è che le espressioni ternarie nidificate sono "difficili da leggere". Distruggiamo quel mito con alcuni esempi di codice:

const withIf = ({
  condizione A, condizione B.
}) => {
  if (conditionA) {
    if (condizione B) {
      valore di ritorno A;
    }
    valore di ritorno B;
  }
  valore di ritorno C;
};

Nota che in questa versione ci sono condizioni di nidificazione e parentesi graffe che separano visivamente la clausola di verità dalle clausole di falsa, facendole sentire molto disconnesse. Questa è una logica abbastanza semplice, ma è un po 'faticosa da analizzare.

Ecco la stessa logica scritta in forma di espressione ternaria:

const withTernary = ({
  condizione A, condizione B.
}) => (
  (! ConditionA)
    ? valoreC
    : (condizione B)
    ? valoreA
    : valoreB
);

Ci sono alcuni punti interessanti da fare qui:

Daisy Chaining vs Nesting

Innanzitutto, abbiamo appiattito la nidificazione. I ternari "nidificati" sono un po 'un termine improprio, perché i ternari sono facili da scrivere in linea retta, non è mai necessario nidificarli con livelli di rientro. Leggono semplicemente dall'alto verso il basso in linea retta, restituendo un valore non appena raggiungono una condizione veritiera o il fallback.

Se scrivi correttamente i ternari, non è necessario analizzare nidificazione. È piuttosto difficile perdersi seguendo una linea retta.

Probabilmente dovremmo chiamarli "ternari incatenati" invece.

La seconda cosa che voglio sottolineare è che, al fine di semplificare questo concatenamento in linea retta, ho cambiato un po 'l'ordine: se arrivi alla fine di un'espressione ternaria e trovi che devi scrivere due clausole dei due punti (: ), afferrare l'ultima clausola, spostarla in alto e invertire la logica del primo condizionale per semplificare l'analisi del ternario. Niente più confusione!

Vale la pena notare che possiamo usare lo stesso trucco per semplificare il modulo di istruzione if:

const withIf = ({
  condizione A, condizione B.
}) => {
  if (! conditionA) restituisce valoreC;
  if (condizione B) {
    valore di ritorno A;
  }
  valore di ritorno B;
};

È meglio, ma rompe ancora visivamente le clausole correlate per conditionB, che può causare confusione. Ho visto che il problema porta a bug logici durante la manutenzione del codice. Anche con la logica appiattita, questa versione è ancora più ingombra rispetto alla versione ternaria.

Sintassi Clutter

La versione if contiene un po 'più di rumore: la parola chiave if vs?, Return per forzare l'istruzione a restituire un valore, punti e virgola extra, parentesi graffe aggiuntive, ecc ... A differenza di questo esempio, la maggior parte delle istruzioni if ​​muta anche qualche stato esterno, che inoltre aggiunge al codice extra e complessità.

Il codice aggiuntivo è negativo per alcuni motivi importanti. Ho già detto tutto questo, ma vale la pena ripeterlo fino a quando tutti gli sviluppatori non si sono bruciati nel cervello:

Memoria di lavoro

Il cervello umano medio ha solo poche risorse condivise per quanti discreti nella memoria di lavoro e ogni variabile consuma potenzialmente uno di quei quanti. Man mano che aggiungi più variabili, la tua capacità di richiamare con precisione il significato di ciascuna variabile diminuisce. I modelli di memoria di lavoro implicano in genere 4-7 quanti discreti. Al di sopra di questi numeri, i tassi di errore aumentano notevolmente.

Quando forziamo la mutazione o gli effetti collaterali con dichiarazioni if ​​rispetto ai ternari, ciò comporta spesso l'aggiunta di variabili al mix che non devono essere presenti.

Rapporto segnale-rumore

Il codice conciso migliora anche il rapporto segnale-rumore del codice. È come ascoltare una radio: quando la radio non è sintonizzata correttamente sulla stazione, si sente un sacco di rumore interferente ed è più difficile ascoltare la musica. Quando lo sintonizzi sulla stazione corretta, il rumore scompare e ricevi un segnale musicale più forte.

Il codice è allo stesso modo. Un'espressione più concisa del codice porta a una migliore comprensione. Alcuni codici ci forniscono informazioni utili e alcuni di essi occupano solo spazio. Se riesci a ridurre la quantità di codice che utilizzi senza ridurre il significato che viene trasmesso, renderai il codice più facile da analizzare e comprendere per le altre persone che hanno bisogno di leggerlo.

Superficie per bug

Dai un'occhiata alle funzioni prima e dopo. Sembra che la funzione sia andata a dieta e abbia perso un sacco di peso. Questo è importante perché un codice aggiuntivo significa una superficie extra in cui nascondersi i bug, il che significa che più bug si nasconderanno al suo interno.

Meno codice = meno area di superficie per i bug = meno bug.

Effetti collaterali e stato mutevole condiviso

Molte istruzioni if ​​fanno molto più che valutare un valore. Inoltre causano effetti collaterali o mutano le variabili, quindi non puoi vedere l'effetto completo dell'istruzione if senza conoscere anche l'impatto di quegli effetti collaterali e la storia completa di tutto il resto che tocca il suo stato mutabile condiviso.

Limitare te stesso al ritorno di una disciplina delle forze di valore: tagliare le dipendenze in modo che il tuo programma sia più facile da capire, eseguire il debug, refactor e mantenere.

Questo è in realtà il mio vantaggio preferito delle espressioni ternarie:

L'uso di ternari ti renderà uno sviluppatore migliore.

Conclusione

Poiché tutti i ternari sono facili da disporre in linea retta, dall'alto verso il basso, chiamarli "ternari nidificati" è un po 'un termine improprio. Chiamiamoli "ternari incatenati", invece.

I ternari incatenati presentano diversi vantaggi rispetto alle dichiarazioni if:

  • È sempre facile scriverli in modo che leggano in linea retta, dall'alto verso il basso. Se riesci a seguire una linea retta, puoi leggere un ternario incatenato.
  • I ternari riducono il disordine della sintassi. Meno codice = meno area di superficie per i bug = meno bug.
  • I ternari non hanno bisogno di variabili temporanee, riducendo il carico sulla memoria di lavoro.
  • I ternari hanno un miglior rapporto segnale-rumore.
  • Se le dichiarazioni incoraggiano effetti collaterali e mutazione. I ternari incoraggiano il codice puro.
  • Il codice puro disaccoppia le nostre espressioni e funzioni l'una dall'altra, quindi i ternari ci addestrano per essere sviluppatori migliori.

Maggiori informazioni su EricElliottJS.com

Le lezioni video sulla programmazione funzionale sono disponibili per i membri di EricElliottJS.com. Se non sei un membro, iscriviti oggi.

Eric Elliott è l'autore di "Programmazione di applicazioni JavaScript" (O’Reilly) e "Impara JavaScript con Eric Elliott". Ha contribuito alle esperienze software per Adobe Systems, Zumba Fitness, The Wall Street Journal, ESPN, BBC e i migliori artisti della registrazione tra cui Usher, Frank Ocean, Metallica e molti altri.

Lavora da remoto ovunque con la donna più bella del mondo.