2011-10-09 22 views
27

Qual è il significato delle eccezioni in Haskell? L'unico utilizzo che vedo è di inserire undefined o error nel mio codice per interrompere l'esecuzione dei programmi. In caso contrario, considero la programmazione con eccezioni come un difetto logico di progettazione. Ma Haskell ha un modulo avanzato avanzato Control.Exception che viene utilizzato da Prelude. Ho letto che il motivo per le eccezioni in C++ era quello di prevenire molte "linee di chiamata, quindi controllare lo stato" delle linee nel codice. Ma queste cose possono essere astratte in Haskell. L'unica altra ragione che posso vedere per la gestione delle eccezioni in Haskell è con FFI, per gestire eccezioni estranee, ma solo per uso interno in una funzione Haskell che avvolge la chiamata.Significato delle eccezioni Haskell

+0

C'è una serie di motivi per cui è possibile ottenere un'eccezione. In un sistema senza eccezioni cosa succede quando: si esaurisce la memoria? Il sistema operativo invia un segnale? Ti dividi per zero? Valuta una funzione parziale ... e scopri che è parziale! Le eccezioni non sono una bella parte di nessuna lingua che conosca, ma per far fronte al mondo reale sono una parte necessaria. –

+4

Esiste un modo reale per rispondere a questa domanda o è solo una sfuriata? Se è solo un rant, forse un post sul blog sarebbe un mezzo più appropriato. –

+1

Sì, per tali cose dovrebbe essere possibile interrompere il programma, quindi abbiamo "indefinito" e "errore" come ho detto. Dividere per zero e valutare una funzione "al di fuori del suo dominio" sono difetti logici di progettazione. Ma non vedo il significato di usare le eccezioni nella meccanica di un programma (in Haskell). – telephone

risposta

27

A mio modesto parere, eccezioni significa "hai rotto il contratto di una funzione". Non sto parlando del tipo di contratto, sto parlando delle cose che generalmente trovi nei commenti.

-- The reciprocal is not defined for the value 0 
myRecip :: Fractional a => a -> a 
myRecip x | x == 0 = throw DivideByZero 
      | otherwise = 1/x 

Naturalmente si può sempre fornire queste funzioni il modo "sicuro":

safeRecip :: Fractional a => a -> Maybe a 
safeRecip x | x == 0 = Nothing 
      | otherwise = Just $ 1/x 

forse dovremmo anche astratto questo schema

restrictInput :: (a -> Bool) -> (a -> b) -> (a -> Maybe b) 
restrictInput pred f x = if pred x then Just (f x) else Nothing 

safeRecip' = restrictInput (/= 0) myRecip 

Si potrebbe immaginare combinatori simili per l'utilizzo Either invece di Maybe per segnalare un errore. Quindi, dato che abbiamo la capacità di gestire queste cose con funzioni pure, perché preoccuparsi del modello di eccezioni impure? Bene, la maggior parte degli Haskeller ti dirà di restare con la purezza. Ma la verità oscura è che è solo un dolore aggiungere quel livello in più. Non è più possibile scrivere

prop_recip x = x == (recip . recip) x 

Perché ora il risultato di recip x è in una Maybe. Ci sono cose utili che puoi fare con le funzioni (a -> a) che non puoi più fare. Ora devi pensare a comporre Maybes. Questo, ovviamente, è banale se si ha familiarità con le monadi:

prop_recip 0 = (safeRecip >=> safeRecip) 0 == Nothing 
prop_recip x = (safeRecip >=> safeRecip) x == Just x 

Ma qui sta lo sfregamento. I neofiti generalmente sanno quasi nulla quando si tratta di composizione monadica. Il Comitato Haskell, come molti sul canale #haskell irc ti dirà velocemente *, ha preso alcune decisioni piuttosto approssimative riguardanti la progettazione della lingua per soddisfare i principianti. Vogliamo essere in grado di dire "non è necessario conoscere le monadi per iniziare a fare cose utili in Haskell". E generalmente sono d'accordo con questo sentimento.

tl; dr Alcune risposte rapide alla domanda: che cos'è un'eccezione?

  • un hack sporco quindi non c'è bisogno di fare le nostre funzioni del tutto sicuro
  • un "try/catch" meccanismo di controllo di flusso per la monade IO (IO è un bidone peccato, quindi perché non tiro prova/cattura anche la lista dei peccati?)

Ci possono essere altre spiegazioni.

Vedi anche Haskell Report > Basic Input/Output > Exception Handling in the IO Monad

* in realtà ho chiesto su #haskell irc se approvato di questa affermazione. L'unica risposta che ho ottenuto è stata "wonky" :) quindi è ovviamente dimostrato vero per assenza di obiezioni.


[Modifica] Si noti che error e undefined sono definiti in termini di throw:

error :: [Char] -> a 
error s = throw (ErrorCall s) 

undefined :: a 
undefined = error "Prelude.undefined" 
+3

L'utilizzo di altre eccezioni rispetto a "errore" nella funzione reciproca è ciò che considero l'utilizzo errato delle eccezioni. La funzione non dovrebbe mai essere chiamata con quel valore e il programma non dovrebbe mai tentare di rilevare eccezioni da esso, in tal caso nel nostro programma c'è un difetto logico. Preferisco uccidere il programma in questi casi con 'errore', per rilevare il bug. Se il bug viene gestito in silenzio con la gestione delle eccezioni, potrebbe causare altri bug in seguito. – telephone

+3

@telephone usando 'error' non è nulla di magico: puoi prendere l'eccezione' ErrorCall' generata e gestirla in silenzio. 'lascia foo a = errore" rawr "in Control.Exception.Base.catch (foo()) (\ (ErrorCall msg) -> putStrLn msg)' stampa il messaggio "rawr" –

+0

Precisazione: sono d'accordo che gestire le eccezioni in modo silenzioso è piuttosto atroce. Stavo solo dicendo che 'error' è (essenzialmente) nient'altro che una macro per' (throw. ErrorCall) ', quindi non sono d'accordo che l'uso di eccezioni diverse da 'error' sia sbagliato. Puoi uccidere il programma con qualsiasi eccezione, a meno che non lo prendi; 'ErrorCall' non è diverso. La cosa bella di 'ErrorCall' (e la sua funzione di convenienza' error'), tuttavia, è che rende così puoi semplicemente scrivere il tuo messaggio di errore in una stringa, piuttosto che avere il problema di creare il tuo tipo di eccezione. –

1

eccezioni sono una forma legittima di controllo di flusso. Non mi è chiaro il motivo per cui, quando viene fornito uno strumento, i programmatori insistono sul fatto che è "solo per" alcuni casi e escludono altri possibili usi.

Ad esempio, se si esegue un calcolo del backtracking, è possibile utilizzare le eccezioni per tornare indietro. In Haskell probabilmente sarebbe più comune usare la lista monad per questo, ma le eccezioni sono un modo legittimo per andare.

5

La funzione "errore" è quando una funzione riceve un input non valido o quando accade qualcosa di interno che non dovrebbe mai accadere (cioè un bug). In breve, chiamare "error" rappresenta un bug - nel chiamante o nel chiamato.

La costante "non definita" è più per valori che non dovrebbero essere utilizzati, in genere perché verranno sostituiti con qualcos'altro o perché sono valori fantasma utilizzati per ottenere un tipo specifico. (In realtà è implementato come una chiamata a "errore".)

Allora perché abbiamo Control.Exception con tutta la sua fantasia?

Fondamentalmente, "perché le operazioni di I/O possono generare eccezioni". Si potrebbe tranquillamente parlare con un server FTP su un socket TCP e improvvisamente la connessione si interrompe. Il risultato? Il tuo programma lancia un'eccezione. Oppure potresti esaurire la RAM, o il disco potrebbe riempire, o qualsiasi altra cosa.

Si noti che quasi tutte queste cose sono non è colpa vostra. Se puoi anticipare che una cosa specifica sta andando male, dovresti usare cose come Maybe e Either per gestirlo in modo puro. Ad esempio, se stai invertendo una matrice, beh, la matrice potrebbe non essere invertibile, quindi è meglio restituire uno Maybe Matrix come risultato. Per cose che non puoi ragionevolmente prevedere (ad esempio, un altro il programma ha appena cancellato il file su cui stai lavorando, eccetto le eccezioni.

Si noti che Control.Exception contiene un sacco di cose per gestione di eccezioni e solo la definizione di molti tipi diversi. Io non rispondo allo al se il codice che ho chiamato ha fatto qualcosa che non è corretto e quindi un bug; Mi piacerebbe ancora essere in grado di dire al cliente con cui stavo parlando che la connessione sta per essere chiusa, registrare una descrizione in un file di registro da qualche parte, e fare altre cose di pulizia, piuttosto che avere il mio programma all'improvviso, tu sapere, stop.