2013-11-04 13 views
5

Sto cercando di leggere un valore da un file e di rilevare tutte le eccezioni che potrebbero verificarsi (nella mentalità di "Easier to ask for forgiveness than permission"). Sto riscontrando problemi nell'ottenere l'eccezione Prelude.read: no parse. A dire try che dovrebbe cogliere ogni eccezione definisco tryAny con il tipo esplicito SomeException che a mia conoscenza è il "tipo super" di ogni eccezione:Impossibile catturare l'eccezione "Prelude.read: no parse" con Control.Exception.try

import Control.Exception (try,SomeException) 
tryAny :: IO a -> IO (Either SomeException a) 
tryAny = try 

Con tryAny mi sembra di essere in grado di catturare IO errori:

> tryAny (fromFile "nonExistingFileName") 
Left nonExistingFileName: openFile: does not exist (No such file or directory) 

Ma l'errore di lettura non sarà catturato:

> tryAny (return ((read "a")::Int)) 
Right *** Exception: Prelude.read: no parse 

che cosa posso d o per catturare ogni eccezione?

+2

Se si sta tentando di risolvere questo particolare problema, si consiglia di provare il pacchetto * safe *. Il [modulo sicuro] (http://hackage.haskell.org/package/safe-0.3.3/docs/Safe.html) esporta, tra le altre cose, 'readMay' che restituisce il valore letto come valore' Maybe' . – kqr

+0

@kqr Grazie, è un buon suggerimento. Lo userò per il mio prossimo "leggere". – Perseids

risposta

4

return non valuta l'argomento e pertanto non genera eccezioni. La valutazione avviene al di fuori di tryAny, quando si tenta di stampare il risultato.

Utilizzare evaluate (eventualmente insieme a force da Control.DeepSeq, a seconda della situazione reale) per quello.

+2

Piuttosto che "valutare", potrebbe essere più semplice utilizzare una libreria di analisi che modelli l'errore in modo esplicito. Cioè qualsiasi cosa diversa da 'read'. –

+2

C'è 'readMaybe' nelle versioni recenti di' base'. Ma questa domanda sembra riguardare le eccezioni, non l'analisi. –

+0

Vero, è per questo che ho rialzato la tua risposta. Ma 'read' probabilmente non è lo strumento giusto per il lavoro. –

2

Dopo un sacco di sperimentare evaluate, force e try, seguendo i suggerimenti del @Roman Cheplyaka, mi è venuta in mente questa soluzione, che sembra quasi incredibilmente ovvio ora vedo il risultato finale:

import Control.DeepSeq (force,NFData) 
import Control.Exception (try,SomeException) 

tryAnyStrict :: NFData a => IO a -> IO (Either SomeException a) 
tryAnyStrict = try.evaluateIoCompletely 

evaluateIoCompletely :: NFData a => IO a -> IO a 
evaluateIoCompletely ioA = do 
    a <- ioA 
    evaluate $ force a 

Questa soluzione divide il processo in una funzione che imporrà una valutazione rigorosa e genererà tutte le eccezioni e un wrapper che catturerà queste eccezioni. force restituisce un oggetto haskell puro che deve essere valutato per primo per forzare una valutazione approfondita di a. Dal documentation:

force x = x `deepseq` x 

forza x valuta pienamente x, e poi lo restituisce. Si noti che la forza x esegue solo la valutazione quando viene richiesto il valore della forza x, quindi in sostanza trasforma la valutazione superficiale in valutazione profonda.

ci sono alcuni punti sottili di questa implementazione che è andato storto nei miei precedenti tentativi:

  • Se si utilizza ioA direttamente come input per force invece di a allora si finirà con un oggetto di digitare IO (IO a), come evaluate ha tipo b -> IO b. Questo problema immediato può essere risolto con una funzione di unwrap di tipo IO(IO a) -> IO a, ma in questo caso si verificherà un problema con l'assunzione di classe di caratteri di NFData a, che ora deve essere NFData (IO a). Ma per IO a sembra non esserci nessuna dichiarazione di istanza disponibile, almeno per a::(String,String).
  • Invece di utilizzare due funzioni, è possibile utilizzare semplicemente tryAnyStrict = do a <- ioA; (try.evaluate.force) a.Il problema è che le eccezioni derivanti da a <- ioA non verranno rilevate, ad esempio se ioA è il risultato di serialized <- readFile; return $ read serialized e il file da leggere non esiste.
  • Non è possibile inserire nel proprio codice try.evaluateIoCompletely nel proprio codice senza informazioni sul tipo, poiché è necessario provare a conoscere l'eccezione che deve catturare. Questa informazione viene ora fornita tramite il tipo di reso tryAnyStrict che risolve il tipo e di try :: Exception e => IO a -> IO (Either e a) per il tipo di eccezione più generale SomeException.

Ora dopo che ho ottenuto tryAnyStrict lavorare ho ancora problemi con la valutazione pigra, perché se a <- ioA fallisce - dove IOA è il risultato di read su una stringa lettura pigramente da un file - (ad esempio il contenuto del file non ha il formato necessario per read), quindi il file non è ancora completamente letto e quindi ancora aperto. Una successiva scrittura sul file fallirà ("openFile: resource busy"). Quindi ... Potrei effettivamente riscrivere l'intero codice ancora usando readMay o readMaybe come suggerito da @kqr e @Roman Cheplyaka e anche forzare una lettura rigorosa del file direttamente.

1

Ho avuto lo stesso problema e non ho trovato readMaybe come suggerito in alcuni dei commenti. Ma ho trovato un'altra variante di lettura utile che ha risolto il mio problema - readIO

La funzione readio è simile a leggere se non che segnala il fallimento di analisi al IO monade, invece di terminare il programma.

Utilizzo È stato possibile rilevare ed elaborare tutti gli errori nel mio programma.

-1

Se è possibile utilizzare safe-exceptions

È possibile e utilizzare tryAnyDeep

tryDeep :: (C.MonadCatch m, MonadIO m, E.Exception e, NFData a) => m a -> m (Either e a) 
tryDeep f = catch (liftM Right (evaluateDeep f)) (return . Left) 
tryAnyDeep :: (C.MonadCatch m, MonadIO m, NFData a) => m a -> m (Either SomeException a) 
tryAnyDeep = tryDeep 

Ma soluzione aspettarsi una ha deriving (NFData).

+0

Sebbene questo collegamento possa rispondere alla domanda, è meglio includere qui le parti essenziali della risposta e fornire il link per riferimento. Le risposte di solo collegamento possono diventare non valide se la pagina collegata cambia. - [Dalla revisione] (/ recensione/post di bassa qualità/17302818) – EJoshuaS

+0

Aggiungere descrizione. – ncaq