Il buon stile funzionale non ha bisogno della codifica difensiva quasi quanto lo stile imperativo. prova e catch non è l'unica struttura di controllo che viene trascurata in Scala (e in altri linguaggi funzionali); per e mentre i loop sono specie a rischio di estinzione e anche if ... else è usato in modo più selettivo (anche se molto più efficacemente, poiché restituisce un valore ).
Nel mondo funzionale, abbini il tuo codice alla forma dei dati. O scegli una forma che dia al tuo codice la struttura che desideri. Questo è vero non solo con Opzione e O ma Elenco e Set e Mappa e altro ancora. Tutti sono abbinati a potenti funzioni di ordine superiore: mappa , piega , filtro ecc.
Per fare un esempio banale, se un codificatore Java o C ++ vuole recuperare il primo (o tutti) degli elementi in un elenco e manipolarlo, devono prima controllare che l'elenco non sia vuoto o avvolgerlo in prova / cattura per gestire l'errore. Se devono gestire una serie di elenchi, è più probabile che quest'ultima opzione sia scelta (e ancor meno probabile che corrisponda al contesto che potrebbe generare un errore).
In Scala puoi semplicemente farlo nell'elenco di Ints chiamato xs :
xs.headOption map (_ * 2)
// Returns twice the first item - if there is one - as an Option
o
xs map (_ * 2)
// Doubles everything in the list. Returns a list.
Se non c'è nulla nell'elenco, non accadrà nulla. Se vuoi che succeda qualcosa, puoi rilevare l'errore e reagire.
xs.headOption map (_ * 2) orElse Some(0)
// Returns Some(twice the head item) if there is one or Some(0)
xs.headOption map (_ * 2) getOrElse 0
// Returns twice the head item or 0 if there is none
Ancora meglio, se hai a che fare con un elenco di elenchi, ognuno dei quali potrebbe essere vuoto, la mappatura su ciascuno di essi produrrà risultati per ogni elenco popolato e nessun problema con nessun vuoto. È molto potente quando si tratta di grandi raccolte di dati imprevedibili.
Quali di questi tipi funzionali (Opzione e Entrambi e Lista e Mappa e tutti gli altri - mi dispiace usare la brutta parola - monadi) offrono anche, molto importante, la separazione delle preoccupazioni. Nota come ho usato mappa in tutti gli esempi precedenti. In due esempi è Option.map , mentre nell'altro è List.map . In tutti i casi, map sta facendo la stessa cosa: ¨ Applica la mia funzione ai dati contenuti nel contenitore, preservando il contesto. Se si tratta di un contesto di lista, si ottiene un elenco di dati trasformati. Se è un contesto "può o non può esistere", puoi ottenere il tuo oggetto trasformato. Se si tratta di un contesto che potrebbe davvero andare male in modo sbagliato, puoi ottenere il tuo oggetto o la possibilità di lamentarti. E così via.
In questo modo si ottiene una separazione delle preoccupazioni tra l'azione che si desidera eseguire e il contesto in cui viene applicata. Il grande vantaggio è che se decidi di cambiare il contesto (ad es. Set di oggetti unici anziché arbitrario Elenco di oggetti), il resto del codice non ha bisogno di cambiare affatto. mappa farà ancora la cosa giusta (così come filtro , fold e il resto).
Parole chiave imperative incorporate come try..catch o if ... else ... if ... else non hanno questo potere. Per cominciare, non hanno alcun significato reale e devono essere messi insieme (distorcendo il codice intricato all'interno di essi) e non offrono alcuna garanzia. Immagina di averli usati per gestire un elenco arbitrario di oggetti e poi decidere di voler garantire unicità . Quanto di quel codice imperativo dovresti cambiare? Come puoi essere sicuro che funzionerà?
Lasciare le eccezioni per gestire l'inaspettato e irrisolvibile. I tipi funzionali possono essere sicuri delle loro garanzie, per aiutarti a evitare errori comuni e prevedibili.