Hai ragione, gli optionals possono essere un dolore, motivo per cui non dovresti esagerare. Ma sono più di qualcosa che devi affrontare quando usi i framework.Sono una soluzione a un problema incredibilmente comune: come gestire una chiamata che restituisce un risultato che potrebbe essere OK o meno.
Ad esempio, prendere il membro Array.first
. Questa è una pratica utility che ti dà il primo elemento di un array. Perché è utile essere in grado di chiamare a.first
, quando si può semplicemente chiamare a[0]
? Perché a runtime la matrice potrebbe essere vuota, nel qual caso esploderà a[0]
. Naturalmente è possibile controllare in anticipo il a.count
-
a. si potrebbe dimenticare,
e
b. questo si traduce in un codice abbastanza brutto.
Array.first
si occupa di ciò restituendo un facoltativo. Quindi sei costretto a scartare l'opzionale prima di poter usare il valore che è il primo elemento dell'array.
Ora, per il problema relativo al valore scartato solo esistente all'interno del blocco con if let
. Immagina il codice parallelo, controllando il numero di array. Sarebbe lo stesso, vero?
if a.count > 0 {
// use a[0]
}
// outside the block, no guarantee
// a[0] is valid
if let firstElement = a.first {
// use firstElement
}
// outside the block, you _can't_
// use firstElement
Certo, si potrebbe fare qualcosa come eseguire un ritorno anticipato dalla funzione se il conteggio è zero. Funziona ma è un po 'a rischio - cosa succede se ti sei dimenticato di farlo, o di metterlo in una dichiarazione condizionale che non è stata eseguita? In sostanza, puoi fare lo stesso con array.first
: controlla il conteggio all'inizio della funzione, quindi in seguito fai array.first!
. Ma quello !
è come un segnale per te - stai attento, stai facendo qualcosa di pericoloso e sarai dispiaciuto se il tuo codice non è completamente corretto.
Gli optionals aiutano anche a rendere le alternative leggermente più belle. Supponiamo di volere il valore predefinito se la matrice è vuota. Invece di questo:
array.count > 0 ? a[0] : somedefault
si può scrivere questo:
array.first ?? somedefault
Questo è più bello in diversi modi. Mette la cosa importante in primo piano: il valore che vuoi è come inizia l'espressione, seguito dal default. A differenza dell'espressione ternaria, che ti colpisce prima con l'espressione di controllo, seguita dal valore che effettivamente desideri, e infine il valore predefinito. È anche più infallibile: è molto più facile evitare errori di battitura, e impossibile che l'errore di battitura porti a un'esplosione di runtime.
Prendete un altro esempio: la funzione find
. Questo controlla se un valore è in una collezione e restituisce l'indice della sua posizione. Ma il valore potrebbe non essere presente nella collezione. Altre lingue potrebbero gestirlo restituendo l'indice finale (che non punta a un valore ma piuttosto a uno passato rispetto all'ultimo valore). Questo è ciò che viene definito un valore "sentinella": un valore che assomiglia a un risultato regolare, ma che in realtà ha un significato speciale. Come nell'esempio precedente, dovresti controllare che il risultato non sia uguale all'indice finale prima di usarlo. Ma per fare questo devi conoscere. Devi cercare i documenti per find
e confermare come funziona.
Con find
restituire un optional è naturale, quando si capisce l'idioma opzionale, rendersi conto che il motivo è che il risultato potrebbe non essere valido per l'ovvia ragione.E si applicano anche le stesse cose sulla sicurezza di cui sopra - non puoi dimenticartene accidentalmente, e usa il risultato come indice, perché devi prima scartarlo.
Detto questo, è possibile sovrautilizzare gli optionals in quanto sono un onere da controllare. Questo è il motivo per cui gli indici di array non restituiscono gli optionals: creerebbe così tanto fastidio da doverli controllare e scartare continuamente, specialmente quando si sa che l'indice che si sta usando è valido (ad esempio, sei in un ciclo for su un intervallo di indice valido dell'array) che le persone utilizzerebbero costantemente !
e quindi ingombrano il loro codice senza trarne vantaggio. Ma poi i metodi di supporto come il primo e l'ultimo vengono aggiunti per coprire casi comuni in cui le persone desiderano eseguire rapidamente un'operazione senza dover prima controllare la dimensione dell'array, ma vogliono farlo in modo sicuro.
(Swift dizionari, d'altra parte, si prevede di accedere regolarmente tramite indici non validi, che è il motivo per cui il loro metodo [key]
fa ritorno opzionale)
Ancora meglio è se la possibilità di fallimento può essere evitato del tutto . Ad esempio, quando filter
non corrisponde a nessun elemento, non viene restituito un valore nil
opzionale. Restituisce un array vuoto. "Beh, ovviamente lo sarebbe", si potrebbe dire. Ma ti sorprenderebbe quanto spesso vedi qualcuno fare un valore di ritorno come un array opzionale quando in realtà dovrebbero solo restituire uno vuoto. Quindi hai perfettamente ragione nel dire che dovresti evitare gli optionals, tranne quando sono necessari - è solo una questione di quali mezzi necessari. Negli esempi sopra, direi che sono necessari e una soluzione migliore per le alternative.
Hai mai sentito parlare di programmazione orientata alle ferrovie? Ecco un post che spiega questo concetto in F # http://fsharpforfunandprofit.com/posts/recipe-part2/ – mustafa
ottima domanda, guarda in Programmazione funzionale e (eventualmente) Funzionale Swift – Nick
Se stai per lanciare l'ultimo voto per chiudere questa domanda è troppo soggettiva, potresti commentare cosa c'è di sbagliato in questo? La domanda è davvero un come-faccio-io-faccio-questo, e la risposta in realtà risponde. Se pensi davvero che dovrebbe essere chiuso, cosa avrei potuto fare per migliorarlo? –