Perché l'opzione / forse è considerata una buona idea e le eccezioni controllate non lo sono?


23

Alcuni linguaggi di programmazione come ad esempio Scala hanno il concetto di Optiontipi (chiamato anche Maybe), che può contenere un valore oppure no.

Da quello che ho letto su di loro sono considerati ampiamente come un modo superiore di affrontare questo problema rispetto a null, perché costringono esplicitamente il programmatore a considerare i casi in cui potrebbe non esserci un valore invece di esplodere durante il runtime.

D'altra parte, le Eccezioni verificate in Java sembrano essere considerate una cattiva idea e Java sembra essere l'unico linguaggio ampiamente utilizzato che le implementa. Ma l'idea dietro di loro sembra essere in qualche modo simile al Optiontipo, per forzare esplicitamente il programmatore a gestire il fatto che potrebbe essere generata un'eccezione.

Ci sono altri problemi con le eccezioni selezionate che i Optiontipi non hanno? Oppure queste idee non sono così simili come penso, e ci sono buone ragioni per forzare la gestione esplicita delle Opzioni e non delle Eccezioni?


Vedi anche il Either e atipo di dati.

4
Informazioni sulle eccezioni verificate: come utente di una moltitudine di librerie Java open source e interne con base di codice in evoluzione e documenti mancanti / obsoleti, rabbrividisco al pensiero che Java non imponga determinate eccezioni per essere dichiarate esplicitamente. Sarebbe un incubo di errori di runtime non gestiti spuntati in posti cattivi, inaspettatamente. E Java7 rende finalmente la gestione delle eccezioni quasi sana, eliminando gran parte del vecchio disordine da provare.
hyde,

Risposte:


24

Perché Optionè compostabile. Ci sono un sacco di metodi utili su Optionche consentono di scrivere codice conciso, pur consentendo un controllo preciso sul flusso: map, flatMap, toList, flattene altro ancora. Ciò è dovuto al fatto che Optionè un tipo particolare di monade, alcuni oggetti che sappiamo molto bene come comporre. Se non avessi questi metodi e dovessi eseguire il pattern match sempre attivo Optiono chiamare isDefinedspesso, non sarebbero altrettanto utili.

Invece, mentre le eccezioni controllate aggiungono un po 'di sicurezza, non c'è molto che puoi fare con loro se non catturarli o lasciarli riempire lo stack (con la piastra della caldaia aggiunta nella dichiarazione del tipo).


1
Le eccezioni verificate si compongono più o meno allo stesso modo ... La differenza tra try {/* bunch of complex code involving calls to 50 different methods that may throw SomeCheckedException */} catch(SomeCheckedException e) {/* operation failed, do something */}ed fromMaybe someDefaultValue (something >>= otherThing >>= ...50 other functions that may return Nothing...)è esattamente cosa? A parte il fatto che il primo ti fornisce maggiori dettagli su cosa è andato storto.
user253751

14

Sebbene correlate, le eccezioni e gli oggetti Forse non affrontano lo stesso tipo di problemi.

eccezioni

Le eccezioni brillano davvero quando devi affrontare non localmente una situazione eccezionale (che in alcuni casi sembra essere un errore). Ad esempio stai analizzando un csv e vuoi proteggerti da linee con una formattazione errata. Il luogo in cui si riscontra che qualcosa non va potrebbe essere alcune chiamate di funzione lontane dall'iterazione della linea. Se si genera un'eccezione al livello più profondo (dove si scopre il problema di formattazione), è possibile catturarlo nel ciclo, registrare l'errore e passare alla riga successiva. Non è necessario modificare nulla nel resto del codice.

L'eccezione controllata aggiunge molto dolore perché tutte le funzioni intermedie devono dichiarare il tipo gettabile. La funzione sconfigge lo scopo originale, motivo per cui non sono popolari al giorno d'oggi.

Forse oggetti

Forse gli oggetti dovrebbero essere scelti quando si è in grado di gestire localmente un "fallimento". In tal senso, sono un sostituto per un codice di ritorno + passa per riferimento api o un tipo nullable.

Il vantaggio dell'oggetto Maybe è che dichiari esplicitamente che qualcosa potrebbe non andare. In haskell, un oggetto non forse deve avere un valore, altrimenti il ​​programma non verrà compilato.

Il problema con i tipi nullable è che devi controllare sempre null per essere assolutamente sicuro. Lo stato "Qualcosa potrebbe essere sbagliato" è quello predefinito.

Il problema con i codici di ritorno + passa per ref apis è che sono meno leggibili per la maggior parte delle persone.


1
@MattFenwick grazie per il feedback. Perché pensi che l'esempio CSV non abbia senso? Il PO in realtà non richiede tecniche di evitamento delle piastre di cottura, e ho la sensazione che il vocabolario come il funzionario applicativo e le monadi possano essere troppo tecnici per questa domanda.
Simon Bergot,

1
Vorrei sottolineare che con Java (non sono sicuro di altre lingue con le eccezioni verificate) gli IDE si occupano dell'aggiunta e della potatura dei tiri e dell'aggiornamento della parte del bollettino dei commenti javadoc. Quindi almeno quella parte non dà fastidio e certamente nessun dolore. Che si tratti di una seccatura o di una manna o qualcosa nel mezzo quando si fa la progettazione di API, questa è un'altra cosa ...
hyde,

5
@hyde: Solo perché un IDE è in grado di automatizzare la generazione di piastre di caldaia inutili non significa che una piastra di caldaia inutile non sia un problema.
Michael Shaw,

2
@hyde: Ma il dolore non viene rimosso. La inutile caldaia è ancora lì, ingombra il codice senza motivo. Se c'è un motivo per il boilerplate, che cos'è?
Michael Shaw,

2
@MichaelShaw Se l'eccezione è inutile, rimuovila: ignora la situazione o restituisci invece il valore dell'errore. Se si tratta di un bug o di una situazione irrecuperabile: utilizzare un'eccezione non selezionata. Ciò che rimane è importante come ad esempio i tipi di parametri, non la targa di cottura inutile. Se si tratta di un'API errata nella libreria esistente, prendere in considerazione il metodo / classe wrapper, utilizzare un'altra libreria o semplicemente subire l'API errata.
hyde,

1

perché con Maybete puoi ritardare la gestione dell'errore fino a quando non hai effettivamente bisogno del valore (che potrebbe essere un paio di chiamate di metodo)

mentre l'eccezione selezionata deve essere gestita nella posizione della chiamata

L'unico lato positivo delle eccezioni è che è possibile trasmettere più informazioni sul motivo per cui ha fallito (a meno che qualcuno non sviluppi un MaybeErrorcampo con un lancio quando è un errore)


2
Ma posso differire la gestione dell'eccezione controllata dichiarando che il mio metodo genera questa eccezione.
Scienziato pazzo,

1
@MadScientist che sale solo nello stack di chiamate, mentre Forse può andare in tutte le direzioni
maniaco del cricchetto

5
Penso che non dovresti confondere i Maybetipi con errori di gestione. Un'eccezione viene utilizzata per segnalare un errore, un tipo di opzione viene utilizzato per rappresentare il risultato di una funzione parziale. Il ritorno di una funzione parziale Nothingnon è un errore.
Giorgio

@MadScientist: se una chiamata al metodo restituisce un'indicazione "Valore non valido", l'istruzione immediatamente dopo può essere eseguita. Al contrario, se un metodo genera un'eccezione che non viene rilevata immediatamente, l'istruzione che segue la chiamata verrà ignorata. Consentire alle eccezioni verificate di percolare lo stack di chiamate è generalmente malvagio (e non avrebbe dovuto essere il modo "più semplice" per gestirle) poiché non è possibile che un chiamante indichi se la condizione ha il significato previsto dal metodo chiamato o se rappresenta una condizione imprevista che il metodo chiamato sta facendo scoppiare.
supercat
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.